red-alert will now work with ships that appear at some point subsequent to mission start

git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@10267 387891d4-d844-0410-90c0-e4c51a9137d3
This commit is contained in:
Goober5000 authored and niffiwan committed 2014-01-14 20:24:45 +10:00
1 parent 180115feba
commit a04e83ab01
6 files changed
+372 -94

No files matched your search

+32 -35
View File
@@ -599,49 +599,46 @@ bool HudGaugeWingmanStatus::maybeFlashStatus(int wing_index, int wing_pos)
return draw_bright;
}
int hud_wingman_status_wing_pos(int shipnum, int wing_status_index, wing *wingp)
void hud_wingman_status_set_index(wing *wingp, ship *shipp, p_object *pobjp)
{
int i, wing_pos = -1;
for (i = 0; i < wingp->wave_count; i++) {
if ( wingp->ship_index[i] == shipnum ) {
wing_pos = i;
break;
}
}
return wing_pos;
}
void hud_wingman_status_set_index(int shipnum)
{
int wing_index, wing_pos;
ship *shipp;
wing *wingp;
if ( shipnum < 0 ) {
return;
}
shipp = &Ships[shipnum];
if ( shipp->wingnum < 0 ) {
return;
}
wingp = &Wings[shipp->wingnum];
int wing_index;
// Check for squadron wings
wing_index = ship_squadron_wing_lookup(wingp->name);
if ( wing_index < 0 ) {
if ( wing_index < 0 )
return;
// this wing is shown on the squadron display
shipp->wing_status_wing_index = (char) wing_index;
// for the first wave, just use the parse object position
if (wingp->current_wave == 1)
{
shipp->wing_status_wing_pos = (char) pobjp->pos_in_wing;
}
// for subsequent waves, find the first position not taken
else
{
int i, pos, wing_bitfield = 0;
shipp->wing_status_wing_index = (char)wing_index;
// fill in all the positions currently used by this wing
for (i = 0; i < wingp->wave_count; i++)
{
pos = Ships[wingp->ship_index[i]].wing_status_wing_pos;
if (pos >= 0)
wing_bitfield |= (1<<pos);
}
wing_pos = hud_wingman_status_wing_pos(shipnum, wing_index, wingp);
shipp->wing_status_wing_pos = (char)wing_pos;
// now assign the first available slot
for (i = 0; i < MAX_SHIPS_PER_WING; i++)
{
if (!(wing_bitfield & (1<<i)))
{
shipp->wing_status_wing_pos = (char) i;
break;
}
}
}
}
void HudGaugeWingmanStatus::pageIn()
+5 -1
View File
@@ -14,6 +14,10 @@
#include "hud/hud.h"
struct wing;
class ship;
class p_object;
void hud_init_wingman_status_gauge();
void hud_wingman_status_update();
void hud_wingman_status_render();
@@ -24,7 +28,7 @@ void hud_set_wingman_status_departed(int wing_index, int wing_pos);
void hud_set_wingman_status_alive( int wing_index, int wing_pos);
void hud_set_wingman_status_none( int wing_index, int wing_pos);
void hud_wingman_status_start_flash(int wing_index, int wing_pos);
void hud_wingman_status_set_index(int shipnum);
void hud_wingman_status_set_index(wing *wingp, ship *shipp, p_object *pobjp);
class HudGaugeWingmanStatus: public HudGauge
{
+40 -14
View File
@@ -2354,8 +2354,7 @@ void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum)
wing *wingp = &Wings[wingnum];
// link ship and wing together
// (do this first because mission log relies on ship_index
// and hud_wingman_status_set_index relies on ship's wingnum)
// (do this first because mission log relies on ship_index)
wingp->ship_index[p_objp->pos_in_wing] = shipnum;
Ships[shipnum].wingnum = wingnum;
@@ -2379,7 +2378,7 @@ void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum)
// at this point the wing has arrived, so handle the stuff for this particular ship
// set up wingman status index
hud_wingman_status_set_index(shipnum);
hud_wingman_status_set_index(wingp, &Ships[shipnum], p_objp);
// copy to parse object
p_objp->wing_status_wing_index = Ships[shipnum].wing_status_wing_index;
@@ -3987,6 +3986,23 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int spec
specific_instance--;
continue;
}
// when not creating a specific ship, we should skip over any ships that weren't carried along in the red-alert
else if (p_objp->flags2 & P2_RED_ALERT_DELETED)
{
num_to_create--;
num_create_save--;
ship_add_ship_type_count(p_objp->ship_class, -1);
wingp->red_alert_skipped_ships++;
// clear the flag so that this parse object can be used for the next wave
p_objp->flags2 &= ~P2_RED_ALERT_DELETED;
// skip over this parse object
if (num_to_create == 0)
break;
else
continue;
}
Assert (!(p_objp->flags & P_SF_CANNOT_ARRIVE)); // get allender
@@ -4001,7 +4017,7 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int spec
// bash the ship name to be the name of the wing + some number if there is > 1 wave in this wing
wingp->total_arrived_count++;
if (wingp->num_waves > 1)
wing_bash_ship_name(p_objp->name, wingp->name, wingp->total_arrived_count);
wing_bash_ship_name(p_objp->name, wingp->name, wingp->total_arrived_count + wingp->red_alert_skipped_ships);
// also, if multiplayer, set the parse object's net signature to be wing's net signature
// base + total_arrived_count (before adding 1)
@@ -4037,7 +4053,7 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int spec
}
// set up wingman status index
hud_wingman_status_set_index(Objects[objnum].instance);
hud_wingman_status_set_index(wingp, &Ships[Objects[objnum].instance], p_objp);
p_objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index;
p_objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos;
@@ -4092,20 +4108,29 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int spec
Assert (num_to_create == 0);
// wing current_count needs to match the end of the ship_index[] list, but there
// is a very off chance it could have holes in it, so make sure to compact the list
for (i = 0; i < (MAX_SHIPS_PER_WING-1); i++) {
if (wingp->ship_index[i] == -1) {
j = i;
while ( j < (MAX_SHIPS_PER_WING-1) ) {
// is a very off chance it could have holes in it (especially if it's a red-alert
// wing that arrives late), so make sure to compact the list
int length = MAX_SHIPS_PER_WING;
for (i = 0; i < length; i++)
{
if (wingp->ship_index[i] == -1)
{
// shift actual values downward
for (j = i; j < length - 1; j++)
{
wingp->ship_index[j] = wingp->ship_index[j+1];
// update "special" ship too
if (wingp->special_ship == j+1) {
if (wingp->special_ship == j+1)
wingp->special_ship--;
}
j++;
}
// last value becomes -1
wingp->ship_index[j] = -1;
length--;
// stay on the current index in case we still have a -1
i--;
}
}
@@ -4204,6 +4229,7 @@ void parse_wing(mission *pm)
wingnum = Num_wings;
wingp->total_arrived_count = 0;
wingp->red_alert_skipped_ships = 0;
wingp->total_destroyed = 0;
wingp->total_departed = 0; // Goober5000
wingp->total_vanished = 0; // Goober5000
+2 -1
View File
@@ -512,8 +512,9 @@ public:
#define P2_SF2_SCRAMBLE_MESSAGES (1<<22)
// and again: these flags do not appear in the array
//#define blah (1<<28)
//#define blah (1<<29)
//#define blah (1<<30)
#define P2_RED_ALERT_DELETED (1<<30) // Goober5000 - used analogously to SEF_PLAYER_DELETED
#define P2_ALREADY_HANDLED (1<<31) // Goober5000 - used for docking currently, but could be used generically
+292 -43
View File
@@ -520,7 +520,7 @@ void red_alert_bash_weapons(red_alert_ship_status *ras, ship_weapon *swp)
}
swp->num_primary_banks = list_size;
// bash secondary weapons
list_size = (int)ras->secondary_weapons.size();
CLAMP(list_size, 0, MAX_SHIP_SECONDARY_BANKS);
for (i = 0; i < list_size; i++) {
@@ -532,6 +532,63 @@ void red_alert_bash_weapons(red_alert_ship_status *ras, ship_weapon *swp)
swp->num_secondary_banks = list_size;
}
void red_alert_bash_weapons(red_alert_ship_status *ras, p_object *pobjp)
{
int i, list_size = 0;
ship_info *sip;
subsys_status *sssp = NULL;
// restore from ship_exited
if ( (ras->ship_class == RED_ALERT_DESTROYED_SHIP_CLASS) || (ras->ship_class == RED_ALERT_PLAYER_DEL_SHIP_CLASS) )
return;
sip = &Ship_info[ras->ship_class];
// parse objects use the "pilot" subsystem
for (i = 0; i < pobjp->subsys_count; i++)
{
if (!stricmp(Subsys_status[pobjp->subsys_index + i].name, "pilot"))
{
sssp = &Subsys_status[pobjp->subsys_index + i];
break;
}
}
if (sssp == NULL)
{
Warning(LOCATION, "Parse object data for ship '%s' doesn't contain the 'Pilot' subsystem!", pobjp->name);
return;
}
// bash primary weapons
list_size = (int)ras->primary_weapons.size();
CLAMP(list_size, 0, MAX_SHIP_PRIMARY_BANKS);
for (i = 0; i < list_size; i++)
{
Assert( ras->primary_weapons[i].index >= 0 );
sssp->primary_banks[i] = ras->primary_weapons[i].index;
if (Weapon_info[sssp->primary_banks[i]].wi_flags2 & WIF2_BALLISTIC)
{
float max_count = sip->primary_bank_ammo_capacity[i] / Weapon_info[sssp->primary_banks[i]].cargo_size;
sssp->primary_ammo[i] = fl2i(100.0f * (ras->primary_weapons[i].count - 2) / max_count + 0.5f);
}
else
sssp->primary_ammo[i] = 100;
}
// bash secondary weapons
list_size = (int)ras->secondary_weapons.size();
CLAMP(list_size, 0, MAX_SHIP_SECONDARY_BANKS);
for (i = 0; i < list_size; i++)
{
Assert( ras->secondary_weapons[i].index >= 0 );
sssp->secondary_banks[i] = ras->secondary_weapons[i].index;
float max_count = sip->secondary_bank_ammo_capacity[i] / Weapon_info[sssp->secondary_banks[i]].cargo_size;
sssp->secondary_ammo[i] = fl2i(100.0f * ras->secondary_weapons[i].count / max_count + 0.5f);
}
}
void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp)
{
ship_subsys *ss;
@@ -568,6 +625,53 @@ void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp)
}
}
extern int insert_subsys_status(p_object *pobjp);
void red_alert_bash_subsys_status(red_alert_ship_status *ras, p_object *pobjp)
{
int i, j;
ship_info *sip;
model_subsystem *psub = NULL;
subsys_status *sssp = NULL;
// restore from ship_exited
if ( (ras->ship_class == RED_ALERT_DESTROYED_SHIP_CLASS) || (ras->ship_class == RED_ALERT_PLAYER_DEL_SHIP_CLASS) )
return;
sip = &Ship_info[ras->ship_class];
// do this differently than the other bash_subsys_status... since the p_object may not contain
// all the subsystems, iterate on the red_alert_ship_status entries rather than the p_object's
// and create missing subsystems where necessary
for (i = 0; i < (int) ras->subsys_current_hits.size(); i++)
{
psub = &sip->subsystems[i];
// in the p_object, subsystem 0 is the pilot, and afterwards the subsystems go in order
j = i + 1;
// this subsystem is in the p_object
if (j < pobjp->subsys_count)
{
sssp = &Subsys_status[pobjp->subsys_index + j];
}
// must create subsystem (same technique as in parse_copy_damage)
else
{
// jam in the new subsystem at the end of the existing list for this parse object
int new_idx = insert_subsys_status(pobjp);
Assert(new_idx == pobjp->subsys_index + j);
sssp = &Subsys_status[new_idx];
strcpy_s(sssp->name, psub->subobj_name);
}
float max_hits = psub->max_subsys_strength * (pobjp->ship_max_hull_strength / sip->max_hull_strength);
float current_hits = ras->subsys_current_hits[i];
sssp->percent = 100.0f * (max_hits - current_hits) / max_hits;
}
}
void red_alert_store_subsys_status(red_alert_ship_status *ras, ship *shipp)
{
@@ -690,6 +794,20 @@ void red_alert_delete_ship(ship *shipp, int ship_state)
}
}
// just mark the parse object as never going to arrive
void red_alert_delete_ship(p_object *pobjp, int ship_state)
{
if (ship_state == RED_ALERT_DESTROYED_SHIP_CLASS || ship_state == RED_ALERT_PLAYER_DEL_SHIP_CLASS)
{
pobjp->flags2 |= P2_RED_ALERT_DELETED;
if (pobjp->wingnum < 0)
pobjp->flags |= P_SF_CANNOT_ARRIVE;
}
else
Error(LOCATION, "Red Alert: asked to delete ship (%s) with invalid ship state (%d)", pobjp->name, ship_state);
}
/*
* Take the red alert status information, and adjust the red alert ships accordingly
* "red alert ships" are wingmen and any ship with the red-alert-carry flag
@@ -697,11 +815,14 @@ void red_alert_delete_ship(ship *shipp, int ship_state)
*/
void red_alert_bash_wingman_status()
{
int i;
ship *shipp;
red_alert_ship_status *ras;
int j;
ship_obj *so;
object *ship_objp;
SCP_vector<red_alert_ship_status>::iterator rasii;
SCP_vector<p_object>::iterator poii;
SCP_hash_map<int, int> Wing_pobjects_deleted;
SCP_hash_map<int, int>::iterator ii;
if ( !(Game_mode & GM_CAMPAIGN_MODE) ) {
return;
@@ -713,61 +834,189 @@ void red_alert_bash_wingman_status()
// go through all ships in the game, and see if there is red alert status data for any
int remove_list[MAX_SHIPS];
int remove_state[MAX_SHIPS];
int remove_count = 0;
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
ship_objp = &Objects[so->objnum];
so = GET_FIRST(&Ship_obj_list);
for ( ; so != END_OF_LIST(&Ship_obj_list); )
{
object *ship_objp = &Objects[so->objnum];
Assert(ship_objp->type == OBJ_SHIP);
shipp = &Ships[ship_objp->instance];
ship *shipp = &Ships[ship_objp->instance];
if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) {
so = GET_NEXT(so);
continue;
}
int found_match = 0;
bool ship_data_restored = false;
int ship_state = RED_ALERT_DESTROYED_SHIP_CLASS;
for ( i = 0; i < (int)Red_alert_wingman_status.size(); i++ ) {
ras = &Red_alert_wingman_status[i];
for ( rasii = Red_alert_wingman_status.begin(); rasii != Red_alert_wingman_status.end(); ++rasii )
{
red_alert_ship_status *ras = &(*rasii);
// we only want to restore ships which haven't been destroyed, or were removed by the player
if ( !stricmp(ras->name.c_str(), shipp->ship_name) && (ras->ship_class != RED_ALERT_DESTROYED_SHIP_CLASS) && (ras->ship_class != RED_ALERT_PLAYER_DEL_SHIP_CLASS) ) {
found_match = 1;
// if necessary, restore correct ship class
if ( ras->ship_class != shipp->ship_info_index ) {
if (ras->ship_class < MAX_SHIP_CLASSES && ras->ship_class > -1) {
change_ship_type(SHIP_INDEX(shipp), ras->ship_class);
} else {
mprintf(("Invalid ship class specified in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
// red-alert data matches this ship!
if ( !stricmp(ras->name.c_str(), shipp->ship_name) )
{
// we only want to restore ships which haven't been destroyed, or were removed by the player
if ( (ras->ship_class != RED_ALERT_DESTROYED_SHIP_CLASS) && (ras->ship_class != RED_ALERT_PLAYER_DEL_SHIP_CLASS) )
{
// if necessary, restore correct ship class
if ( ras->ship_class != shipp->ship_info_index )
{
if (ras->ship_class >= 0 && ras->ship_class < MAX_SHIP_CLASSES)
change_ship_type(SHIP_INDEX(shipp), ras->ship_class);
else
mprintf(("Invalid ship class specified in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
}
}
// restore hull and weapons
if (ras->hull >= 0.0f && ras->hull <= ship_objp->hull_strength) {
ship_objp->hull_strength = ras->hull;
} else {
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
}
red_alert_bash_weapons(ras, &shipp->weapons);
red_alert_bash_subsys_status(ras, shipp);
} else if ( !stricmp(ras->name.c_str(), shipp->ship_name) && ( (ras->ship_class == RED_ALERT_DESTROYED_SHIP_CLASS) || (ras->ship_class == RED_ALERT_PLAYER_DEL_SHIP_CLASS) ) ) {
ship_state = ras->ship_class;
// restore hull (but not shields)
if (ras->hull >= 0.0f && ras->hull <= ship_objp->hull_strength)
ship_objp->hull_strength = ras->hull;
else
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
// restore weapons and subsys
red_alert_bash_weapons(ras, &shipp->weapons);
red_alert_bash_subsys_status(ras, shipp);
ship_data_restored = true;
}
// must be destroyed or deleted
else
{
ship_state = ras->ship_class;
}
// we won't have two ships with the same name, so bail
break;
}
}
if ( !found_match ) {
remove_state[remove_count] = ship_state;
remove_list[remove_count++] = SHIP_INDEX(shipp);
// remove ship if it was destroyed, or if there's no red-alert data for it
if ( !ship_data_restored ) {
// we need to be a little tricky here because deletion invalidates the ship_obj
ship_obj *next_so = GET_NEXT(so);
red_alert_delete_ship(shipp, ship_state);
so = next_so;
} else {
so = GET_NEXT(so);
}
}
// remove ships
for ( i = 0; i < remove_count; i++ ) {
// remove ship
red_alert_delete_ship(&Ships[remove_list[i]], remove_state[i]);
// go through all ships yet to arrive, and see if there is red alert status data for any
for ( poii = Parse_objects.begin(); poii != Parse_objects.end(); ++poii )
{
p_object *pobjp = &(*poii);
// objects that have already arrived would have been handled in the above loop
if ( pobjp->created_object != NULL )
continue;
// if we're in a wing, check whether we're in the player wing
bool from_player_wing = false;
if (pobjp->wingnum >= 0)
{
for (j = 0; j < MAX_STARTING_WINGS; j++)
{
if (!stricmp(Starting_wing_names[j], Wings[pobjp->wingnum].name))
{
from_player_wing = true;
break;
}
}
}
// same condition as in ship_obj loop
if ( !from_player_wing && !(pobjp->flags & P_SF_RED_ALERT_STORE_STATUS) ) {
continue;
}
bool ship_data_restored = false;
int ship_state = RED_ALERT_DESTROYED_SHIP_CLASS;
for ( rasii = Red_alert_wingman_status.begin(); rasii != Red_alert_wingman_status.end(); ++rasii )
{
red_alert_ship_status *ras = &(*rasii);
// red-alert data matches this ship!
if ( !stricmp(ras->name.c_str(), pobjp->name) )
{
// we only want to restore ships which haven't been destroyed, or were removed by the player
if ( (ras->ship_class != RED_ALERT_DESTROYED_SHIP_CLASS) && (ras->ship_class != RED_ALERT_PLAYER_DEL_SHIP_CLASS) )
{
// if necessary, restore correct ship class
if ( ras->ship_class != pobjp->ship_class )
{
if (ras->ship_class >= 0 && ras->ship_class < MAX_SHIP_CLASSES)
swap_parse_object(pobjp, ras->ship_class);
else
mprintf(("Invalid ship class specified in red alert data for ship %s. Using mission defaults.\n", pobjp->name));
}
// restore hull (but not shields)
if (ras->hull >= 0.0f && ras->hull <= (pobjp->initial_hull * pobjp->ship_max_hull_strength / 100.0f))
pobjp->initial_hull = (int) (ras->hull * 100.0f / pobjp->ship_max_hull_strength);
else
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", pobjp->name));
// restore weapons and subsys
red_alert_bash_weapons(ras, pobjp);
red_alert_bash_subsys_status(ras, pobjp);
ship_data_restored = true;
}
// must be destroyed or deleted
else
{
ship_state = ras->ship_class;
}
// we won't have two ships with the same name, so bail
break;
}
}
// remove ship if it was destroyed, or if there's no red-alert data for it
if ( !ship_data_restored )
{
red_alert_delete_ship(pobjp, ship_state);
if (pobjp->wingnum >= 0)
Wing_pobjects_deleted[pobjp->wingnum]++;
}
}
// if all parse objects in a wing have been removed, decrement the count for that wing
for (ii = Wing_pobjects_deleted.begin(); ii != Wing_pobjects_deleted.end(); ++ii)
{
wing *wingp = &Wings[ii->first];
if (wingp->num_waves > 0 && wingp->wave_count == ii->second)
{
wingp->current_wave++;
wingp->red_alert_skipped_ships += wingp->wave_count;
if (wingp->num_waves == 0)
{
wingp->flags |= WF_WING_GONE;
wingp->flags |= WF_NEVER_EXISTED;
}
// look through all ships yet to arrive...
for (p_object *pobjp = GET_FIRST(&Ship_arrival_list); pobjp != END_OF_LIST(&Ship_arrival_list); pobjp = GET_NEXT(pobjp))
{
// ...and mark the ones in this wing
if (pobjp->wingnum == ii->first)
{
// no waves left to arrive, so mark ships accordingly
if (wingp->num_waves == 0)
pobjp->flags |= P_SF_CANNOT_ARRIVE;
// we skipped one complete wave, so clear the flag so the next wave creates all ships
else
pobjp->flags2 &= ~P2_RED_ALERT_DELETED;
}
}
}
}
}
+1
View File
@@ -1480,6 +1480,7 @@ typedef struct wing {
int wave_count; // max ships per wave (as defined by the number of ships in the ships list)
int total_arrived_count; // count of number of ships that we have created, regardless of wave
int red_alert_skipped_ships; // Goober5000 - if we skipped over any indexes while creating red-alert ships
int current_count; // count of number of ships actually in this wing -- used for limit in next array
int ship_index[MAX_SHIPS_PER_WING]; // index into ships array of all ships currently in the wing