diff --git a/code/ai/ai.h b/code/ai/ai.h index df63bfb5a1e..e05cdc66ff1 100644 --- a/code/ai/ai.h +++ b/code/ai/ai.h @@ -96,6 +96,7 @@ extern const int Num_ai_flag_names; #define MAX_ENEMY_DISTANCE 2500.0f // Maximum distance from which a ship will pursue an enemy. #define MAX_AI_GOALS 5 +#define AI_ACTIVE_GOAL_NONE -1 #define AI_ACTIVE_GOAL_DYNAMIC 999 typedef struct ai_class { diff --git a/code/ai/ai_flags.h b/code/ai/ai_flags.h index d1ce7563376..f9d88043cf9 100644 --- a/code/ai/ai_flags.h +++ b/code/ai/ai_flags.h @@ -43,7 +43,7 @@ namespace AI { Dockee_index_valid, // when set, index field for dockee is valid Goal_on_hold, // when set, this goal cannot currently be satisfied, although it could be in the future Subsys_needs_fixup, // when set, the subsystem index (for a destroy subsystem goal) is invalid and must be gotten from the subsys name stored in docker.name field!! - Goal_override, // paired with AIG_TYPE_DYNAMIC to mean this goal overrides any other goal + Goal_override, // paired with ai_goal_type::DYNAMIC to mean this goal overrides any other goal Purge, // purge this goal next time we process Goals_purged, // this goal has already caused other goals to get purged Depart_sound_played,// Goober5000 - replacement for AL's hack ;) diff --git a/code/ai/aibig.cpp b/code/ai/aibig.cpp index 94bc9b033e4..e7ca403876c 100644 --- a/code/ai/aibig.cpp +++ b/code/ai/aibig.cpp @@ -913,13 +913,13 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy) } if (tswp->num_secondary_banks > 0) { - if (!(En_objp->flags[Object::Object_Flags::Protected]) || (aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL))) { + if ( !(En_objp->flags[Object::Object_Flags::Protected]) || ai_goal_is_disable_or_disarm(aip->goals[0].ai_mode) ) { bool valid_secondary = ai_choose_secondary_weapon(Pl_objp, aip, En_objp); int current_bank = tswp->current_secondary_bank; if (current_bank > -1 && valid_secondary) { weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[current_bank]]; - if(!(En_objp->flags[Object::Object_Flags::Protected]) || ((aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL)) && swip->wi_flags[Weapon::Info_Flags::Puncture] )) { //override lockdown on protected ships when using anti subsystem weapons - Valathil + if ( !(En_objp->flags[Object::Object_Flags::Protected]) || (ai_goal_is_disable_or_disarm(aip->goals[0].ai_mode) && swip->wi_flags[Weapon::Info_Flags::Puncture]) ) { //override lockdown on protected ships when using anti subsystem weapons - Valathil // If ship is protected and very low on hits, don't fire missiles. if (!(En_objp->flags[Object::Object_Flags::Protected]) || (En_objp->hull_strength > 10*swip->damage)) { if (aip->ai_flags[AI::AI_Flags::Unload_secondaries]) { diff --git a/code/ai/aicode.cpp b/code/ai/aicode.cpp index 1b7418338a2..d726f9c10c3 100644 --- a/code/ai/aicode.cpp +++ b/code/ai/aicode.cpp @@ -4605,7 +4605,7 @@ void ai_fly_to_target_position(const vec3d* target_pos, bool* pl_done_p=NULL, bo /* I shouldn't be flying to position for what ever called me any more. Set mode to none so that default dynamic behaviour gets started up again. */ - if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) { + if ( (aip->active_goal == AI_ACTIVE_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) { aip->mode = AIM_NONE; } @@ -4871,12 +4871,10 @@ void ai_fly_to_target_position(const vec3d* target_pos, bool* pl_done_p=NULL, bo // for itself and in a wing, treat the completion as we would a ship treat_as_ship = 1; if ( shipp->wingnum != -1 ) { - int type; - // protect array access from invalid indexes if ((aip->active_goal >= 0) && (aip->active_goal < MAX_AI_GOALS)) { - type = aip->goals[aip->active_goal].type; - if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) { + auto type = aip->goals[aip->active_goal].type; + if ( (type == ai_goal_type::EVENT_WING) || (type == ai_goal_type::PLAYER_WING) ) { treat_as_ship = 0; } else { treat_as_ship = 1; @@ -5058,7 +5056,7 @@ int maybe_resume_previous_mode(object *objp, ai_info *aip) aip->mode = aip->previous_mode; aip->submode = aip->previous_submode; aip->submode_start_time = Missiontime; - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; aip->mode_time = -1; // Means do forever. return 1; } @@ -5079,7 +5077,7 @@ int maybe_resume_previous_mode(object *objp, ai_info *aip) aip->mode = aip->previous_mode; aip->submode = AIS_GUARD_PATROL; aip->submode_start_time = Missiontime; - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; return 1; } } @@ -5739,7 +5737,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < ship_info_size()); //made it so it only selects puncture weapons if the active goal is to disable something -Bobboau - if ((flags == Weapon::Info_Flags::Puncture) && (Ai_info[shipp->ai_index].goals[0].ai_mode & (AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL))) + if ((flags == Weapon::Info_Flags::Puncture) && ai_goal_is_disable_or_disarm(Ai_info[shipp->ai_index].goals[0].ai_mode)) { if (swp->current_primary_bank >= 0) { @@ -5966,9 +5964,9 @@ void set_primary_weapon_linkage(object *objp) // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are // puncture weapons - if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) + if ( (aip->active_goal != AI_ACTIVE_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) { - if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISABLE_SHIP_TACTICAL|AI_GOAL_DISARM_SHIP|AI_GOAL_DISARM_SHIP_TACTICAL) ) + if ( ai_goal_is_disable_or_disarm(aip->goals[aip->active_goal].ai_mode) ) { // only continue if both primaries are puncture weapons if ( swp->num_primary_banks == 2 ) { @@ -14974,7 +14972,7 @@ int ai_need_new_target(object *pl_objp, int target_objnum) // Goober5000 - targeting the same team is allowed if pl_objp is going bonkers ai_info *pl_aip = &Ai_info[Ships[pl_objp->instance].ai_index]; - if (pl_aip->active_goal != AI_GOAL_NONE && pl_aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) { + if (pl_aip->active_goal != AI_ACTIVE_GOAL_NONE && pl_aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) { if (pl_aip->goals[pl_aip->active_goal].flags[AI::Goal_Flags::Target_own_team]) { return 0; } @@ -15176,7 +15174,7 @@ void ai_frame(int objnum) if ( ai_need_new_target(Pl_objp, target_objnum) ) { if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) { aip->resume_goal_time = -1; - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; } else if (aip->resume_goal_time == -1) { // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum if ( Ship_info[shipp->ship_info_index].class_type > -1 && (Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_auto_attacks]) ) { @@ -15241,7 +15239,7 @@ void ai_frame(int objnum) // If there is a goal to resume and enough time has elapsed, resume the goal. if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) { - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; aip->resume_goal_time = -1; target_objnum = find_enemy(objnum, 2000.0f, The_mission.ai_profile->max_attackers[Game_skill_level]); if (target_objnum != -1) { @@ -15733,7 +15731,7 @@ void ai_do_default_behavior(object *obj) // default behavior in most cases (especially if we're docked) is to just stay put aip->mode = AIM_NONE; aip->submode_start_time = Missiontime; - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; // if we're not docked, we may modify the behavior a bit if (!object_is_docked(obj)) @@ -16443,7 +16441,7 @@ void ai_ship_destroy(int shipnum) set_target_objnum(other_aip, -1); // If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal. if (other_aip->resume_goal_time != -1) - other_aip->active_goal = AI_GOAL_NONE; + other_aip->active_goal = AI_ACTIVE_GOAL_NONE; } if (other_aip->goal_objnum == dead_shipp->objnum) { @@ -16610,7 +16608,7 @@ void ai_add_rearm_goal( object *requester_objp, object *support_objp ) // ensures that the player get a higher priority! requester_aip->ai_flags.set(AI::AI_Flags::Awaiting_repair); // Tell that I'm awaiting repair. if ( requester_objp->flags[Object::Object_Flags::Player_ship] ) - ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip ); + ai_add_ship_goal_player( ai_goal_type::PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip ); else ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 ); diff --git a/code/ai/aigoals.cpp b/code/ai/aigoals.cpp index e419ce9242f..d7b914152e9 100644 --- a/code/ai/aigoals.cpp +++ b/code/ai/aigoals.cpp @@ -53,8 +53,14 @@ struct ship_registry_entry; // PURGE_GOALS_ONE_SHIP for goals which should only purge other goals in the one ship. // Goober5000 - note that the new disable and disarm goals (AI_GOAL_DISABLE_SHIP_TACTICAL and // AI_GOAL_DISARM_SHIP_TACTICAL) do not purge ANY goals, not even the ones in the one ship -#define PURGE_GOALS_ALL_SHIPS (AI_GOAL_IGNORE | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP) -#define PURGE_GOALS_ONE_SHIP (AI_GOAL_IGNORE_NEW) +inline bool purge_goals_all_ships(ai_goal_mode ai_mode) +{ + return ai_mode == AI_GOAL_IGNORE || ai_mode == AI_GOAL_DISABLE_SHIP || ai_mode == AI_GOAL_DISARM_SHIP; +} +inline bool purge_goals_one_ship(ai_goal_mode ai_mode) +{ + return ai_mode == AI_GOAL_IGNORE_NEW; +} // goals given from the player to other ships in the game are also handled in this // code @@ -86,7 +92,7 @@ ai_goal_list Ai_goal_names[] = { "Guard wing", AI_GOAL_GUARD_WING, 0 }, { "Evade ship", AI_GOAL_EVADE_SHIP, 0 }, { "Stay near ship", AI_GOAL_STAY_NEAR_SHIP, 0 }, - { "keep safe dist", AI_GOAL_KEEP_SAFE_DISTANCE, 0 }, + { "Keep safe dist", AI_GOAL_KEEP_SAFE_DISTANCE, 0 }, { "Rearm ship", AI_GOAL_REARM_REPAIR, 0 }, { "Stay still", AI_GOAL_STAY_STILL, 0 }, { "Play dead", AI_GOAL_PLAY_DEAD, 0 }, @@ -102,7 +108,7 @@ int Num_ai_goals = sizeof(Ai_goal_names) / sizeof(ai_goal_list); // HUD what a ship's current orders are. If the AI goal doesn't correspond to something that // ought to be printable, then NULL is used. // JAS: Converted to a function in order to externalize the strings -const char *Ai_goal_text(int goal, int submode) +const char *Ai_goal_text(ai_goal_mode goal, int submode) { switch(goal) { case AI_GOAL_CHASE: @@ -135,22 +141,22 @@ const char *Ai_goal_text(int goal, int submode) return XSTR( "rearm ", 484); case AI_GOAL_FLY_TO_SHIP: return XSTR( "rendezvous with ", 1597); - case AI_GOAL_LUA: + case AI_GOAL_LUA: { auto mode = ai_lua_find_mode(submode); if (mode == nullptr) return nullptr; return mode->hudText; } - - // Avoid compiler warning - return nullptr; + default: + return nullptr; + } } /** * Reset all fields to their uninitialized defaults. But if this is being called before adding a new goal, the function will set the correct signature and time. * Similarly the added mode, submode, and type can be assigned. */ -void ai_goal_reset(ai_goal *aigp, bool adding_goal, int ai_mode, int ai_submode, int type) +void ai_goal_reset(ai_goal *aigp, bool adding_goal, ai_goal_mode ai_mode, int ai_submode, ai_goal_type type) { if (ai_mode != AI_GOAL_NONE) Assertion(adding_goal, "If a goal mode is being assigned, the adding_goal parameter must be true so that the signature and mission time can be set"); @@ -215,13 +221,13 @@ void ai_maybe_add_form_goal(wing* wingp) if (Netgame.type_flags & NG_TYPE_TEAM) { const ship_registry_entry* ship_regp = ship_registry_get(Ships[wingp->ship_index[j]].ship_name); wingnum = TVT_wings[ship_regp->p_objp()->team]; - ai_add_ship_goal_player(AIG_TYPE_PLAYER_SHIP, AI_GOAL_FORM_ON_WING, -1, Ships[Wings[wingnum].ship_index[Wings[wingnum].special_ship]].ship_name, aip); + ai_add_ship_goal_player(ai_goal_type::PLAYER_SHIP, AI_GOAL_FORM_ON_WING, -1, Ships[Wings[wingnum].ship_index[Wings[wingnum].special_ship]].ship_name, aip); } else { wingnum = Starting_wings[0]; - ai_add_ship_goal_player(AIG_TYPE_PLAYER_SHIP, AI_GOAL_FORM_ON_WING, -1, Ships[Wings[wingnum].ship_index[Wings[wingnum].special_ship]].ship_name, aip); + ai_add_ship_goal_player(ai_goal_type::PLAYER_SHIP, AI_GOAL_FORM_ON_WING, -1, Ships[Wings[wingnum].ship_index[Wings[wingnum].special_ship]].ship_name, aip); } } else if (!(Game_mode & GM_MULTIPLAYER)) { - ai_add_ship_goal_player(AIG_TYPE_PLAYER_SHIP, AI_GOAL_FORM_ON_WING, -1, Player_ship->ship_name, aip); + ai_add_ship_goal_player(ai_goal_type::PLAYER_SHIP, AI_GOAL_FORM_ON_WING, -1, Player_ship->ship_name, aip); } } } @@ -280,13 +286,13 @@ void ai_post_process_mission() * Determines if a goal is valid for a particular type of ship * * @param ship Ship type to test - * @param ai_goal_type Goal type to test + * @param ai_mode Goal type to test */ -int ai_query_goal_valid( int ship, int ai_goal_type ) +int ai_query_goal_valid( int ship, ai_goal_mode ai_mode ) { int accepted; - if (ai_goal_type == AI_GOAL_NONE || ai_goal_type == AI_GOAL_LUA) + if (ai_mode == AI_GOAL_NONE || ai_mode == AI_GOAL_LUA) return 1; // anything can have no orders or Lua'd orders. accepted = 0; @@ -296,7 +302,8 @@ int ai_query_goal_valid( int ship, int ai_goal_type ) int ship_type = Ship_info[Ships[ship].ship_info_index].class_type; if(ship_type > -1) { - if(ai_goal_type & Ship_types[ship_type].ai_valid_goals) { + const auto &set = Ship_types[ship_type].ai_valid_goals; + if (set.find(ai_mode) != set.end()) { accepted = 1; } } @@ -341,7 +348,7 @@ void ai_remove_ship_goal( ai_info *aip, int index ) } } - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; } ai_goal_reset(&aip->goals[index]); @@ -358,7 +365,7 @@ void ai_clear_ship_goals( ai_info *aip ) for (i = 0; i < MAX_AI_GOALS; i++) ai_remove_ship_goal( aip, i ); // resets active_goal and default behavior - aip->active_goal = AI_GOAL_NONE; // for good measure + aip->active_goal = AI_ACTIVE_GOAL_NONE; // for good measure // next line moved here on 8/5/97 by MWA // Don't reset player ai (and hence target) @@ -458,9 +465,8 @@ void ai_mission_wing_goal_complete( int wingnum, ai_goal *remove_goalp ) void ai_mission_goal_complete( ai_info *aip ) { - // if the active goal is dynamic or none, just return. (AI_GOAL_NONE is probably an error, but - // I don't think that this is a problem) - if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) + // if the active goal is dynamic or none, just return. + if ( (aip->active_goal == AI_ACTIVE_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) return; ai_remove_ship_goal( aip, aip->active_goal ); @@ -497,9 +503,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai purge_goal = goal_list; for ( i = 0; i < MAX_AI_GOALS; purge_goal++, i++ ) { - int purge_ai_mode, purge_wing; - - purge_ai_mode = purge_goal->ai_mode; + auto purge_ai_mode = purge_goal->ai_mode; // don't need to process AI_GOAL_NONE if ( purge_ai_mode == AI_GOAL_NONE ) @@ -519,7 +523,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai // 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. - purge_wing = wing_name_lookup( purge_goal->target_name, 1 ); + int purge_wing = wing_name_lookup( purge_goal->target_name, 1 ); // if the target of the purge goal is a ship (purge_wing will be -1), then if the names // don't match, we can continue; if the wing is valid, don't process if the wing numbers @@ -536,7 +540,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai // ignore goals should get rid of any kind of attack goal case AI_GOAL_IGNORE: case AI_GOAL_IGNORE_NEW: - if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS | AI_GOAL_DESTROY_SUBSYSTEM) ) + if (ai_goal_is_disable_or_disarm(purge_ai_mode) || ai_goal_is_specific_chase(purge_ai_mode) || (purge_ai_mode == AI_GOAL_DESTROY_SUBSYSTEM)) purge_goal->flags.set(AI::Goal_Flags::Purge); break; @@ -544,7 +548,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai // (but not tactical disarm/disable) case AI_GOAL_DISARM_SHIP: case AI_GOAL_DISABLE_SHIP: - if ( purge_ai_mode & (AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS) ) { + if (ai_goal_is_specific_chase(purge_ai_mode)) { int ai_ship_type; // for wings we grab the ship type of the wing leader @@ -585,7 +589,7 @@ void ai_goal_purge_all_invalid_goals(ai_goal *aigp) ship_obj *sop; // only purge goals if a new goal is one of the types in next statement - if (!(aigp->ai_mode & PURGE_GOALS_ALL_SHIPS)) + if (!purge_goals_all_ships(aigp->ai_mode)) return; for (sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop)) @@ -728,9 +732,9 @@ void ai_goal_fixup_dockpoints(ai_info *aip, ai_goal *aigp) // from the mission goals (i.e. those goals which come from events) in that we don't // use sexpressions for goals from the player...so we enumerate all the parameters -void ai_add_goal_sub_player(int type, int mode, int submode, const char *target_name, ai_goal *aigp, const ai_lua_parameters& lua_target ) +void ai_add_goal_sub_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *target_name, ai_goal *aigp, const ai_lua_parameters& lua_target ) { - Assert ( (type == AIG_TYPE_PLAYER_WING) || (type == AIG_TYPE_PLAYER_SHIP) ); + Assert ( (type == ai_goal_type::PLAYER_WING) || (type == ai_goal_type::PLAYER_SHIP) ); ai_goal_reset(aigp, true, mode, submode, type); @@ -766,7 +770,7 @@ void ai_add_goal_sub_player(int type, int mode, int submode, const char *target_ else if ( mode == AI_GOAL_FORM_ON_WING ) aigp->priority = PLAYER_PRIORITY_SUPPORT_LOW; - else if ( aigp->type == AIG_TYPE_PLAYER_WING ) + else if ( aigp->type == ai_goal_type::PLAYER_WING ) // NOLINT(readability-braces-around-statements) aigp->priority = PLAYER_PRIORITY_WING; // player wing goals not as high as ship goals else aigp->priority = PLAYER_PRIORITY_SHIP; @@ -818,9 +822,9 @@ int ai_goal_num(ai_goal *goals) } -void ai_add_goal_sub_scripting(int type, int mode, int submode, int priority, const char *target_name, ai_goal *aigp ) +void ai_add_goal_sub_scripting(ai_goal_type type, ai_goal_mode mode, int submode, int priority, const char *target_name, ai_goal *aigp ) { - Assert ( (type == AIG_TYPE_PLAYER_WING) || (type == AIG_TYPE_PLAYER_SHIP) ); + Assert ( (type == ai_goal_type::PLAYER_WING) || (type == ai_goal_type::PLAYER_SHIP) ); ai_goal_reset(aigp, true, mode, submode, type); @@ -835,14 +839,14 @@ void ai_add_goal_sub_scripting(int type, int mode, int submode, int priority, co aigp->priority = priority; } -void ai_add_ship_goal_scripting(int mode, int submode, int priority, const char *shipname, ai_info *aip) +void ai_add_ship_goal_scripting(ai_goal_mode mode, int submode, int priority, const char *shipname, ai_info *aip) { int empty_index; ai_goal *aigp; empty_index = ai_goal_find_empty_slot(aip->goals, aip->active_goal); aigp = &aip->goals[empty_index]; - ai_add_goal_sub_scripting(AIG_TYPE_PLAYER_SHIP, mode, submode, priority, shipname, aigp); + ai_add_goal_sub_scripting(ai_goal_type::PLAYER_SHIP, mode, submode, priority, shipname, aigp); //WMC - hack to get docking setup correctly if ( mode == AI_GOAL_DOCK ) { @@ -859,7 +863,7 @@ void ai_add_ship_goal_scripting(int mode, int submode, int priority, const char // is issued to ship or wing (from player), mode is AI_GOAL_*. submode is the submode the // ship should go into. shipname is the object of the action. aip is the ai_info pointer // of the ship receiving the order -void ai_add_ship_goal_player( int type, int mode, int submode, const char *shipname, ai_info *aip, const ai_lua_parameters& lua_target) +void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *shipname, ai_info *aip, const ai_lua_parameters& lua_target) { int empty_index; ai_goal *aigp; @@ -880,7 +884,7 @@ void ai_add_ship_goal_player( int type, int mode, int submode, const char *shipn // adds a goal from the player to the given wing (which in turn will add it to the proper // ships in the wing -void ai_add_wing_goal_player( int type, int mode, int submode, const char *shipname, int wingnum, const ai_lua_parameters& lua_target) +void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char *shipname, int wingnum, const ai_lua_parameters& lua_target) { int i, empty_index; wing *wingp = &Wings[wingnum]; @@ -904,7 +908,7 @@ void ai_add_wing_goal_player( int type, int mode, int submode, const char *shipn // common routine to add a sexpression mission goal to the appropriate goal structure. -void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, const char *actor_name ) +void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *aigp, const char *actor_name ) { int node, dummy, op; bool priority_is_nan = false, priority_is_nan_forever = false; @@ -1232,7 +1236,7 @@ int ai_find_goal_index( ai_goal* aigp, int mode, int submode, int priority ) /* Remove a goal from the given goals structure * Returns the index of the goal that it clears out. - * This is important so that if active_goal == index you can set AI_GOAL_NONE. + * This is important so that if active_goal == index you can set AI_ACTIVE_GOAL_NONE. * NOTE: Callers should check the value of remove_more. If it is true, the function should be called again. */ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more ) @@ -1452,7 +1456,7 @@ void ai_remove_wing_goal_sexp(int sexp, wing *wingp) do { goalindex = ai_remove_goal_sexp_sub(sexp, aip->goals, remove_more); if (aip->active_goal == goalindex) - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; } while (remove_more); } } @@ -1469,7 +1473,7 @@ void ai_remove_wing_goal_sexp(int sexp, wing *wingp) // adds an ai goal for an individual ship // type determines who has issues this ship a goal (i.e. the player/mission event/etc) -void ai_add_ship_goal_sexp( int sexp, int type, ai_info *aip ) +void ai_add_ship_goal_sexp( int sexp, ai_goal_type type, ai_info *aip ) { int gindex; @@ -1478,7 +1482,7 @@ void ai_add_ship_goal_sexp( int sexp, int type, ai_info *aip ) } // code to add ai goals to wings. -void ai_add_wing_goal_sexp(int sexp, int type, wing *wingp) +void ai_add_wing_goal_sexp(int sexp, ai_goal_type type, wing *wingp) { int i; @@ -1524,7 +1528,7 @@ void ai_add_goal_ship_internal( ai_info *aip, int goal_type, char *name, int /* aigp = &(aip->goals[gindex]); ai_goal_reset(aigp, true); - aigp->type = AIG_TYPE_DYNAMIC; + aigp->type = ai_goal_type::DYNAMIC; aigp->flags.set(AI::Goal_Flags::Goal_override); switch ( goal_type ) { @@ -1911,11 +1915,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // Goober5000 - see note at PURGE_GOALS_ALL_SHIPS... this is bizarre if ((status == SHIP_STATUS_ARRIVED) && !(aigp->flags[AI::Goal_Flags::Goals_purged])) { - if (aigp->ai_mode & PURGE_GOALS_ALL_SHIPS) { + if (purge_goals_all_ships(aigp->ai_mode)) { ai_goal_purge_all_invalid_goals(aigp); aigp->flags.set(AI::Goal_Flags::Goals_purged); } - else if (aigp->ai_mode & PURGE_GOALS_ONE_SHIP) { + else if (purge_goals_one_ship(aigp->ai_mode)) { ai_goal_purge_invalid_goals(aigp, aip->goals, aip, -1); aigp->flags.set(AI::Goal_Flags::Goals_purged); } diff --git a/code/ai/aigoals.h b/code/ai/aigoals.h index b0cfb616792..68547349528 100644 --- a/code/ai/aigoals.h +++ b/code/ai/aigoals.h @@ -24,81 +24,87 @@ struct ai_info; // macros for goals which get set via sexpressions in the mission code -// types of ai goals -- tyese types will help us to determination on which goals should +// types of ai goals -- these will help us to determine which goals should // have priority over others (i.e. when a player issues a goal to a wing, then a seperate // goal to a ship in that wing). We would probably use this type in conjunction with // goal priority to establish which goal to follow -#define AIG_TYPE_EVENT_SHIP 1 // from mission event direct to ship -#define AIG_TYPE_EVENT_WING 2 // from mission event direct to wing -#define AIG_TYPE_PLAYER_SHIP 3 // from player direct to ship -#define AIG_TYPE_PLAYER_WING 4 // from player direct to wing -#define AIG_TYPE_DYNAMIC 5 // created on the fly - -#define AI_GOAL_NONE -1 +enum class ai_goal_type +{ + INVALID = 0, + EVENT_SHIP, // from mission event direct to ship + EVENT_WING, // from mission event direct to wing + PLAYER_SHIP, // from player direct to ship + PLAYER_WING, // from player direct to wing + DYNAMIC // created on the fly +}; -// IMPORTANT! If you add a new AI_GOAL_x define, be sure to update the functions +// IMPORTANT! If you add a new AI_GOAL_x enum, be sure to update the functions // ai_update_goal_references() and query_referenced_in_ai_goals() or else risk breaking // Fred. If the goal you add doesn't have a target (such as chase_any), then you don't have // to worry about doing this. Also add it to list in Fred\Management.cpp, and let Hoffoss know! // WMC - Oh and add them to Ai_goal_names plz. TY! :) // Goober5000 - As well as Ai_goal_text and Ai_goal_list, if appropriate -#define AI_GOAL_CHASE (1<<1) // 0x00000002 -#define AI_GOAL_DOCK (1<<2) // 0x00000004 // used for undocking as well -#define AI_GOAL_WAYPOINTS (1<<3) // 0x00000008 -#define AI_GOAL_WAYPOINTS_ONCE (1<<4) // 0x00000010 -#define AI_GOAL_WARP (1<<5) // 0x00000020 -#define AI_GOAL_DESTROY_SUBSYSTEM (1<<6) // 0x00000040 -#define AI_GOAL_FORM_ON_WING (1<<7) // 0x00000080 -#define AI_GOAL_UNDOCK (1<<8) // 0x00000100 -#define AI_GOAL_CHASE_WING (1<<9) // 0x00000200 -#define AI_GOAL_GUARD (1<<10) // 0x00000400 -#define AI_GOAL_DISABLE_SHIP (1<<11) // 0x00000800 -#define AI_GOAL_DISARM_SHIP (1<<12) // 0x00001000 -#define AI_GOAL_CHASE_ANY (1<<13) // 0x00002000 -#define AI_GOAL_IGNORE (1<<14) // 0x00004000 -#define AI_GOAL_GUARD_WING (1<<15) // 0x00008000 -#define AI_GOAL_EVADE_SHIP (1<<16) // 0x00010000 - -// the next goals are for support ships only -#define AI_GOAL_STAY_NEAR_SHIP (1<<17) // 0x00020000 -#define AI_GOAL_KEEP_SAFE_DISTANCE (1<<18) // 0x00040000 -#define AI_GOAL_REARM_REPAIR (1<<19) // 0x00080000 - -// resume regular goals -#define AI_GOAL_STAY_STILL (1<<20) // 0x00100000 -#define AI_GOAL_PLAY_DEAD (1<<21) // 0x00200000 - -// added by SCP -#define AI_GOAL_CHASE_WEAPON (1<<22) // 0x00400000 -#define AI_GOAL_FLY_TO_SHIP (1<<23) // 0x00800000 -#define AI_GOAL_IGNORE_NEW (1<<24) // 0x01000000 -#define AI_GOAL_CHASE_SHIP_CLASS (1<<25) // 0x02000000 -#define AI_GOAL_PLAY_DEAD_PERSISTENT (1<<26) // 0x04000000 -#define AI_GOAL_LUA (1<<27) // 0x08000000 -#define AI_GOAL_DISARM_SHIP_TACTICAL (1<<28) // 0x10000000 -#define AI_GOAL_DISABLE_SHIP_TACTICAL (1<<29) // 0x20000000 - -// now the masks for ship types - -// Goober5000: added AI_GOAL_STAY_NEAR_SHIP and AI_GOAL_KEEP_SAFE_DISTANCE as valid for fighters -//WMC - Don't need these anymore. Whee! -/* -#define AI_GOAL_ACCEPT_FIGHTER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_CHASE | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_DESTROY_SUBSYSTEM | AI_GOAL_CHASE_WING | AI_GOAL_GUARD | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISABLE_SHIP_TACTICAL | AI_GOAL_DISARM_SHIP | AI_GOAL_DISARM_SHIP_TACTICAL | AI_GOAL_CHASE_ANY | AI_GOAL_IGNORE | AI_GOAL_IGNORE_NEW | AI_GOAL_GUARD_WING | AI_GOAL_EVADE_SHIP | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD | AI_GOAL_PLAY_DEAD_PERSISTENT | AI_GOAL_STAY_NEAR_SHIP | AI_GOAL_KEEP_SAFE_DISTANCE ) -#define AI_GOAL_ACCEPT_BOMBER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_FIGHTER | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_STEALTH ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_FIGHTER | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_TRANSPORT ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_DOCK | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_UNDOCK | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD | AI_GOAL_PLAY_DEAD_PERSISTENT | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_FREIGHTER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_TRANSPORT | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_CRUISER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_FREIGHTER | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_CORVETTE ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_CRUISER | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_GAS_MINER ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_CRUISER | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_AWACS ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_CRUISER | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_CAPITAL ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_CRUISER & ~(AI_GOAL_DOCK | AI_GOAL_UNDOCK) | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_SUPERCAP ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_CAPITAL | AI_GOAL_STAY_NEAR_SHIP ) -#define AI_GOAL_ACCEPT_SUPPORT ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_DOCK | AI_GOAL_UNDOCK | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_STAY_NEAR_SHIP | AI_GOAL_KEEP_SAFE_DISTANCE | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD | AI_GOAL_PLAY_DEAD_PERSISTENT ) -#define AI_GOAL_ACCEPT_ESCAPEPOD ( AI_GOAL_FLY_TO_SHIP | AI_GOAL_ACCEPT_TRANSPORT| AI_GOAL_STAY_NEAR_SHIP ) -*/ +enum ai_goal_mode : uint8_t +{ + AI_GOAL_NONE = 0, + AI_GOAL_PLACEHOLDER_1, + + AI_GOAL_CHASE, // per the original #define list, AI_GOAL_CHASE started at 2 (1<<1) + AI_GOAL_DOCK, // used for undocking as well + AI_GOAL_WAYPOINTS, + AI_GOAL_WAYPOINTS_ONCE, + AI_GOAL_WARP, + AI_GOAL_DESTROY_SUBSYSTEM, + AI_GOAL_FORM_ON_WING, + AI_GOAL_UNDOCK, + AI_GOAL_CHASE_WING, + AI_GOAL_GUARD, + AI_GOAL_DISABLE_SHIP, + AI_GOAL_DISARM_SHIP, + AI_GOAL_CHASE_ANY, + AI_GOAL_IGNORE, + AI_GOAL_GUARD_WING, + AI_GOAL_EVADE_SHIP, + + // the next goals are for support ships only + AI_GOAL_STAY_NEAR_SHIP, + AI_GOAL_KEEP_SAFE_DISTANCE, + AI_GOAL_REARM_REPAIR, + + // resume regular goals + AI_GOAL_STAY_STILL, + AI_GOAL_PLAY_DEAD, + + // added by SCP + AI_GOAL_CHASE_WEAPON, + AI_GOAL_FLY_TO_SHIP, + AI_GOAL_IGNORE_NEW, + AI_GOAL_CHASE_SHIP_CLASS, + AI_GOAL_PLAY_DEAD_PERSISTENT, + AI_GOAL_LUA, + AI_GOAL_DISARM_SHIP_TACTICAL, + AI_GOAL_DISABLE_SHIP_TACTICAL, + + AI_GOAL_NUM_VALUES +}; -#define MAX_AI_DOCK_NAMES 25 +inline ai_goal_mode int_to_ai_goal_mode(int int_mode) +{ + if (int_mode >= 0 && int_mode < AI_GOAL_NUM_VALUES) + return static_cast(int_mode); + + Warning(LOCATION, "ai_goal_mode %d out of range! Setting to AI_GOAL_NONE.", int_mode); + return AI_GOAL_NONE; +} + +inline bool ai_goal_is_disable_or_disarm(ai_goal_mode ai_mode) +{ + return ai_mode == AI_GOAL_DISABLE_SHIP || ai_mode == AI_GOAL_DISABLE_SHIP_TACTICAL || ai_mode == AI_GOAL_DISARM_SHIP || ai_mode == AI_GOAL_DISARM_SHIP_TACTICAL; +} +inline bool ai_goal_is_specific_chase(ai_goal_mode ai_mode) +{ + return ai_mode == AI_GOAL_CHASE || ai_mode == AI_GOAL_CHASE_WING || ai_mode == AI_GOAL_CHASE_SHIP_CLASS; +} enum class ai_achievability { ACHIEVABLE, NOT_ACHIEVABLE, NOT_KNOWN, SATISFIED }; @@ -110,9 +116,9 @@ struct ai_lua_parameters { // structure for AI goals typedef struct ai_goal { int signature; // Unique identifier. All goals ever created (per mission) have a unique signature. - int ai_mode; // one of the AIM_* modes for this goal + ai_goal_mode ai_mode; // one of the AI_GOAL_* modes for this goal int ai_submode; // maybe need a submode - int type; // one of the AIG_TYPE_* values above + ai_goal_type type; // one of the ai_goal_type (originally AIG_TYPE_*) values above flagset flags; // one of the AIGF_* values above fix time; // time at which this goal was issued. int priority; // how important is this goal -- number 0 - 100 @@ -144,18 +150,20 @@ typedef struct ai_goal { } ai_goal; -extern void ai_goal_reset(ai_goal *aigp, bool adding_goal = false, int ai_mode = AI_GOAL_NONE, int ai_submode = -1, int type = -1); +extern void ai_goal_reset(ai_goal *aigp, bool adding_goal = false, ai_goal_mode ai_mode = AI_GOAL_NONE, int ai_submode = -1, ai_goal_type type = ai_goal_type::INVALID); -typedef flag_def_list_templated ai_goal_list; +typedef flag_def_list_templated ai_goal_list; extern ai_goal_list Ai_goal_names[]; extern int Num_ai_goals; +#define MAX_AI_DOCK_NAMES 25 + extern int Num_ai_dock_names; extern char Ai_dock_names[MAX_AI_DOCK_NAMES][NAME_LENGTH]; -extern const char *Ai_goal_text(int goal, int submode); +extern const char *Ai_goal_text(ai_goal_mode goal, int submode); // every goal in a mission gets a unique signature extern int Ai_goal_signature; @@ -168,17 +176,17 @@ extern void ai_process_mission_orders( int objnum, ai_info *aip ); extern int ai_goal_num(ai_goal *goals); // adds goals to ships/wing through sexpressions -extern void ai_add_ship_goal_scripting(int mode, int submode, int priority, const char *shipname, ai_info *aip); -extern void ai_add_ship_goal_sexp( int sexp, int type, ai_info *aip ); -extern void ai_add_wing_goal_sexp( int sexp, int type, wing *wingp ); -extern void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, const char *actor_name); +extern void ai_add_ship_goal_scripting(ai_goal_mode mode, int submode, int priority, const char *shipname, ai_info *aip); +extern void ai_add_ship_goal_sexp(int sexp, ai_goal_type type, ai_info *aip); +extern void ai_add_wing_goal_sexp(int sexp, ai_goal_type type, wing *wingp); +extern void ai_add_goal_sub_sexp(int sexp, ai_goal_type type, ai_info *aip, ai_goal *aigp, const char *actor_name); extern int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp, bool &remove_more ); extern void ai_remove_wing_goal_sexp( int sexp, wing *wingp ); // adds goals to ships/sings through player orders -extern void ai_add_ship_goal_player(int type, int mode, int submode, const char* shipname, ai_info* aip, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} }); -extern void ai_add_wing_goal_player(int type, int mode, int submode, const char* shipname, int wingnum, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} }); +extern void ai_add_ship_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char* shipname, ai_info* aip, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} }); +extern void ai_add_wing_goal_player(ai_goal_type type, ai_goal_mode mode, int submode, const char* shipname, int wingnum, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} }); extern void ai_remove_ship_goal( ai_info *aip, int index ); extern void ai_clear_ship_goals( ai_info *aip ); @@ -193,7 +201,7 @@ extern void ai_update_goal_references(ai_goal *goals, sexp_ref_type type, const extern bool query_referenced_in_ai_goals(ai_goal *goals, sexp_ref_type type, const char *name); extern char *ai_add_dock_name(const char *str); -extern int ai_query_goal_valid( int ship, int ai_goal_type ); +extern int ai_query_goal_valid( int ship, ai_goal_mode ai_mode ); extern void ai_add_goal_ship_internal( ai_info *aip, int goal_type, char *name, int docker_point, int dockee_point, int immediate = 1 ); diff --git a/code/autopilot/autopilot.cpp b/code/autopilot/autopilot.cpp index 4cd14ac6b53..e0574673c9e 100644 --- a/code/autopilot/autopilot.cpp +++ b/code/autopilot/autopilot.cpp @@ -241,7 +241,7 @@ bool StartAutopilot() // is support ship trying to rearm-repair if ( ai_find_goal_index( support_ship_aip->goals, AI_GOAL_REARM_REPAIR ) == -1 ) { // no, so tell it to depart - ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WARP, -1, NULL, support_ship_aip ); + ai_add_ship_goal_player( ai_goal_type::PLAYER_SHIP, AI_GOAL_WARP, -1, nullptr, support_ship_aip ); } else { // yes send_autopilot_msgID(NP_MSG_FAIL_SUPPORT_WORKING); @@ -464,12 +464,12 @@ bool StartAutopilot() { if (Navs[CurrentNav].flags & NP_WAYPOINT) { - ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WAYPOINTS_ONCE, 0, Waypoint_lists[Navs[CurrentNav].target_index].get_name(), &Ai_info[Ships[i].ai_index] ); + ai_add_ship_goal_player( ai_goal_type::PLAYER_SHIP, AI_GOAL_WAYPOINTS_ONCE, 0, Waypoint_lists[Navs[CurrentNav].target_index].get_name(), &Ai_info[Ships[i].ai_index] ); //fixup has to wait until after wing goals } else { - ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_FLY_TO_SHIP, 0, Ships[Objects[Navs[CurrentNav].target_index].instance].ship_name, &Ai_info[Ships[i].ai_index] ); + ai_add_ship_goal_player( ai_goal_type::PLAYER_SHIP, AI_GOAL_FLY_TO_SHIP, 0, Ships[Objects[Navs[CurrentNav].target_index].instance].ship_name, &Ai_info[Ships[i].ai_index] ); } } @@ -485,15 +485,15 @@ bool StartAutopilot() { //ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip ); - //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_STAY_NEAR_SHIP, 0, target_shipname, wingnum ); - //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, target_shipname, wingnum ); + //ai_add_wing_goal_player( ai_goal_type::PLAYER_WING, AI_GOAL_STAY_NEAR_SHIP, 0, target_shipname, wingnum ); + //ai_add_wing_goal_player( ai_goal_type::PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, target_shipname, wingnum ); //ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) ); ai_clear_wing_goals( &Wings[i] ); if (Navs[CurrentNav].flags & NP_WAYPOINT) { - ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, Waypoint_lists[Navs[CurrentNav].target_index].get_name(), i ); + ai_add_wing_goal_player( ai_goal_type::PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, Waypoint_lists[Navs[CurrentNav].target_index].get_name(), i ); // "fix up" the goal for (j = 0; j < MAX_AI_GOALS; j++) @@ -507,7 +507,7 @@ bool StartAutopilot() } else { - ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_FLY_TO_SHIP, 0, Ships[Objects[Navs[CurrentNav].target_index].instance].ship_name, i ); + ai_add_wing_goal_player( ai_goal_type::PLAYER_WING, AI_GOAL_FLY_TO_SHIP, 0, Ships[Objects[Navs[CurrentNav].target_index].instance].ship_name, i ); } } diff --git a/code/hud/hudsquadmsg.cpp b/code/hud/hudsquadmsg.cpp index 82964eba021..b6a9a981c08 100644 --- a/code/hud/hudsquadmsg.cpp +++ b/code/hud/hudsquadmsg.cpp @@ -1001,7 +1001,8 @@ int enemy_message(int message) { int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, int update_history, int player_num ) { ai_info *ainfo; - int ai_mode, ai_submode; // ai mode and submode needed for ship commands + ai_goal_mode ai_mode; // ai mode... + int ai_submode; // ...and submode needed for ship commands ship *target = nullptr; char *target_shipname; ai_lua_parameters lua_target; @@ -1257,7 +1258,7 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, // handle case of messaging one ship. Deal with messaging all fighters next. if (ai_mode != AI_GOAL_NONE) { Assert(ai_submode != -1234567); - ai_add_ship_goal_player(AIG_TYPE_PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index], lua_target); + ai_add_ship_goal_player(ai_goal_type::PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index], lua_target); if (update_history == SQUADMSG_HISTORY_ADD_ENTRY) { hud_add_issued_order(Ships[shipnum].ship_name, command); hud_update_last_order(target_shipname, player_num, special_index); @@ -1293,7 +1294,8 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message, int update_history, int player_num ) { ai_info *ainfo; - int ai_mode, ai_submode; // ai mode and submode needed for ship commands + ai_goal_mode ai_mode; // ai mode... + int ai_submode; // ...and submode needed for ship commands ship *target = nullptr; char *target_shipname; ai_lua_parameters lua_target; @@ -1476,7 +1478,7 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message, if (ai_mode != AI_GOAL_NONE) { Assert(ai_submode != -1234567); - ai_add_wing_goal_player(AIG_TYPE_PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum, lua_target); + ai_add_wing_goal_player(ai_goal_type::PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum, lua_target); if (update_history == SQUADMSG_HISTORY_ADD_ENTRY) { hud_add_issued_order(Wings[wingnum].name, command); diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index 21a1ae499e3..def66285da9 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -2256,7 +2256,7 @@ int parse_create_object_sub(p_object *p_objp, bool standalone_ship) // set up the ai goals for this object. for (sexp = CDR(p_objp->ai_goals); sexp != -1; sexp = CDR(sexp)) - ai_add_ship_goal_sexp(sexp, AIG_TYPE_EVENT_SHIP, aip); + ai_add_ship_goal_sexp(sexp, ai_goal_type::EVENT_SHIP, aip); // free the sexpression nodes only for non-wing ships. wing code will handle its own case if (p_objp->wingnum < 0) @@ -4847,7 +4847,7 @@ void parse_wing(mission *pm) // this will assign the goals to the wings as well as to any ships in the wing that have been // already created. for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) ) - ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingp); // used by Fred + ai_add_wing_goal_sexp(sexp, ai_goal_type::EVENT_WING, wingp); // used by Fred free_sexp2(wing_goals); // free up sexp nodes for reuse, since they aren't needed anymore. } diff --git a/code/network/multimsgs.cpp b/code/network/multimsgs.cpp index 98a9c5ffe76..c0f70ee5bc9 100644 --- a/code/network/multimsgs.cpp +++ b/code/network/multimsgs.cpp @@ -5146,10 +5146,10 @@ void send_ai_info_update_packet( object *objp, char what, object * other_objp ) // for orders, we only need to send a little bit of information here. Be sure that the // first order for this ship is active - Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ); + Assert( (aip->active_goal != AI_ACTIVE_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ); aigp = &aip->goals[aip->active_goal]; - ADD_INT( aigp->ai_mode ); + ADD_INT( static_cast(aigp->ai_mode) ); ADD_INT( aigp->ai_submode ); shipnum = -1; @@ -5167,7 +5167,7 @@ void send_ai_info_update_packet( object *objp, char what, object * other_objp ) ADD_USHORT( other_signature ); // for docking, add the dock and dockee index - if (aigp->ai_mode & (AI_GOAL_DOCK | AI_GOAL_REARM_REPAIR)) { + if (aigp->ai_mode == AI_GOAL_DOCK || aigp->ai_mode == AI_GOAL_REARM_REPAIR) { Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] && aigp->flags[AI::Goal_Flags::Docker_index_valid]); Assert( (aigp->docker.index >= 0) && (aigp->docker.index < UCHAR_MAX) ); Assert( (aigp->dockee.index >= 0) && (aigp->dockee.index < UCHAR_MAX) ); @@ -5192,7 +5192,8 @@ void send_ai_info_update_packet( object *objp, char what, object * other_objp ) void process_ai_info_update_packet( ubyte *data, header *hinfo) { int offset = HEADER_LENGTH; - int mode, submode; + ai_goal_mode mode; + int int_mode, submode; ushort net_signature, other_net_signature; object *objp, *other_objp; ai_info *aip; @@ -5237,10 +5238,12 @@ void process_ai_info_update_packet( ubyte *data, header *hinfo) break; case AI_UPDATE_ORDERS: - GET_INT( mode ); + GET_INT( int_mode ); + mode = int_to_ai_goal_mode(int_mode); GET_INT( submode ); GET_USHORT( other_net_signature ); - if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) { + + if ( mode == AI_GOAL_DOCK || mode == AI_GOAL_REARM_REPAIR ) { GET_DATA(docker_index); GET_DATA(dockee_index); } @@ -5257,7 +5260,7 @@ void process_ai_info_update_packet( ubyte *data, header *hinfo) aigp->ai_submode = submode; // for docking, add the docker and dockee index to the active goal - if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) { + if ( mode == AI_GOAL_DOCK || mode == AI_GOAL_REARM_REPAIR ) { aigp->docker.index = docker_index; aigp->dockee.index = dockee_index; aigp->flags.set(AI::Goal_Flags::Dockee_index_valid); diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index a12f6dd6c55..f5a32e16a23 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -13176,7 +13176,7 @@ void sexp_add_goal(int n) if (!ship_entry->has_shipp()) return; // ship not around anymore???? then forget it! - ai_add_ship_goal_sexp(goal_node, AIG_TYPE_EVENT_SHIP, &(Ai_info[ship_entry->shipp()->ai_index])); + ai_add_ship_goal_sexp(goal_node, ai_goal_type::EVENT_SHIP, &(Ai_info[ship_entry->shipp()->ai_index])); return; } @@ -13186,7 +13186,7 @@ void sexp_add_goal(int n) if (wingp->flags[Ship::Wing_Flags::Gone]) return; // wing not around anymore???? then forget it! - ai_add_wing_goal_sexp(goal_node, AIG_TYPE_EVENT_WING, wingp); + ai_add_wing_goal_sexp(goal_node, ai_goal_type::EVENT_WING, wingp); } } @@ -13210,7 +13210,7 @@ void sexp_remove_goal(int n) if (goalindex >= 0) { if (aip->active_goal == goalindex) - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; } } while (remove_more); return; diff --git a/code/parse/sexp.h b/code/parse/sexp.h index 3fda6beaa89..cad826b4e96 100644 --- a/code/parse/sexp.h +++ b/code/parse/sexp.h @@ -1282,10 +1282,12 @@ enum sexp_error_check // numbers used in special_training_check() function #define SPECIAL_CHECK_TRAINING_FAILURE 2000 -typedef struct sexp_ai_goal_link { - int ai_goal; +enum ai_goal_mode : uint8_t; + +struct sexp_ai_goal_link { + ai_goal_mode ai_goal; int op_code; -} sexp_ai_goal_link; +}; enum class sexp_oper_type diff --git a/code/scripting/api/objs/enums.cpp b/code/scripting/api/objs/enums.cpp index 65bf16852bf..58550c4ed2a 100644 --- a/code/scripting/api/objs/enums.cpp +++ b/code/scripting/api/objs/enums.cpp @@ -26,6 +26,8 @@ const lua_enum_def_list Enumerations[] = { {"FLIGHTMODE_FLIGHTCURSOR", LE_FLIGHTMODE_FLIGHTCURSOR, true}, {"FLIGHTMODE_SHIPLOCKED", LE_FLIGHTMODE_SHIPLOCKED, true}, {"ORDER_ATTACK", LE_ORDER_ATTACK, true}, + {"ORDER_ATTACK_WING", LE_ORDER_ATTACK_WING, true}, + {"ORDER_ATTACK_SHIP_CLASS", LE_ORDER_ATTACK_SHIP_CLASS, true}, {"ORDER_ATTACK_ANY", LE_ORDER_ATTACK_ANY, true}, {"ORDER_DEPART", LE_ORDER_DEPART, true}, {"ORDER_DISABLE", LE_ORDER_DISABLE, true}, @@ -37,6 +39,7 @@ const lua_enum_def_list Enumerations[] = { {"ORDER_FLY_TO", LE_ORDER_FLY_TO, true}, {"ORDER_FORM_ON_WING", LE_ORDER_FORM_ON_WING, true}, {"ORDER_GUARD", LE_ORDER_GUARD, true}, + {"ORDER_GUARD_WING", LE_ORDER_GUARD_WING, true}, {"ORDER_IGNORE_SHIP", LE_ORDER_IGNORE, true}, {"ORDER_IGNORE_SHIP_NEW", LE_ORDER_IGNORE_NEW, true}, {"ORDER_KEEP_SAFE_DISTANCE", LE_ORDER_KEEP_SAFE_DISTANCE, true}, @@ -48,9 +51,7 @@ const lua_enum_def_list Enumerations[] = { {"ORDER_UNDOCK", LE_ORDER_UNDOCK, true}, {"ORDER_WAYPOINTS", LE_ORDER_WAYPOINTS, true}, {"ORDER_WAYPOINTS_ONCE", LE_ORDER_WAYPOINTS_ONCE, true}, - {"ORDER_ATTACK_WING", LE_ORDER_ATTACK_WING, true}, - {"ORDER_GUARD_WING", LE_ORDER_GUARD_WING, true}, - {"ORDER_ATTACK_SHIP_CLASS", LE_ORDER_ATTACK_SHIP_CLASS, true}, + {"ORDER_LUA", LE_ORDER_LUA, true}, {"PARTICLE_DEBUG", LE_PARTICLE_DEBUG, true}, {"PARTICLE_BITMAP", LE_PARTICLE_BITMAP, true}, {"PARTICLE_FIRE", LE_PARTICLE_FIRE, true}, diff --git a/code/scripting/api/objs/enums.h b/code/scripting/api/objs/enums.h index f4e592535b2..a2db7fe9d60 100644 --- a/code/scripting/api/objs/enums.h +++ b/code/scripting/api/objs/enums.h @@ -25,6 +25,8 @@ enum lua_enum : int32_t { LE_FLIGHTMODE_FLIGHTCURSOR, LE_FLIGHTMODE_SHIPLOCKED, LE_ORDER_ATTACK, + LE_ORDER_ATTACK_WING, + LE_ORDER_ATTACK_SHIP_CLASS, LE_ORDER_ATTACK_ANY, LE_ORDER_DEPART, LE_ORDER_DISABLE, @@ -36,6 +38,7 @@ enum lua_enum : int32_t { LE_ORDER_FLY_TO, LE_ORDER_FORM_ON_WING, LE_ORDER_GUARD, + LE_ORDER_GUARD_WING, LE_ORDER_IGNORE, LE_ORDER_IGNORE_NEW, LE_ORDER_KEEP_SAFE_DISTANCE, @@ -47,9 +50,7 @@ enum lua_enum : int32_t { LE_ORDER_UNDOCK, LE_ORDER_WAYPOINTS, LE_ORDER_WAYPOINTS_ONCE, - LE_ORDER_ATTACK_WING, - LE_ORDER_GUARD_WING, - LE_ORDER_ATTACK_SHIP_CLASS, + LE_ORDER_LUA, LE_PARTICLE_DEBUG, LE_PARTICLE_BITMAP, LE_PARTICLE_FIRE, diff --git a/code/scripting/api/objs/order.cpp b/code/scripting/api/objs/order.cpp index df78c346850..96e2b0b9b5f 100644 --- a/code/scripting/api/objs/order.cpp +++ b/code/scripting/api/objs/order.cpp @@ -89,6 +89,10 @@ ADE_FUNC(getType, l_Order, NULL, "Gets the type of the order.", "enumeration", " return ade_set_error(L, "o", l_Enum.Set(enum_h())); switch(ohp->aigp->ai_mode){ + case AI_GOAL_NONE: + case AI_GOAL_PLACEHOLDER_1: + case AI_GOAL_NUM_VALUES: + break; case AI_GOAL_DESTROY_SUBSYSTEM: case AI_GOAL_CHASE_WEAPON: case AI_GOAL_CHASE: @@ -169,6 +173,9 @@ ADE_FUNC(getType, l_Order, NULL, "Gets the type of the order.", "enumeration", " case AI_GOAL_CHASE_SHIP_CLASS: eh_idx = LE_ORDER_ATTACK_SHIP_CLASS; break; + case AI_GOAL_LUA: + eh_idx = LE_ORDER_LUA; + break; } return ade_set_args(L, "o", l_Enum.Set(enum_h(eh_idx))); @@ -180,7 +187,7 @@ ADE_VIRTVAR(Target, l_Order, "object", "Target of the order. Value may also be a object_h *newh = NULL; ai_info *aip = NULL; waypoint_list *wpl = NULL; - int shipnum = -1, objnum = -1; + int shipnum = -1, wingnum = -1, objnum = -1; if(!ade_get_args(L, "o|o", l_Order.GetPtr(&ohp), l_Object.GetPtr(&newh))) return ade_set_error(L, "o", l_Object.Set(object_h())); @@ -272,6 +279,8 @@ ADE_VIRTVAR(Target, l_Order, "object", "Target of the order. Value may also be a } } break; + default: + break; } } } @@ -322,12 +331,14 @@ ADE_VIRTVAR(Target, l_Order, "object", "Target of the order. Value may also be a break; case AI_GOAL_CHASE_WING: case AI_GOAL_GUARD_WING: - int wingnum = wing_name_lookup(ohp->aigp->target_name); + wingnum = wing_name_lookup(ohp->aigp->target_name); if (Wings[wingnum].current_count > 0){ shipnum = Wings[wingnum].ship_index[0]; objnum = Ships[shipnum].objnum; } break; + default: + break; } return ade_set_object_with_breed(L, objnum); diff --git a/code/scripting/api/objs/ship.cpp b/code/scripting/api/objs/ship.cpp index 0dd52cd1110..5e146ac6049 100644 --- a/code/scripting/api/objs/ship.cpp +++ b/code/scripting/api/objs/ship.cpp @@ -1530,7 +1530,7 @@ ADE_FUNC(giveOrder, l_Ship, "enumeration Order, [object Target=nil, subsystem Ta bool tgh_valid = tgh && tgh->isValid(); bool tgsh_valid = tgsh && tgsh->isValid(); - int ai_mode = AI_GOAL_NONE; + ai_goal_mode ai_mode = AI_GOAL_NONE; int ai_submode = -1234567; const char *ai_shipname = NULL; switch(eh->index) diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 9dbb9d032ec..b676b93567a 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -17298,9 +17298,6 @@ SCP_string ship_return_orders(ship* sp) // The active goal is always in the first element of aip->goals[] aigp = &aip->goals[0]; - if (aigp->ai_mode < 0) - return SCP_string(); - auto order_text = Ai_goal_text(aigp->ai_mode, aigp->ai_submode); if (order_text == nullptr) return SCP_string(); diff --git a/code/ship/ship.h b/code/ship/ship.h index e91aef14ea7..0297c85cda1 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1025,7 +1025,7 @@ typedef struct ship_type_info { float fog_complete_dist; //AI - int ai_valid_goals; + SCP_set ai_valid_goals; std::set ai_player_orders; int ai_active_dock; int ai_passive_dock; @@ -1046,7 +1046,7 @@ typedef struct ship_type_info { : debris_max_speed( 0.f ), ff_multiplier( 0.f ), emp_multiplier( 0.f ), warp_sound_range_multiplier( 1.f ), fog_start_dist( 0.f ), fog_complete_dist( 0.f ), - ai_valid_goals( 0 ), ai_active_dock( 0 ), ai_passive_dock( 0 ), + ai_active_dock( 0 ), ai_passive_dock( 0 ), skip_deathroll_chance( 0.f ) { diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index 1ea3d487649..192d34bcb8b 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -6373,7 +6373,7 @@ void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_o // Goober5000 - if we're going bonkers, pretend we're not targeting our own team ai_info *parent_aip = &Ai_info[Ships[parent_objp->instance].ai_index]; - if (parent_aip->active_goal != AI_GOAL_NONE && parent_aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) { + if (parent_aip->active_goal != AI_ACTIVE_GOAL_NONE && parent_aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) { if (parent_aip->goals[parent_aip->active_goal].flags[AI::Goal_Flags::Target_own_team]) { targeting_same = 0; } @@ -7900,7 +7900,7 @@ void weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos, set_target_objnum(aip, -1); // If this ship had a dynamic goal of chasing this weapon, clear the dynamic goal. if (aip->resume_goal_time != -1) - aip->active_goal = AI_GOAL_NONE; + aip->active_goal = AI_ACTIVE_GOAL_NONE; } if (aip->goal_objnum == objnum) { diff --git a/fred2/management.cpp b/fred2/management.cpp index feb3f33e1df..49024a82da7 100644 --- a/fred2/management.cpp +++ b/fred2/management.cpp @@ -122,10 +122,12 @@ CCriticalSection CS_cur_object_index; ai_goal_list Ai_goal_list[] = { { "Waypoints", AI_GOAL_WAYPOINTS, 0 }, { "Waypoints once", AI_GOAL_WAYPOINTS_ONCE, 0 }, - { "Attack", AI_GOAL_CHASE | AI_GOAL_CHASE_WING, 0 }, + { "Attack", AI_GOAL_CHASE, 0 }, + { "Attack", AI_GOAL_CHASE_WING, 0 }, // duplicate needed because we can no longer use bitwise operators { "Attack any ship", AI_GOAL_CHASE_ANY, 0 }, { "Attack ship class", AI_GOAL_CHASE_SHIP_CLASS, 0 }, - { "Guard", AI_GOAL_GUARD | AI_GOAL_GUARD_WING, 0 }, + { "Guard", AI_GOAL_GUARD, 0 }, + { "Guard", AI_GOAL_GUARD_WING, 0 }, // duplicate needed because we can no longer use bitwise operators { "Disable ship", AI_GOAL_DISABLE_SHIP, 0 }, { "Disable ship (tactical)",AI_GOAL_DISABLE_SHIP_TACTICAL, 0 }, { "Disarm ship", AI_GOAL_DISARM_SHIP, 0 }, @@ -2227,7 +2229,7 @@ char *object_name(int obj) return "*unknown*"; } -const char *get_order_name(int order) +const char *get_order_name(ai_goal_mode order) { int i; @@ -2235,7 +2237,7 @@ const char *get_order_name(int order) return "None"; for (i=0; i ResetContent(); - z = m_behavior_box[i] -> AddString("None"); - m_behavior_box[i] -> SetItemData(z, (DWORD) AI_GOAL_NONE); - for (j=0; j AddString(Ai_goal_list[j].name); - m_behavior_box[i] -> SetItemData(z, (DWORD) Ai_goal_list[j].def); - } + for (const auto &entry: m_ai_goal_combo_data) { + m_behavior_box[i]->AddString(entry.first); } } @@ -337,6 +335,56 @@ BOOL ShipGoalsDlg::OnInitDialog() return TRUE; } +void ShipGoalsDlg::init_combo_data(int valid[]) +{ + // don't add more than one of the same string (case-insensitive) + SCP_unordered_map strings_to_indexes; + + // start by adding "None" + auto none_str = "None"; + strings_to_indexes.emplace(none_str, 0); + SCP_set none_set{ AI_GOAL_NONE }; + m_ai_goal_combo_data.clear(); + m_ai_goal_combo_data.emplace_back(none_str, std::move(none_set)); + + // initialize the data used in the combo boxes in the Initial Orders dialog + for (int i = 0; i < Ai_goal_list_size; ++i) + { + if (!valid[i]) + continue; + auto &entry = Ai_goal_list[i]; + + // see if we already added the string + auto ii = strings_to_indexes.find(entry.name); + if (ii != strings_to_indexes.end()) + { + // skip adding the string, but add the entry's goal definition to the combo box data at the existing index + m_ai_goal_combo_data[ii->second].second.insert(entry.def); + } + else + { + // this string will correspond to the index that is about to be created + strings_to_indexes[entry.name] = m_ai_goal_combo_data.size(); + + // add the entry's goal definition as the first (maybe only) member of the set + SCP_set new_set{ entry.def }; + m_ai_goal_combo_data.emplace_back(entry.name, std::move(new_set)); + } + } +} + +ai_goal_mode ShipGoalsDlg::get_first_mode_from_combo_box(int which_item) +{ + // which_item indicates initial goal 1 through MAX_AI_GOALS, so find that behavior... + int behavior_index = m_behavior[which_item]; + + // the behavior is the index into the combo box that contains a subset of goals from Ai_goal_list + const auto &set = m_ai_goal_combo_data[behavior_index].second; + + // just get the first mode in the set, since chase/chase-wing and guard/guard-wing are handled respectively together + return *(set.begin()); +} + void ShipGoalsDlg::initialize_multi() { int i, flag = 0; @@ -403,7 +451,8 @@ void ShipGoalsDlg::initialize_multi() // perform one-time initialization of data from the goals struct. void ShipGoalsDlg::initialize(ai_goal *goals, int ship) { - int i, item, num, inst, flag, mode; + int i, item, num, inst, flag; + ai_goal_mode mode; object *ptr; // note that the flag variable is a bitfield: @@ -431,9 +480,10 @@ void ShipGoalsDlg::initialize(ai_goal *goals, int ship) m_behavior[item] = 0; if (mode != AI_GOAL_NONE) { - i = m_behavior_box[item] -> GetCount(); + i = static_cast(m_ai_goal_combo_data.size()); while (i-- > 0){ - if (mode & (m_behavior_box[item]->GetItemData(i))) { + const auto &set = m_ai_goal_combo_data[i].second; + if (set.find(mode) != set.end()) { m_behavior[item] = i; break; } @@ -611,7 +661,7 @@ void ShipGoalsDlg::set_item(int item, int init) return; } - auto mode = m_behavior_box[item] -> GetItemData(m_behavior[item]); + auto mode = get_first_mode_from_combo_box(item); m_priority_box[item] -> EnableWindow(TRUE); if ((mode == AI_GOAL_CHASE_ANY) || (mode == AI_GOAL_UNDOCK) || (mode == AI_GOAL_KEEP_SAFE_DISTANCE) || (mode == AI_GOAL_PLAY_DEAD) || (mode == AI_GOAL_PLAY_DEAD_PERSISTENT) || (mode == AI_GOAL_WARP) ) { m_object_box[item] -> EnableWindow(FALSE); @@ -667,9 +717,11 @@ void ShipGoalsDlg::set_item(int item, int init) // for goals that deal with individual ships switch (mode) { case AI_GOAL_DESTROY_SUBSYSTEM: - case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: case AI_GOAL_DOCK: - case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: case AI_GOAL_DISABLE_SHIP: case AI_GOAL_DISABLE_SHIP_TACTICAL: case AI_GOAL_DISARM_SHIP: @@ -715,8 +767,10 @@ void ShipGoalsDlg::set_item(int item, int init) // for goals that deal with wings switch (mode) { - case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: - case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: for (i=0; i AddString(Wings[i].name); @@ -911,7 +965,7 @@ int ShipGoalsDlg::verify_orders(int ship) void ShipGoalsDlg::update_item(int item, int multi) { char *docker, *dockee, *subsys; - int mode; + ai_goal_mode mode; char buf[512], save[80]; waypoint_list *wp_list; @@ -928,7 +982,7 @@ void ShipGoalsDlg::update_item(int item, int multi) m_behavior[item] = 0; } - mode = (int)m_behavior_box[item] -> GetItemData(m_behavior[item]); + mode = get_first_mode_from_combo_box(item); switch (mode) { case AI_GOAL_NONE: case AI_GOAL_CHASE_ANY: @@ -977,7 +1031,8 @@ void ShipGoalsDlg::update_item(int item, int multi) break; - case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: switch (m_data[item] & TYPE_MASK) { case TYPE_SHIP: case TYPE_PLAYER: @@ -1028,7 +1083,8 @@ void ShipGoalsDlg::update_item(int item, int multi) break; - case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: switch (m_data[item] & TYPE_MASK) { case TYPE_SHIP: case TYPE_PLAYER: @@ -1106,7 +1162,7 @@ void ShipGoalsDlg::OnOK() UpdateData(TRUE); for (i=0; i GetItemData(m_behavior[i]); + auto mode = get_first_mode_from_combo_box(i); if ((mode != AI_GOAL_NONE) && (mode != AI_GOAL_CHASE_ANY) && (mode != AI_GOAL_UNDOCK) && (mode != AI_GOAL_KEEP_SAFE_DISTANCE) && (mode != AI_GOAL_PLAY_DEAD) && (mode != AI_GOAL_PLAY_DEAD_PERSISTENT) && (mode != AI_GOAL_WARP) ) { if (!m_object_box[i] -> GetCount()) // no valid objects? m_behavior[i] = 0; @@ -1205,7 +1261,8 @@ void ShipGoalsDlg::set_object(int item) ship_subsys *subsys; if (m_behavior[item] > 0) { - auto mode = m_behavior_box[item] -> GetItemData(m_behavior[item]); + auto mode = get_first_mode_from_combo_box(item); + if (!m_object_box[item] -> GetCount()) m_behavior[item] = m_data[item] = 0; else diff --git a/fred2/shipgoalsdlg.h b/fred2/shipgoalsdlg.h index 555395ab910..2badcb7516f 100644 --- a/fred2/shipgoalsdlg.h +++ b/fred2/shipgoalsdlg.h @@ -29,6 +29,8 @@ class ShipGoalsDlg : public CDialog void OnOK(); void update(); void initialize(ai_goal *goals, int ship = cur_ship); + void init_combo_data(int valid[]); + ai_goal_mode get_first_mode_from_combo_box(int which_item); ShipGoalsDlg(CWnd* pParent = NULL); // standard constructor int self_ship, self_wing; @@ -38,6 +40,7 @@ class ShipGoalsDlg : public CDialog int m_subsys[ED_MAX_GOALS]; int m_dock2[ED_MAX_GOALS]; int m_data[ED_MAX_GOALS]; + SCP_vector>> m_ai_goal_combo_data; CComboBox *m_behavior_box[ED_MAX_GOALS]; CComboBox *m_object_box[ED_MAX_GOALS]; diff --git a/qtfred/src/mission/Editor.cpp b/qtfred/src/mission/Editor.cpp index 4b46b7afd80..08f2ff0807b 100644 --- a/qtfred/src/mission/Editor.cpp +++ b/qtfred/src/mission/Editor.cpp @@ -67,10 +67,12 @@ std::pair query_referenced_in_ai_goals(sexp_ref_type type, const ai_goal_list Ai_goal_list[] = { { "Waypoints", AI_GOAL_WAYPOINTS, 0 }, { "Waypoints once", AI_GOAL_WAYPOINTS_ONCE, 0 }, - { "Attack", AI_GOAL_CHASE | AI_GOAL_CHASE_WING, 0 }, + { "Attack", AI_GOAL_CHASE, 0 }, + { "Attack", AI_GOAL_CHASE_WING, 0 }, // duplicate needed because we can no longer use bitwise operators { "Attack any ship", AI_GOAL_CHASE_ANY, 0 }, { "Attack ship class", AI_GOAL_CHASE_SHIP_CLASS, 0 }, - { "Guard", AI_GOAL_GUARD | AI_GOAL_GUARD_WING, 0 }, + { "Guard", AI_GOAL_GUARD, 0 }, + { "Guard", AI_GOAL_GUARD_WING, 0 }, // duplicate needed because we can no longer use bitwise operators { "Disable ship", AI_GOAL_DISABLE_SHIP, 0 }, { "Disable ship (tactical)",AI_GOAL_DISABLE_SHIP_TACTICAL, 0 }, { "Disarm ship", AI_GOAL_DISARM_SHIP, 0 }, @@ -2906,6 +2908,9 @@ const char* Editor::error_check_initial_orders(ai_goal* goals, int ship, int win break; } + + default: + break; } switch (goals[i].ai_mode) { @@ -2917,7 +2922,6 @@ const char* Editor::error_check_initial_orders(ai_goal* goals, int ship, int win else return "Wing assigned to guard a different team"; } - break; case AI_GOAL_CHASE: @@ -2933,19 +2937,21 @@ const char* Editor::error_check_initial_orders(ai_goal* goals, int ship, int win else return "Wings assigned to attack same team"; } + break; + default: break; } } return NULL; } -const char* Editor::get_order_name(int order) { +const char* Editor::get_order_name(ai_goal_mode order) { if (order == AI_GOAL_NONE) // special case return "None"; for (auto& entry : Ai_goal_list) - if (entry.def & order) + if (entry.def == order) return entry.name; return "???"; diff --git a/qtfred/src/mission/Editor.h b/qtfred/src/mission/Editor.h index 91efbbbad3d..abc20a38006 100644 --- a/qtfred/src/mission/Editor.h +++ b/qtfred/src/mission/Editor.h @@ -295,7 +295,7 @@ class Editor : public QObject { int global_error_check_player_wings(int multi); - const char* get_order_name(int order); + static const char* get_order_name(ai_goal_mode order); void updateStartingWingLoadoutUseCounts(); }; diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp index ee507a95ae1..3e6ce39fe6c 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp @@ -9,6 +9,58 @@ namespace fso { { initializeData(multi, shipp, wingp); } + void ShipGoalsDialogModel::init_combo_data() + { + // don't add more than one of the same string (case-insensitive) + SCP_unordered_map strings_to_indexes; + + // start by adding "None" + auto none_str = "None"; + strings_to_indexes.emplace(none_str, 0); + SCP_set none_set{ AI_GOAL_NONE }; + m_ai_goal_combo_data.clear(); + m_ai_goal_combo_data.emplace_back(none_str, std::move(none_set)); + + // initialize the data used in the combo boxes in the Initial Orders dialog + for (int i = 0; i < Ai_goal_list_size; ++i) + { + if (!valid[i]) + continue; + auto &entry = Editor::getAi_goal_list()[i]; + + // see if we already added the string + auto ii = strings_to_indexes.find(entry.name); + if (ii != strings_to_indexes.end()) + { + // skip adding the string, but add the entry's goal definition to the combo box data at the existing index + m_ai_goal_combo_data[ii->second].second.insert(entry.def); + } + else + { + // this string will correspond to the index that is about to be created + strings_to_indexes[entry.name] = m_ai_goal_combo_data.size(); + + // add the entry's goal definition as the first (maybe only) member of the set + SCP_set new_set{ entry.def }; + m_ai_goal_combo_data.emplace_back(entry.name, std::move(new_set)); + } + } + } + const SCP_vector>> &ShipGoalsDialogModel::get_ai_goal_combo_data() + { + return m_ai_goal_combo_data; + }; + ai_goal_mode ShipGoalsDialogModel::get_first_mode_from_combo_box(int which_item) + { + // which_item indicates initial goal 1 through MAX_AI_GOALS, so find that behavior... + int behavior_index = m_behavior[which_item]; + + // the behavior is the index into the combo box that contains a subset of goals from Ai_goal_list + const auto &set = m_ai_goal_combo_data[behavior_index].second; + + // just get the first mode in the set, since chase/chase-wing and guard/guard-wing are handled respectively together + return *(set.begin()); + } bool ShipGoalsDialogModel::apply() { int i; @@ -79,7 +131,7 @@ namespace fso { void ShipGoalsDialogModel::update_item(const int item) { char* docker, * dockee, * subsys; - int mode; + ai_goal_mode mode; char save[80]{}; SCP_string error_message; waypoint_list* wp_list; @@ -90,7 +142,7 @@ namespace fso { if (!m_multi_edit || m_priority[item] >= 0) goalp[item].priority = m_priority[item]; - mode = m_behavior[item]; + mode = get_first_mode_from_combo_box(item); switch (mode) { case AI_GOAL_NONE: case AI_GOAL_CHASE_ANY: @@ -143,7 +195,8 @@ namespace fso { break; - case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: switch (m_object[item] & TYPE_MASK) { case TYPE_SHIP: case TYPE_PLAYER: @@ -198,7 +251,8 @@ namespace fso { break; - case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: switch (m_object[item] & TYPE_MASK) { case TYPE_SHIP: case TYPE_PLAYER: @@ -338,6 +392,9 @@ namespace fso { case AI_GOAL_WAYPOINTS_ONCE: // case AI_GOAL_WARP: valid[i] = 0; + break; + default: + break; } } } @@ -363,6 +420,9 @@ namespace fso { } } + // initialize the data for the behavior boxes (they remain constant while the dialog is open) + init_combo_data(); + if (self_ship >= 0) { initialize(Ai_info[Ships[self_ship].ai_index].goals, self_ship); } @@ -376,7 +436,8 @@ namespace fso { } void ShipGoalsDialogModel::initialize(ai_goal* goals, int ship) { - int i, item, num, inst, flag, mode; + int i, item, num, inst, flag; + ai_goal_mode mode; object* ptr; SCP_vector docks; @@ -403,9 +464,16 @@ namespace fso { m_priority[item] = 50; } - m_behavior[item] = -1; + m_behavior[item] = 0; if (mode != AI_GOAL_NONE) { - m_behavior[item] = mode; + i = static_cast(m_ai_goal_combo_data.size()); + while (i-- > 0) { + const auto &set = m_ai_goal_combo_data[i].second; + if (set.find(mode) != set.end()) { + m_behavior[item] = i; + break; + } + } } switch (mode) { @@ -548,7 +616,8 @@ namespace fso { } } } - + break; + default: break; } diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h index 8696216550d..cc96559261c 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h @@ -24,6 +24,7 @@ class ShipGoalsDialogModel : public AbstractDialogModel { int Ai_goal_list_size = Editor::getAigoal_list_size(); void initialize(ai_goal* goals, int ship); void initialize_multi(); + void init_combo_data(); @@ -34,6 +35,7 @@ class ShipGoalsDialogModel : public AbstractDialogModel { SCP_string m_subsys[ED_MAX_GOALS]; long long m_dock2[ED_MAX_GOALS]; //int m_data[ED_MAX_GOALS]; + SCP_vector>> m_ai_goal_combo_data; int valid[MAX_VALID]; bool m_multi_edit; @@ -45,6 +47,10 @@ class ShipGoalsDialogModel : public AbstractDialogModel { public: ShipGoalsDialogModel(QObject* parent, EditorViewport* viewport, bool multi, int self_ship, int self_wing); + + const SCP_vector>> &get_ai_goal_combo_data(); + ai_goal_mode get_first_mode_from_combo_box(int which_item); + void initializeData(bool multi, int self_ship, int self_wing); bool apply() override; void reject() override; diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp index 9691479537c..e3063d05425 100644 --- a/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp +++ b/qtfred/src/ui/dialogs/ShipEditor/ShipGoalsDialog.cpp @@ -77,8 +77,7 @@ ShipGoalsDialog::ShipGoalsDialog(QWidget* parent, EditorViewport* viewport, bool connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipGoalsDialog::updateUI); for (int i = 0; i < ED_MAX_GOALS; i++) { connect(behaviors[i], QOverload::of(&QComboBox::currentIndexChanged), [=](int index) { - int datap = behaviors[i]->itemData(index).value(); - _model->setBehavior(i, datap); + _model->setBehavior(i, index); }); connect(objects[i], QOverload::of(&QComboBox::currentIndexChanged), [=](int index) { int datap = objects[i]->itemData(index).value(); @@ -119,22 +118,23 @@ void ShipGoalsDialog::rejectHandler() void ShipGoalsDialog::updateUI() { - util::SignalBlockers blockers(this); + for (int i = 0; i < ED_MAX_GOALS; i++) { behaviors[i]->clear(); objects[i]->clear(); subsys[i]->clear(); docks[i]->clear(); - auto value = _model->getBehavior(i); - behaviors[i]->addItem("None", QVariant(int(AI_GOAL_NONE))); - for (int j = 0; j < _model->getGoalsSize(); j++) { - if (_model->getValid(j)) { - behaviors[i]->addItem(_model->getGoalTypes()[j].name, QVariant(int(_model->getGoalTypes()[j].def))); - } + + for (const auto &entry : _model->get_ai_goal_combo_data()) { + behaviors[i]->addItem(entry.first); } - behaviors[i]->setCurrentIndex(behaviors[i]->findData(value)); - auto mode = value; + + auto value = _model->getBehavior(i); + behaviors[i]->setCurrentIndex(value); + + auto mode = _model->get_first_mode_from_combo_box(i); + SCP_vector::iterator ii; if (i >= MAX_AI_GOALS) behaviors[i]->setEnabled(false); @@ -192,6 +192,9 @@ void ShipGoalsDialog::updateUI() priority[i]->setValue(_model->getPriority(i)); break; + + default: + break; } // for goals that deal with ship classes switch (mode) { @@ -205,13 +208,17 @@ void ShipGoalsDialog::updateUI() priority[i]->setEnabled(true); priority[i]->setValue(_model->getPriority(i)); + break; + default: break; } // for goals that deal with individual ships switch (mode) { case AI_GOAL_DESTROY_SUBSYSTEM: - case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: case AI_GOAL_DOCK: - case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: case AI_GOAL_DISABLE_SHIP: case AI_GOAL_DISABLE_SHIP_TACTICAL: case AI_GOAL_DISARM_SHIP: @@ -259,11 +266,15 @@ void ShipGoalsDialog::updateUI() ptr = GET_NEXT(ptr); } break; + default: + break; } // for goals that deal with wings switch (mode) { - case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: - case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: int j; for (j = 0; j < MAX_WINGS; j++) { if (Wings[j].wave_count && j != _model->getWing()) { @@ -274,7 +285,8 @@ void ShipGoalsDialog::updateUI() } priority[i]->setEnabled(true); priority[i]->setValue(_model->getPriority(i)); - + break; + default: break; } if (mode == AI_GOAL_DESTROY_SUBSYSTEM) {