mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 15:29:48 -10:00
693 lines
21 KiB
C++
693 lines
21 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)
|
|
#define SWARM_POSITIVE_PATH (1<<1)
|
|
|
|
|
|
swarm_info Swarm_missiles[MAX_SWARM_MISSILES];
|
|
|
|
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;
|
|
swarm_info *swarmp;
|
|
turret_swarm_info *tswarmp;
|
|
|
|
for ( i = 0; i < MAX_SWARM_MISSILES; i++ ) {
|
|
swarmp = &Swarm_missiles[i];
|
|
swarmp->flags = 0;
|
|
swarmp->change_timestamp = 1;
|
|
swarmp->path_num = -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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--;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_create()
|
|
//
|
|
// Get a free swarm missile entry, and initialize the struct members
|
|
//
|
|
int swarm_create()
|
|
{
|
|
int i;
|
|
swarm_info *swarmp = NULL;
|
|
|
|
for ( i = 0; i < MAX_SWARM_MISSILES; i++ ) {
|
|
swarmp = &Swarm_missiles[i];
|
|
if ( !(swarmp->flags & SWARM_USED) )
|
|
break;
|
|
}
|
|
|
|
if ( i >= MAX_SWARM_MISSILES ) {
|
|
nprintf(("Warning","No more swarm missiles are available\n"));
|
|
return -1;
|
|
}
|
|
|
|
swarmp->flags = 0;
|
|
swarmp->change_timestamp = 1;
|
|
swarmp->path_num = -1;
|
|
swarmp->homing_objnum = -1;
|
|
|
|
swarmp->flags |= SWARM_USED;
|
|
return i;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_delete()
|
|
//
|
|
//
|
|
void swarm_delete(int i)
|
|
{
|
|
swarm_info *swarmp;
|
|
|
|
Assert(i >= 0 && i < MAX_SWARM_MISSILES);
|
|
swarmp = &Swarm_missiles[i];
|
|
|
|
if ( !(swarmp->flags & SWARM_USED) ) {
|
|
Int3(); // tried to delete a swarm missile that didn't exist, get Alan
|
|
}
|
|
|
|
swarmp->flags = 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// swarm_update_direction()
|
|
//
|
|
// Check if we want to update the direction of a swarm missile.
|
|
//
|
|
void swarm_update_direction(object *objp)
|
|
{
|
|
weapon_info *wip;
|
|
weapon *wp;
|
|
object *hobjp;
|
|
swarm_info *swarmp;
|
|
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];
|
|
|
|
if (wp->swarm_index == -1) {
|
|
return;
|
|
}
|
|
|
|
wip = &Weapon_info[wp->weapon_info_index];
|
|
hobjp = wp->homing_object;
|
|
pi = &Objects[wp->objnum].phys_info;
|
|
swarmp = &Swarm_missiles[wp->swarm_index];
|
|
|
|
// check if homing is lost.. if it is then get a new path to move swarm missile along
|
|
if ( swarmp->homing_objnum != -1 && hobjp == &obj_used_list ) {
|
|
swarmp->change_timestamp = 1;
|
|
swarmp->path_num = -1;
|
|
swarmp->homing_objnum = -1;
|
|
}
|
|
|
|
if ( hobjp != &obj_used_list ) {
|
|
swarmp->homing_objnum = OBJ_INDEX(hobjp);
|
|
}
|
|
|
|
if ( timestamp_elapsed(swarmp->change_timestamp) ) {
|
|
// Time to (maybe) zig-zag!
|
|
swarmp->change_timestamp = timestamp(swarmp->change_time);
|
|
|
|
if (swarmp->path_num == -1) {
|
|
// Swarm missile was just created, or just lost homing
|
|
if (Objects[objp->parent].type != OBJ_SHIP) {
|
|
//AL: parent ship died... so just pick some random paths
|
|
swarmp->path_num = myrand() % 4;
|
|
|
|
} else {
|
|
// Parent ship is alive, try to get a new path from them
|
|
ship *parent_shipp;
|
|
parent_shipp = &Ships[Objects[objp->parent].instance];
|
|
swarmp->path_num = (parent_shipp->next_swarm_path++) % 4;
|
|
|
|
if (parent_shipp->next_swarm_path % 4 == 0) {
|
|
swarmp->flags ^= SWARM_POSITIVE_PATH;
|
|
}
|
|
}
|
|
|
|
// Set the change time. This is here because it's common to both swarm_create() and turret_swarm_create()
|
|
swarmp->change_time = fl2i(SWARM_CHANGE_DIR_TIME + SWARM_TIME_VARIANCE*(frand() - 0.5f) * 2);
|
|
|
|
// Set target to straight ahead
|
|
vm_vec_scale_add(&swarmp->original_target, &objp->pos, &objp->orient.vec.fvec, SWARM_CONE_LENGTH);
|
|
|
|
// Reset zigzag index
|
|
swarmp->change_count = 1;
|
|
|
|
vm_vec_zero(&swarmp->last_offset);
|
|
|
|
} else if (hobjp != &obj_used_list &&
|
|
f2fl(Missiontime - wp->creation_time) > 0.5f &&
|
|
f2fl(Missiontime - wp->creation_time) > 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]) {
|
|
vm_vec_scale_add2(&target_pos, &hobjp->phys_info.vel, (0.33f * wip->target_lead_scaler * MIN(time_to_target, 6.0f)));
|
|
}
|
|
else if (wip->is_locked_homing()) {
|
|
vm_vec_scale_add2(&target_pos, &hobjp->phys_info.vel, MIN(time_to_target, 2.0f));
|
|
}
|
|
}
|
|
swarmp->original_target = target_pos;
|
|
}
|
|
else { // Else, retail behavior (usually simply the target's position)
|
|
swarmp->original_target = wp->homing_pos;
|
|
}
|
|
}
|
|
|
|
// Calculate a rvec and uvec that will determine the displacement from the
|
|
// intended target. Use crossprod to generate a right vector, from the missile
|
|
// up vector and the vector connecting missile to the homing object.
|
|
// Faster missiles have a tighter grouping than slower missiles, They should expand at sharp turns
|
|
{
|
|
float missile_dist; // straight-line distance the missile will travel between now and next check
|
|
float missile_speed; // current speed of the missile
|
|
|
|
swarmp->circle_uvec = objp->orient.vec.uvec;
|
|
swarmp->circle_rvec = objp->orient.vec.rvec;
|
|
|
|
missile_speed = pi->speed;
|
|
missile_dist = missile_speed * swarmp->change_time / 1000.0f;
|
|
if (missile_dist < SWARM_DIST_OFFSET) {
|
|
missile_dist = SWARM_DIST_OFFSET;
|
|
}
|
|
swarmp->angle_offset = asinf(SWARM_DIST_OFFSET / missile_dist);
|
|
Assert(!fl_is_nan(swarmp->angle_offset));
|
|
}
|
|
|
|
vm_vec_sub(&obj_to_target, &swarmp->original_target, &objp->pos);
|
|
target_dist = vm_vec_mag_quick(&obj_to_target);
|
|
swarmp->last_dist = target_dist;
|
|
|
|
// Radius around the target pos. Shortens as the missiles get closer to the target.
|
|
radius = tanf(swarmp->angle_offset) * target_dist;
|
|
|
|
// If homing swarm missile is close to target, let missile home in on original target
|
|
if ((target_dist / pi->speed) <= ((swarmp->change_time / 1000.0f) * SWARM_FRAME_STOP_SWARMING)) {
|
|
swarmp->new_target = swarmp->original_target;
|
|
vm_vec_zero(&swarmp->last_offset);
|
|
goto swarm_new_target_calced;
|
|
}
|
|
|
|
vec3d rvec_component, uvec_component;
|
|
vm_vec_zero(&rvec_component);
|
|
vm_vec_zero(&uvec_component);
|
|
|
|
swarmp->change_count++;
|
|
if ( swarmp->change_count > 2 ) {
|
|
swarmp->flags ^= SWARM_POSITIVE_PATH;
|
|
swarmp->change_count = 0;
|
|
}
|
|
|
|
// pick a new path number to follow once at center
|
|
if ( swarmp->change_count == 1 ) {
|
|
swarmp->path_num = swarmp->path_num + myrand()%3;
|
|
if ( swarmp->path_num > 3 ) {
|
|
swarmp->path_num = 0;
|
|
}
|
|
}
|
|
|
|
switch ( swarmp->path_num ) {
|
|
case 0: // straight up and down
|
|
if ( swarmp->flags & SWARM_POSITIVE_PATH )
|
|
vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius);
|
|
else
|
|
vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius);
|
|
break;
|
|
|
|
case 1: // left/right
|
|
if ( swarmp->flags & SWARM_POSITIVE_PATH )
|
|
vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius);
|
|
else
|
|
vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius);
|
|
break;
|
|
|
|
case 2: // top/right - bottom/left
|
|
if ( swarmp->flags & SWARM_POSITIVE_PATH ) {
|
|
vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius);
|
|
vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius);
|
|
}
|
|
else {
|
|
vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius);
|
|
vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius);
|
|
}
|
|
break;
|
|
|
|
case 3: // top-left - bottom/right
|
|
if ( swarmp->flags & SWARM_POSITIVE_PATH ) {
|
|
vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius);
|
|
vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius);
|
|
}
|
|
else {
|
|
vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius);
|
|
vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
|
|
swarmp->new_target = swarmp->original_target;
|
|
vm_vec_zero(&swarmp->last_offset);
|
|
vm_vec_add(&swarmp->last_offset, &uvec_component, &rvec_component);
|
|
vm_vec_add2(&swarmp->new_target, &swarmp->last_offset);
|
|
|
|
} else {
|
|
// Not time to zig-zag.
|
|
if (hobjp != &obj_used_list &&
|
|
f2fl(Missiontime - wp->creation_time) > 0.5f &&
|
|
f2fl(Missiontime - wp->creation_time) > wip->free_flight_time) {
|
|
// Still homing
|
|
swarmp->new_target = swarmp->original_target;
|
|
|
|
// Continue homing in on our last offset around the target
|
|
vm_vec_add2(&swarmp->new_target, &swarmp->last_offset);
|
|
} // Else, lost homing, maintain current trajectory
|
|
}
|
|
|
|
swarm_new_target_calced:
|
|
|
|
ai_turn_towards_vector(&swarmp->new_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->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, weapon_info *wip, int weapon_num)
|
|
{
|
|
turret_swarm_info *tsi;
|
|
object *parent_obj, *target_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 >= 0) && (turret->turret_enemy_objnum < MAX_OBJECTS));
|
|
if((turret->turret_enemy_objnum < 0) || (turret->turret_enemy_objnum >= MAX_OBJECTS)){
|
|
return;
|
|
}
|
|
target_obj = &Objects[turret->turret_enemy_objnum];
|
|
|
|
// 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_get_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]) {
|
|
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 = target_obj->signature;
|
|
tsi->turret = turret;
|
|
tsi->target_subsys = turret->targeted_subsys;
|
|
tsi->time_to_fire = 1; // first missile next frame
|
|
tsi->weapon_num = weapon_num;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|