mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Follow-up to #7635. Pass the firing bank explicitly through weapon_create() and beam_fire() instead of searching the ship's banks by weapon class, which could attribute substitution patterns to the wrong bank. Factor the substitution logic into get_weapon_substitution_tuple() and only apply linked/dual-fire pairing to ship-level fire, not turret fire. Skip substitution on multiplayer clients, since the server already resolved it. Derive turret bank indices from the weapon slot being fired rather than the turret's bank cursor, so ammo and substitution state are tracked on the bank that actually fired. Previously a single-selection turret could consume ammo from a bank it never fired, and non-ammo turrets (whose cursors stay at -1) would have skipped substitution entirely. Use the Player_ship object flag rather than Player_obj in ship_secondary_bank_can_dual_fire() so the server and clients agree on dual fire for remote players, and guard ship_secondary_has_ammo() against a negative bank index. Co-Authored-By: Claude Fable 5.1 <[email protected]>
555 lines
17 KiB
C++
555 lines
17 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "globalincs/linklist.h"
|
|
#include "io/timer.h"
|
|
#include "object/object.h"
|
|
#include "ship/ship.h"
|
|
#include "weapon/swarm.h"
|
|
#include "weapon/weapon.h"
|
|
|
|
|
|
|
|
#define SWARM_DIST_OFFSET 2.0f // distance swarm missile should vary from original path
|
|
#define SWARM_CONE_LENGTH 10000.0f // used to pick a target point far in the distance
|
|
#define SWARM_CHANGE_DIR_TIME 400 // time to force change in direction of swarm missile
|
|
#define SWARM_ANGLE_CHANGE (4*PI/180) // in rad
|
|
|
|
|
|
// *No longer need this -Et1
|
|
//#define SWARM_MISSILE_DELAY 150 // time delay between each swarm missile that is fired
|
|
|
|
|
|
#define SWARM_TIME_VARIANCE 100 // max time variance when deciding when to change swarm missile course
|
|
|
|
#define SWARM_FRAME_STOP_SWARMING 4 // Estimated number of zigzag frames BEFORE impact to stop zig-zagging and instead home in straight to the target
|
|
|
|
#define TURRET_SWARM_VALIDITY_CHECKTIME 5000 // number of ms between checks on turret_swam_info checks
|
|
|
|
#define SWARM_USED (1<<0)
|
|
|
|
|
|
turret_swarm_info Turret_swarm_info[MAX_TURRET_SWARM_INFO];
|
|
|
|
int Turret_swarm_validity_next_check_time;
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_level_init()
|
|
//
|
|
// Called at the start of each new mission
|
|
//
|
|
void swarm_level_init()
|
|
{
|
|
int i;
|
|
turret_swarm_info *tswarmp;
|
|
|
|
for (i=0; i<MAX_TURRET_SWARM_INFO; i++) {
|
|
tswarmp = &Turret_swarm_info[i];
|
|
tswarmp->flags = 0;
|
|
tswarmp->weapon_class = -1;
|
|
tswarmp->num_to_launch = 0;
|
|
tswarmp->parent_objnum = -1;
|
|
tswarmp->parent_sig = -1;
|
|
tswarmp->target_objnum = -1;
|
|
tswarmp->target_sig = -1;
|
|
tswarmp->turret = NULL;
|
|
tswarmp->target_subsys = NULL;
|
|
tswarmp->time_to_fire = 0;
|
|
tswarmp->swp_pbank = -1;
|
|
tswarmp->swp_sbank = -1;
|
|
}
|
|
|
|
Turret_swarm_validity_next_check_time = timestamp(TURRET_SWARM_VALIDITY_CHECKTIME);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_maybe_fire_missile()
|
|
//
|
|
// Check if there are any swarm missiles to fire, and if enough time
|
|
// has elapsed since last one fired, go ahead and fire it.
|
|
//
|
|
// This is called once per ship frame in ship_move()
|
|
//
|
|
void swarm_maybe_fire_missile(int shipnum)
|
|
{
|
|
ship *sp;
|
|
ship_weapon *swp;
|
|
int weapon_info_index;
|
|
|
|
Assert(shipnum >= 0 && shipnum < MAX_SHIPS );
|
|
sp = &Ships[shipnum];
|
|
|
|
if ( sp->num_swarm_missiles_to_fire <= 0 ) {
|
|
sp->swarm_missile_bank = -1;
|
|
return;
|
|
}
|
|
|
|
swp = &sp->weapons;
|
|
if ( sp->swarm_missile_bank == -1 ) {
|
|
sp->num_swarm_missiles_to_fire = 0;
|
|
return;
|
|
}
|
|
|
|
weapon_info_index = swp->secondary_bank_weapons[sp->swarm_missile_bank];
|
|
Assert( weapon_info_index >= 0 && weapon_info_index < weapon_info_size() );
|
|
|
|
// if swarm secondary bank is not a swarm missile, return
|
|
if ( !(Weapon_info[weapon_info_index].wi_flags[Weapon::Info_Flags::Swarm]) ) {
|
|
sp->num_swarm_missiles_to_fire = 0;
|
|
sp->swarm_missile_bank = -1;
|
|
return;
|
|
}
|
|
|
|
if ( timestamp_elapsed(sp->next_swarm_fire) )
|
|
{
|
|
|
|
// *Get timestamp from weapon info's -Et1
|
|
sp->next_swarm_fire = timestamp(Weapon_info[weapon_info_index].SwarmWait );
|
|
|
|
ship_fire_secondary( &Objects[sp->objnum], 1 );
|
|
sp->num_swarm_missiles_to_fire--;
|
|
}
|
|
}
|
|
|
|
// if it can't home on something, just set target dead ahead
|
|
void swarm_set_default_target_pos(object* objp, swarm_info* swarmp) {
|
|
vm_vec_scale_add(&swarmp->current_target, &objp->pos, &objp->orient.vec.fvec, SWARM_CONE_LENGTH);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_create()
|
|
//
|
|
// Set up all the basic info for a swarm missile
|
|
//
|
|
void swarm_create(object* objp, swarm_info* swarmp)
|
|
{
|
|
swarmp->change_timestamp = 1;
|
|
swarmp->homing_objnum = -1;
|
|
swarmp->zig_direction = golden_ratio_rand() * PI2;
|
|
|
|
vm_vec_zero(&swarmp->offset);
|
|
|
|
swarm_set_default_target_pos(objp, swarmp);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_update_direction()
|
|
//
|
|
// Check if we want to update the direction of a swarm missile.
|
|
//
|
|
void swarm_update_direction(object *objp, swarm_info* swarmp)
|
|
{
|
|
weapon_info *wip;
|
|
weapon *wp;
|
|
object *hobjp;
|
|
vec3d obj_to_target; // Vector pointing from the swarm missile to its target
|
|
float vel, target_dist, radius;
|
|
physics_info *pi;
|
|
|
|
Assert(objp->instance >= 0 && objp->instance < MAX_WEAPONS);
|
|
|
|
wp = &Weapons[objp->instance];
|
|
|
|
wip = &Weapon_info[wp->weapon_info_index];
|
|
hobjp = wp->homing_object;
|
|
pi = &Objects[wp->objnum].phys_info;
|
|
|
|
// check if homing is lost.. if it is then set the target point dead ahead
|
|
if ( swarmp->homing_objnum != -1 && hobjp == &obj_used_list ) {
|
|
swarmp->change_timestamp = 1;
|
|
swarmp->homing_objnum = -1;
|
|
swarm_set_default_target_pos(objp, swarmp);
|
|
}
|
|
|
|
if ( hobjp != &obj_used_list ) {
|
|
swarmp->homing_objnum = OBJ_INDEX(hobjp);
|
|
}
|
|
|
|
if ( timestamp_elapsed(swarmp->change_timestamp) ) {
|
|
|
|
// Time to (maybe) zig-zag!
|
|
int zig_zag_time = fl2i(SWARM_CHANGE_DIR_TIME + SWARM_TIME_VARIANCE * (frand() - 0.5f) * 2);
|
|
swarmp->change_timestamp = timestamp(zig_zag_time);
|
|
|
|
float missile_age = f2fl(Missiontime - wp->creation_time);
|
|
|
|
if (hobjp != &obj_used_list && missile_age > 0.5f && missile_age > wip->free_flight_time)
|
|
{
|
|
// This is a copy of the relevant bits near the end of weapon_home() to make missiles lead their targets
|
|
// note that if the swarm missile has a target_lead_scaler of 0 this is equivalent to retail behavior (no leading)
|
|
if (Swarmers_lead_targets) {
|
|
vec3d target_pos = wp->homing_pos;
|
|
vec3d vec_to_goal;
|
|
float dist_to_target = vm_vec_normalized_dir(&vec_to_goal, &target_pos, &objp->pos);
|
|
float time_to_target = dist_to_target / wip->max_speed;
|
|
|
|
vec3d tvec;
|
|
tvec = objp->phys_info.vel;
|
|
vm_vec_normalize(&tvec);
|
|
|
|
float old_dot = vm_vec_dot(&tvec, &vec_to_goal);
|
|
|
|
if ((old_dot > 0.1f) && (time_to_target > 0.1f)) {
|
|
if (wip->wi_flags[Weapon::Info_Flags::Variable_lead_homing]) {
|
|
target_pos += hobjp->phys_info.vel * (0.33f * wip->target_lead_scaler * MIN(time_to_target, 6.0f));
|
|
}
|
|
else if (wip->is_locked_homing()) {
|
|
target_pos += hobjp->phys_info.vel * MIN(time_to_target, 2.0f);
|
|
}
|
|
}
|
|
swarmp->current_target = target_pos;
|
|
} else { // Else, retail behavior (usually simply the target's position)
|
|
swarmp->current_target = wp->homing_pos;
|
|
}
|
|
}
|
|
|
|
obj_to_target = swarmp->current_target - objp->pos;
|
|
target_dist = vm_vec_mag_quick(&obj_to_target);
|
|
|
|
// If homing swarm missile is close to target, let missile home in on original target
|
|
if ((target_dist / pi->speed) <= ((zig_zag_time / 1000.0f) * SWARM_FRAME_STOP_SWARMING)) {
|
|
vm_vec_zero(&swarmp->offset);
|
|
} else {
|
|
|
|
// calculate a radius around our target such that it would be the same angular offset
|
|
// as SWARM_DIST_OFFSET (2 meters) at our next zig distance
|
|
|
|
float missile_dist; // straight-line distance the missile will travel between now and next check
|
|
float missile_speed; // current speed of the missile
|
|
|
|
missile_speed = pi->speed;
|
|
missile_dist = missile_speed * zig_zag_time / 1000.0f;
|
|
if (missile_dist < SWARM_DIST_OFFSET)
|
|
missile_dist = SWARM_DIST_OFFSET;
|
|
float angle_offset = asinf_safe(SWARM_DIST_OFFSET / missile_dist);
|
|
Assert(!fl_is_nan(angle_offset));
|
|
|
|
// Radius around the target pos. Shortens as the missiles get closer to the target.
|
|
radius = tanf(angle_offset) * target_dist;
|
|
|
|
// maybe zig zag to a different angle
|
|
int zigs_zagged = (int)(missile_age * (1 / (SWARM_CHANGE_DIR_TIME / 1000.0f)));
|
|
int mod_signature = (Game_mode & GM_MULTIPLAYER ? objp->net_signature : objp->signature) % 3;
|
|
// depending on the signature, switch every 3, 4, or 5 zig zags
|
|
if (zigs_zagged % (mod_signature + 3)) {
|
|
// angle change!
|
|
swarmp->zig_direction = golden_ratio_rand() * PI2;
|
|
} else // otherwise go 180 degrees off of what you were before
|
|
swarmp->zig_direction += PI;
|
|
|
|
// then rotate by it
|
|
swarmp->offset = objp->orient.vec.uvec * radius;
|
|
vm_rot_point_around_line(&swarmp->offset, &swarmp->offset, swarmp->zig_direction, &vmd_zero_vector, &objp->orient.vec.fvec);
|
|
}
|
|
}
|
|
|
|
vec3d actual_target = swarmp->current_target + swarmp->offset;
|
|
ai_turn_towards_vector(&actual_target, objp, nullptr, nullptr, 0.0f, 0);
|
|
vel = vm_vec_mag(&objp->phys_info.desired_vel);
|
|
vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.vec.fvec, vel);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// turret_swarm_create()
|
|
//
|
|
// Get a free swarm missile entry, and initialize the struct members
|
|
//
|
|
int turret_swarm_create()
|
|
{
|
|
int i;
|
|
turret_swarm_info *tswarmp = NULL;
|
|
|
|
for (i=0; i<MAX_TURRET_SWARM_INFO; i++) {
|
|
tswarmp = &Turret_swarm_info[i];
|
|
if ( !(tswarmp->flags & SWARM_USED) )
|
|
break;
|
|
}
|
|
|
|
if ( i >= MAX_TURRET_SWARM_INFO ) {
|
|
nprintf(("Warning","No more turret swarm info slots are available\n"));
|
|
return -1;
|
|
}
|
|
|
|
tswarmp->flags = 0;
|
|
tswarmp->weapon_class = -1;
|
|
tswarmp->num_to_launch = 0;
|
|
tswarmp->parent_objnum = -1;
|
|
tswarmp->parent_sig = -1;
|
|
tswarmp->target_objnum = -1;
|
|
tswarmp->target_sig = -1;
|
|
tswarmp->turret = NULL;
|
|
tswarmp->target_subsys = NULL;
|
|
tswarmp->time_to_fire = 0;
|
|
tswarmp->swp_pbank = -1;
|
|
tswarmp->swp_sbank = -1;
|
|
|
|
tswarmp->flags |= SWARM_USED;
|
|
return i;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// turret_swarm_delete()
|
|
//
|
|
void turret_swarm_delete(int i)
|
|
{
|
|
turret_swarm_info *tswarmp;
|
|
|
|
Assert(i >= 0 && i < MAX_TURRET_SWARM_INFO);
|
|
tswarmp = &Turret_swarm_info[i];
|
|
|
|
if ( !(tswarmp->flags & SWARM_USED) ) {
|
|
Int3(); // tried to delete a swarm missile that didn't exist, get DaveA
|
|
}
|
|
|
|
tswarmp->flags = 0;
|
|
}
|
|
|
|
// Set up turret swarm info struct
|
|
void turret_swarm_set_up_info(int parent_objnum, ship_subsys *turret, const weapon_info *wip, int weapon_num, int swp_pbank, int swp_sbank, bool no_tracking_object)
|
|
{
|
|
turret_swarm_info *tsi;
|
|
object *parent_obj;
|
|
ship *shipp;
|
|
int tsi_index;
|
|
|
|
// weapon info pointer
|
|
//Removed check in the interests of speed -WMC
|
|
/*
|
|
Assert((turret_weapon_class >= 0) && (turret_weapon_class < weapon_info_size()));
|
|
if((turret_weapon_class < 0) || (turret_weapon_class >= weapon_info_size())){
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// get ship pointer
|
|
Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
|
|
if((parent_objnum < 0) || (parent_objnum >= MAX_OBJECTS)){
|
|
return;
|
|
}
|
|
parent_obj = &Objects[parent_objnum];
|
|
Assert(parent_obj->type == OBJ_SHIP);
|
|
shipp = &Ships[parent_obj->instance];
|
|
Assert(turret->turret_enemy_objnum < MAX_OBJECTS);
|
|
if (turret->turret_enemy_objnum < 0 && !no_tracking_object)
|
|
return;
|
|
if (turret->turret_enemy_objnum >= MAX_OBJECTS)
|
|
return;
|
|
|
|
// valid swarm weapon
|
|
Assert(((wip->wi_flags[Weapon::Info_Flags::Swarm]) && (wip->swarm_count > 0)) || ((wip->wi_flags[Weapon::Info_Flags::Corkscrew]) && (wip->cs_num_fired > 0)));
|
|
|
|
if(!((wip->wi_flags[Weapon::Info_Flags::Swarm]) || (wip->wi_flags[Weapon::Info_Flags::Corkscrew])) || ((wip->wi_flags[Weapon::Info_Flags::Swarm]) && (wip->swarm_count <= 0)) || ((wip->wi_flags[Weapon::Info_Flags::Corkscrew]) && (wip->cs_num_fired <= 0)))
|
|
return;
|
|
|
|
// get turret_swarm_info
|
|
tsi_index = turret_swarm_create();
|
|
if (tsi_index == -1) {
|
|
return;
|
|
}
|
|
|
|
// set turret to point to tsi
|
|
tsi = &Turret_swarm_info[tsi_index];
|
|
|
|
if (turret->turret_swarm_num == MAX_TFP)
|
|
{
|
|
mprintf(("Overlapping turret swarm firing intervals\n"));
|
|
turret_swarm_delete(turret->turret_swarm_info_index[0]);
|
|
int old_s;
|
|
for (old_s = 0; old_s < (MAX_TFP - 1); old_s++)
|
|
{
|
|
turret->turret_swarm_info_index[old_s] = turret->turret_swarm_info_index[old_s + 1];
|
|
}
|
|
turret->turret_swarm_info_index[MAX_TFP - 1] = -1;
|
|
turret->turret_swarm_num--;
|
|
shipp->num_turret_swarm_info--;
|
|
}
|
|
turret->turret_swarm_info_index[turret->turret_swarm_num] = tsi_index;
|
|
turret->turret_swarm_num++;
|
|
|
|
// increment ship tsi counter
|
|
shipp->num_turret_swarm_info++;
|
|
|
|
// *Unnecessary check, now done on startup -Et1
|
|
/*
|
|
|
|
// make sure time is sufficient to launch all the missiles before next volley
|
|
#ifndef NDEBUG
|
|
Assert(wip->swarm_count * SWARM_MISSILE_DELAY < wip->fire_wait * 1000.0f);
|
|
#endif
|
|
|
|
*/
|
|
|
|
ship_weapon *swp = &turret->weapons;
|
|
int bank_fired = swp->current_secondary_bank;
|
|
|
|
// initialize tsi
|
|
tsi->weapon_class = WEAPON_INFO_INDEX(wip);
|
|
if (wip->wi_flags[Weapon::Info_Flags::Swarm]) {
|
|
tsi->num_to_launch = wip->swarm_count;
|
|
} else {
|
|
tsi->num_to_launch = wip->cs_num_fired;
|
|
}
|
|
if (turret->system_info->flags[Model::Subsystem_Flags::Turret_use_ammo] && !Weapon_info[swp->secondary_bank_weapons[bank_fired]].wi_flags[Weapon::Info_Flags::SecondaryNoAmmo]) {
|
|
swp->secondary_bank_ammo[bank_fired] -= tsi->num_to_launch;
|
|
}
|
|
tsi->parent_objnum = parent_objnum;
|
|
tsi->parent_sig = parent_obj->signature;
|
|
tsi->target_objnum = turret->turret_enemy_objnum;
|
|
tsi->target_sig = turret->turret_enemy_objnum >= 0 ? Objects[turret->turret_enemy_objnum].signature : 0;
|
|
tsi->turret = turret;
|
|
tsi->target_subsys = turret->targeted_subsys;
|
|
tsi->time_to_fire = 1; // first missile next frame
|
|
tsi->weapon_num = weapon_num;
|
|
tsi->swp_pbank = swp_pbank;
|
|
tsi->swp_sbank = swp_sbank;
|
|
}
|
|
|
|
void turret_swarm_fire_from_turret(turret_swarm_info *tsi);
|
|
// check if ship has turret ready to fire swarm type missiles
|
|
void turret_swarm_maybe_fire_missile(int shipnum)
|
|
{
|
|
ship *shipp = &Ships[shipnum];
|
|
ship_subsys *subsys;
|
|
turret_swarm_info *tsi;
|
|
object *parent_obj, *target_obj;
|
|
int num_turret_swarm_turrets_left;
|
|
int k, j;
|
|
weapon_info *wip;
|
|
|
|
// check if ship has any turrets ready to fire
|
|
if (shipp->num_turret_swarm_info <= 0) {
|
|
Assert(shipp->num_turret_swarm_info == 0);
|
|
return;
|
|
}
|
|
|
|
// ship obj which has fired turret swarm missiles
|
|
parent_obj = &Objects[shipp->objnum];
|
|
num_turret_swarm_turrets_left = shipp->num_turret_swarm_info;
|
|
|
|
// search ship subsystems for turrets with valid turret_swarm_info_index
|
|
for (subsys = GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys)) {
|
|
if (subsys->turret_swarm_num > 0) {
|
|
|
|
int swarms_per_turret = subsys->turret_swarm_num;
|
|
|
|
for (k = 0; k < swarms_per_turret; k++)
|
|
{
|
|
int turret_tsi = subsys->turret_swarm_info_index[k];
|
|
num_turret_swarm_turrets_left--;
|
|
Assert(num_turret_swarm_turrets_left >= 0);
|
|
|
|
// get turret_swarm_info
|
|
Assert( (turret_tsi >= 0) && (turret_tsi < MAX_TURRET_SWARM_INFO) );
|
|
tsi = &Turret_swarm_info[turret_tsi];
|
|
wip = &Weapon_info[tsi->weapon_class];
|
|
|
|
// check if parent ship is valid (via signature)
|
|
if ( tsi->parent_sig == parent_obj->signature ) {
|
|
|
|
// make sure we have the right turret.
|
|
Assert(tsi->turret == subsys);
|
|
|
|
// check if time to fire
|
|
if (timestamp_elapsed(tsi->time_to_fire)) {
|
|
Assert(tsi->num_to_launch > 0);
|
|
|
|
// check target still alive
|
|
if (tsi->target_objnum > -1) {
|
|
target_obj= &Objects[tsi->target_objnum];
|
|
|
|
if (target_obj->signature != tsi->target_sig) {
|
|
// poor target, it died
|
|
tsi->target_objnum = -1;
|
|
}
|
|
}
|
|
|
|
// make sure turret is still alive and fire swarmer
|
|
if (subsys->current_hits > 0) {
|
|
turret_swarm_fire_from_turret(tsi);
|
|
}
|
|
|
|
// *Get timestamp from weapon info's -Et1
|
|
if (wip->wi_flags[Weapon::Info_Flags::Swarm]) {
|
|
tsi->time_to_fire = timestamp( wip->SwarmWait );
|
|
} else {
|
|
tsi->time_to_fire = timestamp( wip->cs_delay );
|
|
}
|
|
|
|
// do book keeping
|
|
tsi->num_to_launch--;
|
|
|
|
if (tsi->num_to_launch == 0) {
|
|
// we are done firing, so see about resetting any animation timestamps for reversal (closing)...
|
|
// (I figure that a good estimate is to trigger a close after three additional swarms had fired - taylor)
|
|
if (subsys->turret_animation_position == MA_POS_READY)
|
|
subsys->turret_animation_done_time = timestamp( Weapon_info[tsi->weapon_class].SwarmWait * 3);
|
|
|
|
shipp->num_turret_swarm_info--;
|
|
subsys->turret_swarm_num--;
|
|
turret_swarm_delete(subsys->turret_swarm_info_index[k]);
|
|
subsys->turret_swarm_info_index[k] = -1;
|
|
}
|
|
}
|
|
} else {
|
|
Warning(LOCATION, "Found turret swarm info on ship: %s with turret: %s, but signature does not match.", shipp->ship_name, subsys->system_info->subobj_name);
|
|
shipp->num_turret_swarm_info--;
|
|
subsys->turret_swarm_num--;
|
|
turret_swarm_delete(subsys->turret_swarm_info_index[k]);
|
|
subsys->turret_swarm_info_index[k] = -1;
|
|
}
|
|
}
|
|
//swarm reset stuff
|
|
for (k = 0; k < (MAX_TFP - 1); k++)
|
|
{
|
|
for (j = (k + 1); j < MAX_TFP; j++)
|
|
{
|
|
if ((subsys->turret_swarm_info_index[k] == -1) && (subsys->turret_swarm_info_index[j] != -1))
|
|
{
|
|
subsys->turret_swarm_info_index[k] = subsys->turret_swarm_info_index[j];
|
|
subsys->turret_swarm_info_index[j] = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Assert(num_turret_swarm_turrets_left == 0);
|
|
}
|
|
|
|
// check Turret_swarm_info for info that are invalid - ie, ships died while firing.
|
|
void turret_swarm_check_validity()
|
|
{
|
|
int i;
|
|
turret_swarm_info *tswarmp;
|
|
object *ship_objp;
|
|
|
|
if (timestamp_elapsed(Turret_swarm_validity_next_check_time)) {
|
|
|
|
// reset timestamp
|
|
Turret_swarm_validity_next_check_time = timestamp(TURRET_SWARM_VALIDITY_CHECKTIME);
|
|
|
|
// go through all Turret_swarm_info, check obj and obj->signature
|
|
for (i=0; i<MAX_TURRET_SWARM_INFO; i++) {
|
|
tswarmp = &Turret_swarm_info[i];
|
|
|
|
if (tswarmp->flags & SWARM_USED) {
|
|
ship_objp = &Objects[tswarmp->parent_objnum];
|
|
if (ship_objp->type == OBJ_SHIP) {
|
|
if (ship_objp->signature == tswarmp->parent_sig) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
turret_swarm_delete(i);
|
|
}
|
|
}
|
|
}
|
|
}
|