From f98806b03485201e6e4bdff9d583e2bed86725b0 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 5 Oct 2023 00:27:48 -0400 Subject: [PATCH] refactor ai_mission_goal_achievable Change `ai_mission_goal_achievable` to use the ship registry instead of looking up the target repeatedly. --- code/ai/aigoals.cpp | 131 +++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 81 deletions(-) diff --git a/code/ai/aigoals.cpp b/code/ai/aigoals.cpp index 56a5254a104..9906b82526c 100644 --- a/code/ai/aigoals.cpp +++ b/code/ai/aigoals.cpp @@ -976,7 +976,7 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char // this goal needs some extra setup // if this doesn't work, the goal will be immediately removed auto ship_entry = ship_registry_get(aigp->target_name); - if (ship_entry->shipp) + if (ship_entry && ship_entry->shipp) { auto target_aip = &Ai_info[ship_entry->shipp->ai_index]; target_aip->ai_flags.set(AI::AI_Flags::Awaiting_repair); @@ -1530,13 +1530,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) { int status; ai_achievability return_val; - ai_info *aip; - int index = -1, sindex = -1; - int modelnum = -1; auto objp = &Objects[objnum]; Assert( objp->instance != -1 ); - aip = &Ai_info[Ships[objp->instance].ai_index]; + auto shipp = &Ships[objp->instance]; + auto aip = &Ai_info[shipp->ai_index]; // these orders are always achievable. if ( (aigp->ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE) @@ -1547,11 +1545,11 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) if (aigp->ai_mode == AI_GOAL_LUA) return ai_lua_is_achievable(aigp, objnum); + auto target_ship_entry = aigp->target_name == nullptr ? nullptr : ship_registry_get(aigp->target_name); + // warp (depart) only achievable if there's somewhere to depart to if (aigp->ai_mode == AI_GOAL_WARP) { - ship *shipp = &Ships[objp->instance]; - // always valid if has working subspace drive, not disabled, and not limited by subsystem strength if ( ship_can_warp_full_check(shipp) ) return ai_achievability::ACHIEVABLE; @@ -1570,9 +1568,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // if the wing target is valid then be sure to set the override bit so that it always // gets executed next if ( aigp->ai_mode == AI_GOAL_FORM_ON_WING ) { - sindex = ship_name_lookup( aigp->target_name ); - - if (sindex < 0) + if (!target_ship_entry || !target_ship_entry->shipp) return ai_achievability::NOT_ACHIEVABLE; aigp->flags.set(AI::Goal_Flags::Goal_override); @@ -1629,11 +1625,10 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // shipnum could be -1 depending on if the ship hasn't arrived or died. only look for subsystem // destroyed when shipnum is valid - sindex = ship_name_lookup( aigp->target_name ); // can't determine the status of this goal if ship not valid // or we haven't found a valid subsystem index yet - if ( (sindex == -1) || (aigp->flags[AI::Goal_Flags::Subsys_needs_fixup]) ) { + if ( !target_ship_entry || !target_ship_entry->shipp || (aigp->flags[AI::Goal_Flags::Subsys_needs_fixup]) ) { status = 0; break; } @@ -1642,13 +1637,13 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // as 0 so we can continue. (The subsystem name must be turned into an index into the ship's subsystems // for this goal to be valid). Assert ( aigp->ai_submode >= 0 ); - ssp = ship_get_indexed_subsys( &Ships[sindex], aigp->ai_submode ); + ssp = ship_get_indexed_subsys( target_ship_entry->shipp, aigp->ai_submode ); if (ssp != NULL) { // see MWA 3/20/97 comment above - instead of checking the mission log, check the current hits status = (ssp->current_hits <= 0.0f) ? 1 : 0; } else { // not supposed to ever happen, but could if there is a mismatch between the table and model subsystems - nprintf(("AI", "Couldn't find subsystem %d for ship %s\n", aigp->ai_submode, Ships[sindex].ship_name)); + nprintf(("AI", "Couldn't find subsystem %d for ship %s\n", aigp->ai_submode, target_ship_entry->shipp->ship_name)); status = 0; } break; @@ -1658,14 +1653,13 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) { // shipnum could be -1 depending on if the ship hasn't arrived or died. only look for subsystem // destroyed when shipnum is valid - sindex = ship_name_lookup(aigp->target_name); // can't determine the status of this goal if ship not valid - if (sindex < 0) { + if (!target_ship_entry || !target_ship_entry->shipp) { status = 0; } else { // see MWA 3/20/97 comment above - instead of checking the mission log, check the current hits - status = (Ships[sindex].subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f) ? 1 : 0; + status = (target_ship_entry->shipp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f) ? 1 : 0; } break; } @@ -1674,14 +1668,13 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) { // shipnum could be -1 depending on if the ship hasn't arrived or died. only look for subsystem // destroyed when shipnum is valid - sindex = ship_name_lookup(aigp->target_name); // can't determine the status of this goal if ship not valid - if (sindex < 0) { + if (!target_ship_entry || !target_ship_entry->shipp) { status = 0; } else { // see MWA 3/20/97 comment above - instead of checking the mission log, check the current hits - status = (Ships[sindex].subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f) ? 1 : 0; + status = (target_ship_entry->shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f) ? 1 : 0; } break; } @@ -1700,18 +1693,10 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // MWA -- 4/22/98. Check for the ship actually being in the mission before // checking departure and destroyed. In multiplayer, since ships can respawn, // they get log entries for being destroyed even though they have respawned. - sindex = ship_name_lookup( aigp->target_name ); - if ( sindex < 0 ) { - status = mission_log_get_time( LOG_SHIP_DEPARTED, aigp->target_name, NULL, NULL); - if ( !status ) { - status = mission_log_get_time( LOG_SHIP_DESTROYED, aigp->target_name, NULL, NULL); - if ( !status ) { - status = mission_log_get_time( LOG_SELF_DESTRUCTED, aigp->target_name, NULL, NULL); - } - } - - if ( status ) - return_val = ai_achievability::NOT_ACHIEVABLE; + // Goober5000 - update this to use ship registry status with similar logic + if (target_ship_entry && target_ship_entry->status == ShipStatus::EXITED) { + status = 1; + return_val = ai_achievability::NOT_ACHIEVABLE; } else { status = 0; } @@ -1768,12 +1753,12 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // the if statement. if ( (aigp->ai_mode == AI_GOAL_CHASE_WING) || (aigp->ai_mode == AI_GOAL_GUARD_WING) ) { - sindex = wing_name_lookup( aigp->target_name ); + int wingnum = wing_name_lookup( aigp->target_name ); - if (sindex < 0) + if (wingnum < 0) return ai_achievability::NOT_KNOWN; - wing *wingp = &Wings[sindex]; + wing *wingp = &Wings[wingnum]; if ( wingp->flags[Ship::Wing_Flags::Gone] ) return ai_achievability::NOT_ACHIEVABLE; @@ -1790,24 +1775,22 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) } else { - auto ship_entry = ship_registry_get(aigp->target_name); - - if (!ship_entry) + if (!target_ship_entry) { status = SHIP_STATUS_UNKNOWN; } // goal ship is currently in mission - else if (ship_entry->status == ShipStatus::PRESENT || ship_entry->status == ShipStatus::DEATH_ROLL) + else if (target_ship_entry->status == ShipStatus::PRESENT || target_ship_entry->status == ShipStatus::DEATH_ROLL) { status = SHIP_STATUS_ARRIVED; } // goal ship is still on the arrival list - else if (ship_entry->status == ShipStatus::NOT_YET_PRESENT) + else if (target_ship_entry->status == ShipStatus::NOT_YET_PRESENT) { status = SHIP_STATUS_NOT_ARRIVED; } // goal ship has left the area - else if (ship_entry->status == ShipStatus::EXITED) + else if (target_ship_entry->status == ShipStatus::EXITED) { status = SHIP_STATUS_GONE; } @@ -1818,18 +1801,16 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) if (status == SHIP_STATUS_UNKNOWN) { - mprintf(("Potentially incorrect behaviour in AI goal for ship %s: Ship %s could not be found among currently active, departed, or yet-to-arrive ships.\nPlease check the mission file.\n", Ships[objp->instance].ship_name, aigp->target_name)); + mprintf(("Potentially incorrect behaviour in AI goal for ship %s: Ship %s could not be found among currently active, departed, or yet-to-arrive ships.\nPlease check the mission file.\n", shipp->ship_name, aigp->target_name)); } } // Goober5000 - before doing anything else, check if this is a disarm goal for an arrived ship... if ((status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode == AI_GOAL_DISARM_SHIP)) { - sindex = ship_name_lookup(aigp->target_name); - - if ( sindex >= 0 ) { + if (target_ship_entry && target_ship_entry->shipp) { // if the ship has no turrets, we can't disarm it! - if (Ships[sindex].subsys_info[SUBSYSTEM_TURRET].type_count == 0) + if (target_ship_entry->shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0) return ai_achievability::NOT_ACHIEVABLE; } else { UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name); @@ -1856,40 +1837,36 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // this goal will get removed. if ( (aigp->ai_mode == AI_GOAL_DOCK) && (status == SHIP_STATUS_ARRIVED) ) { if (!(aigp->flags[AI::Goal_Flags::Docker_index_valid])) { - modelnum = Ship_info[Ships[objp->instance].ship_info_index].model_num; + int modelnum = Ship_info[shipp->ship_info_index].model_num; Assert( modelnum >= 0 ); - index = model_find_dock_name_index(modelnum, aigp->docker.name); - aigp->docker.index = index; + aigp->docker.index = model_find_dock_name_index(modelnum, aigp->docker.name); aigp->flags.set(AI::Goal_Flags::Docker_index_valid); } if (!(aigp->flags[AI::Goal_Flags::Dockee_index_valid])) { - sindex = ship_name_lookup(aigp->target_name); - if ( sindex != -1 ) { - modelnum = Ship_info[Ships[sindex].ship_info_index].model_num; - index = model_find_dock_name_index(modelnum, aigp->dockee.name); - aigp->dockee.index = index; + if (target_ship_entry && target_ship_entry->shipp) { + int modelnum = Ship_info[target_ship_entry->shipp->ship_info_index].model_num; + aigp->dockee.index = model_find_dock_name_index(modelnum, aigp->dockee.name); aigp->flags.set(AI::Goal_Flags::Dockee_index_valid); } else aigp->dockee.index = -1; // this will force code into if statement below making goal not achievable. } if ( (aigp->dockee.index == -1) || (aigp->docker.index == -1) ) { - Warning(LOCATION, "Cannot determine docking information for %s!", Ships[objp->instance].ship_name); // for now, allender wants to know about these things!!!! + Warning(LOCATION, "Cannot determine docking information for %s!", shipp->ship_name); // for now, allender wants to know about these things!!!! return ai_achievability::NOT_ACHIEVABLE; } // if ship is disabled, don't know if it can dock or not - if ( Ships[objp->instance].flags[Ship::Ship_Flags::Disabled] ) + if ( shipp->flags[Ship::Ship_Flags::Disabled] ) return ai_achievability::NOT_KNOWN; // we must also determine if we're prevented from docking for any reason - sindex = ship_name_lookup(aigp->target_name); - if (sindex < 0) { + if (!target_ship_entry || !target_ship_entry->shipp) { UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name); return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid } - object *goal_objp = &Objects[Ships[sindex].objnum]; + auto goal_objp = target_ship_entry->objp; // if the ship that I am supposed to dock with is docked with something else, then I need to put my goal on hold // [MK, 4/23/98: With Mark, we believe this fixes the problem of Comet refusing to warp out after docking with Omega. @@ -1938,11 +1915,9 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // might happen too, so err on the safe side. (Yay for emergent paragraph justification!) if ((aip->mode == AIM_DOCK) && (aip->submode >= AIS_UNDOCK_0)) { - sindex = ship_name_lookup(aigp->target_name); - - if ( sindex >= 0 ) { + if ( target_ship_entry && target_ship_entry->shipp ) { // only put it on hold if it's someone other than the guy we're undocking from right now!! - if (aip->goal_objnum != Ships[sindex].objnum) + if (aip->goal_objnum != target_ship_entry->shipp->objnum) return ai_achievability::NOT_KNOWN; } else { UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name); @@ -1955,10 +1930,8 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // have fixed up the subsystem name (of the subsystem to destroy) into an index into // the ship's subsystem list if ( aigp->flags[AI::Goal_Flags::Subsys_needs_fixup] ) { - sindex = ship_name_lookup( aigp->target_name ); - - if ( sindex != -1 ) { - aigp->ai_submode = ship_find_subsys( &Ships[sindex], aigp->docker.name ); + if ( target_ship_entry && target_ship_entry->shipp ) { + aigp->ai_submode = ship_find_subsys( target_ship_entry->shipp, aigp->docker.name ); aigp->flags.remove(AI::Goal_Flags::Subsys_needs_fixup); } else { UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name); @@ -1966,15 +1939,12 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) } } } else if ( ((aigp->ai_mode == AI_GOAL_IGNORE) || (aigp->ai_mode == AI_GOAL_IGNORE_NEW)) && (status == SHIP_STATUS_ARRIVED) ) { - object *ignored; - // for ignoring a ship, call the ai_ignore object function, then declare the goal satisfied - sindex = ship_name_lookup( aigp->target_name ); - if (sindex < 0) { + if (!target_ship_entry || !target_ship_entry->shipp) { UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name); return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid } - ignored = &Objects[Ships[sindex].objnum]; + auto ignored = target_ship_entry->objp; ai_ignore_object(objp, ignored, (aigp->ai_mode == AI_GOAL_IGNORE_NEW)); @@ -2007,7 +1977,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) else if ( status == SHIP_STATUS_UNKNOWN ) return ai_achievability::NOT_KNOWN; - UNREACHABLE("Invalid status variable %d for ship %s; get Allender or a SCP member", status, Ships[objp->instance].ship_name); // get allender -- bad logic + UNREACHABLE("Invalid status variable %d for ship %s; get Allender or a SCP member", status, shipp->ship_name); // get allender -- bad logic break; } @@ -2019,30 +1989,29 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // short circuit a couple of cases. Ship not arrived shouldn't happen. Ship gone means // we mark the goal as not achievable. if ( status == SHIP_STATUS_NOT_ARRIVED ) { - UNREACHABLE("Ship %s cannot rearm a target %s that hasn't arrived; get Allender or a SCP member", Ships[objp->instance].ship_name, aigp->target_name); // get Allender. this shouldn't happen!!! + UNREACHABLE("Ship %s cannot rearm a target %s that hasn't arrived; get Allender or a SCP member", shipp->ship_name, aigp->target_name); // get Allender. this shouldn't happen!!! return ai_achievability::NOT_ACHIEVABLE; } if ( status == SHIP_STATUS_GONE ) return ai_achievability::NOT_ACHIEVABLE; - sindex = ship_name_lookup( aigp->target_name ); - if ( sindex < 0 ) { + if ( !target_ship_entry || !target_ship_entry->shipp ) { UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name); return ai_achievability::NOT_ACHIEVABLE; } - // if desitnation currently being repaired, then goal is stil active - if ( Ai_info[Ships[sindex].ai_index].ai_flags[AI::AI_Flags::Being_repaired] ) + // if destination currently being repaired, then goal is still active + if ( Ai_info[target_ship_entry->shipp->ai_index].ai_flags[AI::AI_Flags::Being_repaired] ) return ai_achievability::ACHIEVABLE; // if the destination ship is dying or departing (but not completed yet), the mark goal as // not achievable. - if ( Ships[sindex].is_dying_or_departing()) + if ( target_ship_entry->shipp->is_dying_or_departing()) return ai_achievability::NOT_ACHIEVABLE; // if the destination object is no longer awaiting repair, then remove the item - if ( !(Ai_info[Ships[sindex].ai_index].ai_flags[AI::AI_Flags::Awaiting_repair]) ) + if ( !(Ai_info[target_ship_entry->shipp->ai_index].ai_flags[AI::AI_Flags::Awaiting_repair]) ) return ai_achievability::NOT_ACHIEVABLE; // not repairing anything means that he can do this goal!!! @@ -2057,7 +2026,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp ) // if he is repairing something, he can satisfy his repair goal (his goal_objnum) // return GOAL_NOT_KNOWN which is kind of a hack which puts the goal on hold until it can be // satisfied. - if ( aip->goal_objnum != Ships[sindex].objnum ) + if ( aip->goal_objnum != target_ship_entry->shipp->objnum ) return ai_achievability::NOT_KNOWN; return ai_achievability::ACHIEVABLE;