-
Notifications
You must be signed in to change notification settings - Fork 167
Draft: Implementation for an AI order to chase ship types #6620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
fbc4235
6407748
6c708bd
958c657
9be17c3
2317f4e
99b8488
2a8e9e6
f3f1af0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2214,6 +2214,7 @@ typedef struct eval_nearest_objnum { | |
object *trial_objp; | ||
int enemy_team_mask; | ||
int enemy_ship_info_index; | ||
int enemy_class_type; | ||
int enemy_wing; | ||
float range; | ||
int max_attackers; | ||
|
@@ -2247,6 +2248,10 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno) | |
if ((eno->enemy_ship_info_index >= 0) && (shipp->ship_info_index != eno->enemy_ship_info_index)) | ||
return; | ||
|
||
// If only supposed to attack ships of a certain ship type, don't attack other ships. | ||
if ((eno->enemy_class_type >= 0) && (Ship_info[shipp->ship_info_index].class_type != eno->enemy_class_type)) | ||
return; | ||
|
||
// Don't keep firing at a ship that is in its death throes. | ||
if (shipp->flags[Ship::Ship_Flags::Dying]) | ||
return; | ||
|
@@ -2335,8 +2340,9 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno) | |
* @param range Ship must be within range "range". | ||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it. | ||
* @param ship_info_index If >=0, the enemy object must be of the specified ship class | ||
* @param class_type If >=0, the enemy object must be of the specified ship type | ||
*/ | ||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index) | ||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index, int class_type) | ||
{ | ||
object *danger_weapon_objp; | ||
ai_info *aip; | ||
|
@@ -2346,6 +2352,7 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra | |
eval_nearest_objnum eno; | ||
eno.enemy_team_mask = enemy_team_mask; | ||
eno.enemy_ship_info_index = ship_info_index; | ||
eno.enemy_class_type = class_type; | ||
eno.enemy_wing = enemy_wing; | ||
eno.max_attackers = max_attackers; | ||
eno.objnum = objnum; | ||
|
@@ -2391,7 +2398,7 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra | |
// If only looking for target in certain wing and couldn't find anything in | ||
// that wing, look for any object. | ||
if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) { | ||
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, ship_info_index); | ||
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, ship_info_index, class_type); | ||
} | ||
|
||
return eno.nearest_objnum; | ||
|
@@ -2495,33 +2502,38 @@ int get_enemy_timestamp() | |
/** | ||
* Return objnum if enemy found, else return -1; | ||
* | ||
* @param objnum Object number | ||
* @param range Range within which to look | ||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it. | ||
* @param objnum Object number | ||
* @param range Range within which to look | ||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it. | ||
* @param ship_info_index If specified, restrict the search to enemies with this ship class | ||
* @param class_type If specified, restrict the search to enemies with this ship type | ||
*/ | ||
int find_enemy(int objnum, float range, int max_attackers, int ship_info_index) | ||
int find_enemy(int objnum, float range, int max_attackers, int ship_info_index, int class_type) | ||
{ | ||
int enemy_team_mask; | ||
int enemy_team_mask; | ||
|
||
if (objnum < 0) | ||
return -1; | ||
|
||
enemy_team_mask = iff_get_attackee_mask(obj_team(&Objects[objnum])); | ||
|
||
// if target_objnum != -1, use that as goal. | ||
ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index]; | ||
ai_info* aip = &Ai_info[Ships[Objects[objnum].instance].ai_index]; | ||
if (timestamp_elapsed(aip->choose_enemy_timestamp)) { | ||
aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp()); | ||
if (aip->target_objnum != -1) { | ||
int target_objnum = aip->target_objnum; | ||
int target_objnum = aip->target_objnum; | ||
ship* target_shipp = &Ships[Objects[target_objnum].instance]; | ||
|
||
// DKA don't undo object as target in nebula missions. | ||
// This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range. (BAD) | ||
if ( Objects[target_objnum].signature == aip->target_signature ) { | ||
if (iff_matches_mask(Ships[Objects[target_objnum].instance].team, enemy_team_mask)) { | ||
if (ship_info_index < 0 || ship_info_index == Ships[Objects[target_objnum].instance].ship_info_index) { | ||
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) { | ||
return target_objnum; | ||
// This could cause attack on ship on fringe on nebula to stop if attackee moves out of nebula range. (BAD) | ||
if (Objects[target_objnum].signature == aip->target_signature) { | ||
if (iff_matches_mask(target_shipp->team, enemy_team_mask)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change this to if (target_shipp && iff_matches_mask(target_shipp->team, enemy_team_mask)) { because with my above review comment, it will be possible for |
||
if (ship_info_index < 0 || ship_info_index == target_shipp->ship_info_index) { | ||
if (class_type < 0 || (target_shipp->ship_info_index >= 0 && class_type == Ship_info[target_shipp->ship_info_index].class_type)) { | ||
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) { | ||
return target_objnum; | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -2530,16 +2542,16 @@ int find_enemy(int objnum, float range, int max_attackers, int ship_info_index) | |
aip->target_signature = -1; | ||
} | ||
} | ||
|
||
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers, ship_info_index); | ||
|
||
return get_nearest_objnum(objnum, enemy_team_mask,aip->enemy_wing, range, max_attackers, ship_info_index, class_type); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. put back the space between |
||
|
||
} else { | ||
aip->target_objnum = -1; | ||
aip->target_signature = -1; | ||
return -1; | ||
} | ||
} | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This blank line can be removed |
||
/** | ||
* If issued an order to a ship that's awaiting repair, abort that process. | ||
* However, do not abort process for an object that is currently being repaired -- let it finish. | ||
|
@@ -2573,7 +2585,7 @@ void force_avoid_player_check(object *objp, ai_info *aip) | |
* If attacked == NULL, then attack any enemy object. | ||
* Attack point *rel_pos on object. This is for supporting attacking subsystems. | ||
*/ | ||
void ai_attack_object(object* attacker, object* attacked, int ship_info_index) | ||
void ai_attack_object(object* attacker, object* attacked, int ship_info_index, int class_type) | ||
{ | ||
int temp; | ||
ai_info* aip; | ||
|
@@ -2606,7 +2618,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index) | |
if (attacked == nullptr) { | ||
aip->choose_enemy_timestamp = timestamp(0); | ||
// nebula safe | ||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index)); | ||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index, class_type)); | ||
} else { | ||
// check if we can see attacked in nebula | ||
if (aip->target_objnum != OBJ_INDEX(attacked)) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,7 @@ ai_goal_list Ai_goal_names[] = | |
{ "Attack weapon", AI_GOAL_CHASE_WEAPON, 0 }, | ||
{ "Fly to ship", AI_GOAL_FLY_TO_SHIP, 0 }, | ||
{ "Attack ship class", AI_GOAL_CHASE_SHIP_CLASS, 0 }, | ||
{ "Attack ship type", AI_GOAL_CHASE_SHIP_TYPE, 0 }, | ||
}; | ||
|
||
int Num_ai_goals = sizeof(Ai_goal_names) / sizeof(ai_goal_list); | ||
|
@@ -114,6 +115,7 @@ const char *Ai_goal_text(ai_goal_mode goal, int submode) | |
case AI_GOAL_CHASE: | ||
case AI_GOAL_CHASE_WING: | ||
case AI_GOAL_CHASE_SHIP_CLASS: | ||
case AI_GOAL_CHASE_SHIP_TYPE: | ||
return XSTR( "attack ", 474); | ||
case AI_GOAL_DOCK: | ||
return XSTR( "dock ", 475); | ||
|
@@ -484,7 +486,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai | |
int i, j; | ||
ai_goal *purge_goal; | ||
const char *name; | ||
int mode, ship_index, wingnum; | ||
int mode, ship_index, wingnum, ship_type; | ||
|
||
// get locals for easer access | ||
name = aigp->target_name; | ||
|
@@ -513,13 +515,26 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai | |
if ( purge_goal->target_name == NULL ) | ||
continue; | ||
|
||
// goals operating on ship classes are handled slightly differently | ||
// goals operating on ship classes and ship types are handled slightly differently | ||
if ( purge_ai_mode == AI_GOAL_CHASE_SHIP_CLASS ) { | ||
// if the target of the purge goal is the same class of ship we are concerned about, then we have a match; | ||
// if it is not, then we can continue (see standard ship check below) | ||
if ( stricmp(purge_goal->target_name, Ship_info[Ships[ship_index].ship_info_index].name) != 0 ) | ||
continue; | ||
} | ||
else if (purge_ai_mode == AI_GOAL_CHASE_SHIP_TYPE) { | ||
// Get the ship type of the ship we're concerned about | ||
ship_type = Ship_info[Ships[ship_index].ship_info_index].class_type; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of declaring |
||
|
||
// If the ship type is invalid, we can't match it, so continue | ||
if (ship_type < 0) | ||
continue; | ||
|
||
// Check if the target name of the purge goal matches the ship type name | ||
if (stricmp(purge_goal->target_name, Ship_types[ship_type].name) != 0) | ||
continue; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the blank line here |
||
// standard goals operating on either wings or ships | ||
else { | ||
// determine if the purge goal is acting either on the ship or the ship's wing. | ||
|
@@ -1092,6 +1107,7 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a | |
case OP_AI_CHASE: | ||
case OP_AI_CHASE_WING: | ||
case OP_AI_CHASE_SHIP_CLASS: | ||
case OP_AI_CHASE_SHIP_TYPE: | ||
case OP_AI_GUARD: | ||
case OP_AI_GUARD_WING: | ||
case OP_AI_EVADE_SHIP: | ||
|
@@ -1123,6 +1139,8 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a | |
aigp->ai_mode = AI_GOAL_CHASE_WING; | ||
} else if (op == OP_AI_CHASE_SHIP_CLASS) { | ||
aigp->ai_mode = AI_GOAL_CHASE_SHIP_CLASS; | ||
} else if (op == OP_AI_CHASE_SHIP_TYPE) { | ||
aigp->ai_mode = AI_GOAL_CHASE_SHIP_TYPE; | ||
} else if ( op == OP_AI_IGNORE ) { | ||
aigp->ai_mode = AI_GOAL_IGNORE; | ||
} else if ( op == OP_AI_IGNORE_NEW ) { | ||
|
@@ -1167,7 +1185,7 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a | |
} | ||
|
||
// Goober5000 - we now have an extra optional chase argument to allow chasing our own team | ||
if ( op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_CHASE_SHIP_CLASS | ||
if (op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_CHASE_SHIP_CLASS || op == OP_AI_CHASE_SHIP_TYPE | ||
|| op == OP_AI_DISABLE_SHIP || op == OP_AI_DISABLE_SHIP_TACTICAL || op == OP_AI_DISARM_SHIP || op == OP_AI_DISARM_SHIP_TACTICAL ) { | ||
if (is_sexp_true(CDDDR(node))) | ||
aigp->flags.set(AI::Goal_Flags::Target_own_team); | ||
|
@@ -1192,6 +1210,7 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a | |
if (op == OP_AI_CHASE || | ||
op == OP_AI_CHASE_WING || | ||
op == OP_AI_CHASE_SHIP_CLASS || | ||
op == OP_AI_CHASE_SHIP_TYPE || | ||
op == OP_AI_DISABLE_SHIP || | ||
op == OP_AI_DISABLE_SHIP_TACTICAL || | ||
op == OP_AI_DISARM_SHIP || | ||
|
@@ -1378,6 +1397,10 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more ) | |
priority = eval_priority_et_seq(CDDR(node)); | ||
goalmode = AI_GOAL_CHASE_SHIP_CLASS; | ||
break; | ||
case OP_AI_CHASE_SHIP_TYPE: | ||
priority = eval_priority_et_seq(CDDR(node)); | ||
goalmode = AI_GOAL_CHASE_SHIP_TYPE; | ||
break; | ||
case OP_AI_EVADE_SHIP: | ||
priority = eval_priority_et_seq(CDDR(node)); | ||
goalmode = AI_GOAL_EVADE_SHIP; | ||
|
@@ -1688,6 +1711,19 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) | |
return ai_achievability::NOT_KNOWN; | ||
} | ||
|
||
if (aigp->ai_mode == AI_GOAL_CHASE_SHIP_TYPE) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment above this line: |
||
for (auto so : list_range(&Ship_obj_list)) { | ||
auto type_objp = &Objects[so->objnum]; | ||
if (type_objp->flags[Object::Object_Flags::Should_be_dead]) | ||
continue; | ||
int class_type = Ship_info[Ships[type_objp->instance].ship_info_index].class_type; | ||
if ((type_objp->type == OBJ_SHIP) && class_type >= 0 && | ||
Comment on lines
+1719
to
+1720
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not possible to get the |
||
!strcmp(aigp->target_name, Ship_types[class_type].name)) { | ||
return ai_achievability::ACHIEVABLE; | ||
} | ||
} | ||
return ai_achievability::NOT_KNOWN; | ||
} | ||
|
||
return_val = ai_achievability::SATISFIED; | ||
|
||
|
@@ -2521,10 +2557,20 @@ void ai_process_mission_orders( int objnum, ai_info *aip ) | |
|
||
// chase-ship-class is chase-any but restricted to a subset of ships | ||
case AI_GOAL_CHASE_SHIP_CLASS: | ||
shipnum = ship_info_lookup( current_goal->target_name ); | ||
Assertion( shipnum >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!" ); | ||
ai_attack_object( objp, nullptr, shipnum ); | ||
{ | ||
int ship_info_index = ship_info_lookup(current_goal->target_name); | ||
Assertion(ship_info_index >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!"); | ||
ai_attack_object(objp, nullptr, ship_info_index); | ||
break; | ||
} | ||
|
||
case AI_GOAL_CHASE_SHIP_TYPE: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment above this line: |
||
{ | ||
int class_type = ship_type_name_lookup(current_goal->target_name); | ||
Assertion(class_type >= 0, "The target of AI_GOAL_CHASE_SHIP_TYPE must refer to a valid ship type!"); | ||
ai_attack_object(objp, nullptr, -1, class_type); | ||
break; | ||
} | ||
|
||
case AI_GOAL_WARP: { | ||
mission_do_departure( objp, true ); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -847,6 +847,7 @@ SCP_vector<sexp_oper> Operators = { | |
{ "ai-chase", OP_AI_CHASE, 2, 4, SEXP_GOAL_OPERATOR, }, | ||
{ "ai-chase-wing", OP_AI_CHASE_WING, 2, 4, SEXP_GOAL_OPERATOR, }, | ||
{ "ai-chase-ship-class", OP_AI_CHASE_SHIP_CLASS, 2, 4, SEXP_GOAL_OPERATOR, }, | ||
{ "ai-chase-ship-type", OP_AI_CHASE_SHIP_TYPE, 2, 4, SEXP_GOAL_OPERATOR, }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can put your username in a comment at the end of this line, like other sexp authors do |
||
{ "ai-chase-any", OP_AI_CHASE_ANY, 1, 2, SEXP_GOAL_OPERATOR, }, | ||
{ "ai-guard", OP_AI_GUARD, 2, 3, SEXP_GOAL_OPERATOR, }, | ||
{ "ai-guard-wing", OP_AI_GUARD_WING, 2, 3, SEXP_GOAL_OPERATOR, }, | ||
|
@@ -907,6 +908,7 @@ sexp_ai_goal_link Sexp_ai_goal_links[] = { | |
{ AI_GOAL_CHASE, OP_AI_CHASE }, | ||
{ AI_GOAL_CHASE_WING, OP_AI_CHASE_WING }, | ||
{ AI_GOAL_CHASE_SHIP_CLASS, OP_AI_CHASE_SHIP_CLASS }, | ||
{ AI_GOAL_CHASE_SHIP_TYPE, OP_AI_CHASE_SHIP_TYPE}, | ||
{ AI_GOAL_CHASE_ANY, OP_AI_CHASE_ANY }, | ||
{ AI_GOAL_DOCK, OP_AI_DOCK }, | ||
{ AI_GOAL_UNDOCK, OP_AI_UNDOCK }, | ||
|
@@ -31418,6 +31420,7 @@ int query_operator_return_type(int op) | |
case OP_AI_CHASE: | ||
case OP_AI_CHASE_WING: | ||
case OP_AI_CHASE_SHIP_CLASS: | ||
case OP_AI_CHASE_SHIP_TYPE: | ||
case OP_AI_CHASE_ANY: | ||
case OP_AI_DOCK: | ||
case OP_AI_UNDOCK: | ||
|
@@ -32428,6 +32431,14 @@ int query_operator_argument_type(int op, int argnum) | |
else | ||
return OPF_BOOL; | ||
|
||
case OP_AI_CHASE_SHIP_TYPE: | ||
if (argnum == 0) | ||
return OPF_SHIP_TYPE; | ||
else if (argnum == 1) | ||
return OPF_POSITIVE; | ||
else | ||
return OPF_BOOL; | ||
|
||
case OP_AI_GUARD: | ||
if (argnum == 0) | ||
return OPF_SHIP_WING; | ||
|
@@ -36590,6 +36601,7 @@ int get_category(int op_id) | |
case OP_AI_IGNORE_NEW: | ||
case OP_AI_FORM_ON_WING: | ||
case OP_AI_CHASE_SHIP_CLASS: | ||
case OP_AI_CHASE_SHIP_TYPE: | ||
case OP_AI_PLAY_DEAD_PERSISTENT: | ||
case OP_AI_FLY_TO_SHIP: | ||
case OP_AI_REARM_REPAIR: | ||
|
@@ -39069,6 +39081,15 @@ SCP_vector<sexp_help_struct> Sexp_help = { | |
"\t4 (optional):\tWhether to afterburn as hard as possible to the target; defaults to false." | ||
}, | ||
|
||
{ OP_AI_CHASE_SHIP_TYPE, "Ai-chase ship type (Ship goal)\r\n" | ||
"\tCauses the specified ship to chase and attack a target ship type.\r\n\r\n" | ||
"Takes 2 to 4 arguments...\r\n" | ||
"\t1:\tName of ship type to chase.\r\n" | ||
"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100).\r\n" | ||
"\t3 (optional):\tWhether to attack the target even if it is on the same team; defaults to false.\r\n" | ||
"\t4 (optional):\tWhether to afterburn as hard as possible to the target; defaults to false." | ||
}, | ||
|
||
{ OP_AI_CHASE_ANY, "Ai-chase-any (Ship goal)\r\n" | ||
"\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n" | ||
"Takes 1 or 2 arguments...\r\n" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this to:
ship* target_shipp = (Objects[target_objnum].type == OBJ_SHIP) ? &Ships[Objects[target_objnum].instance] : nullptr;
Because not every target is necessarily a ship. This was a bug in Volition's code too :-/