Skip to content

Commit

Permalink
proper fix for fighter/bomber
Browse files Browse the repository at this point in the history
This fixes the fighter/bomber type which had been broken ever since the objecttypes.tbl upgrade in 246cfcf: the fighter/bomber type is again treated as a union of fighters and bombers, not a unique type of its own.  This requires special handling for the `OPF_SHIP_TYPE` SEXP argument type, and all four SEXPs that use `OPF_SHIP_TYPE` have been modified to check for the special case.  The explicit fighter/bomber type has again been removed from the built-in objecttypes.tbl.

The requirements for "fighter/bomber" to be valid are 1) there is no explicit "fighter/bomber" type in the mod; 2) a "fighter" type exists; 3) a "bomber" type exists.  If any of these conditions is not met, the special case behavior is not enabled.

Follow-up to #6390.  Properly fixes #6388.
  • Loading branch information
Goober5000 committed Oct 14, 2024
1 parent 947b3d4 commit a9d2605
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 40 deletions.
130 changes: 98 additions & 32 deletions code/parse/sexp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3167,18 +3167,25 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, s
break;
}

case OPF_SHIP_TYPE:
case OPF_SHIP_TYPE: {
if (type2 != SEXP_ATOM_STRING){
return SEXP_CHECK_TYPE_MISMATCH;
}

i = ship_type_name_lookup(CTEXT(node));
auto type_name = CTEXT(node);
i = ship_type_name_lookup(type_name);

if (i < 0){
if (Fighter_bomber_valid && !stricmp(type_name, Fighter_bomber_type_name)) {
// this is allowed even though it's not an explicit type
break;
}

return SEXP_CHECK_INVALID_SHIP_TYPE;
}

break;
}

case OPF_WAYPOINT_PATH:
if (find_matching_waypoint_list(CTEXT(node)) == nullptr) {
Expand Down Expand Up @@ -7724,21 +7731,35 @@ int sexp_ship_type_destroyed(int n)
return SEXP_KNOWN_FALSE;

auto shiptype = CTEXT(CDR(n));

int type = ship_type_name_lookup(shiptype);

// bogus if we reach the end of this array!!!!
if ( type < 0 ) {
Warning(LOCATION, "Invalid shiptype passed to ship-type-destroyed");
if (type < 0 && Fighter_bomber_valid && !stricmp(shiptype, Fighter_bomber_type_name))
type = Ship_type_fighter_bomber;
else if (!SCP_vector_inbounds(Ship_types, type))
{
// bogus if we reach the end of this array!!!!
Warning(LOCATION, "Invalid shiptype '%s' passed to ship-type-destroyed", shiptype);
return SEXP_FALSE;
}

if ( type >= (int)Ship_type_counts.size() || Ship_type_counts[type].total == 0 )
int killed, total;
if (type == Ship_type_fighter_bomber)
{
killed = Ship_type_counts[Ship_type_fighter].killed + Ship_type_counts[Ship_type_bomber].killed;
total = Ship_type_counts[Ship_type_fighter].total + Ship_type_counts[Ship_type_bomber].total;
}
else
{
//We are safe from array indexing probs b/c of previous if.
killed = Ship_type_counts[type].killed;
total = Ship_type_counts[type].total;
}

if (total == 0)
return SEXP_FALSE;

//We are safe from array indexing probs b/c of previous if.
// determine if the percentage of killed/total is >= percentage given in the expression
if ( (Ship_type_counts[type].killed * 100 / Ship_type_counts[type].total) >= percent)
if ((killed * 100 / total) >= percent)
return SEXP_KNOWN_TRUE;

return SEXP_FALSE;
Expand Down Expand Up @@ -11686,6 +11707,8 @@ int eval_for_ship_collection(int arg_handler_node, int condition_node, int op_co
break;
case OP_FOR_SHIP_TYPE:
constraint_index = ship_type_name_lookup(constraint);
if (constraint_index < 0 && Fighter_bomber_valid && !stricmp(constraint, Fighter_bomber_type_name))
constraint_index = Ship_type_fighter_bomber;
break;
case OP_FOR_SHIP_TEAM:
constraint_index = iff_lookup(constraint);
Expand Down Expand Up @@ -11724,7 +11747,13 @@ int eval_for_ship_collection(int arg_handler_node, int condition_node, int op_co
break;
}

if (constraint_index == ship_index)
bool constraint_matches;
if (op_const == OP_FOR_SHIP_TYPE && constraint_index == Ship_type_fighter_bomber)
constraint_matches = (ship_index == Ship_type_fighter || ship_index == Ship_type_bomber);
else
constraint_matches = (ship_index == constraint_index);

if (constraint_matches)
{
if (just_count)
num_valid_arguments++;
Expand Down Expand Up @@ -12704,13 +12733,22 @@ int sexp_is_ship_class_or_type(int n, bool ship_class)
Assert( n >= 0 );

// get class or type
int index;
auto name = CTEXT(n);
int constraint_index;
if (ship_class)
index = ship_info_lookup(CTEXT(n));
constraint_index = ship_info_lookup(name);
else
index = ship_type_name_lookup(CTEXT(n));
{
constraint_index = ship_type_name_lookup(name);
if (constraint_index < 0 && Fighter_bomber_valid && !stricmp(name, Fighter_bomber_type_name))
constraint_index = Ship_type_fighter_bomber;
}
n = CDR(n);

// not valid; no need to check
if (constraint_index < 0)
return SEXP_FALSE;

// eval ships
while (n != -1)
{
Expand All @@ -12719,29 +12757,37 @@ int sexp_is_ship_class_or_type(int n, bool ship_class)
if (!ship_entry)
return SEXP_NAN;

int other_index;
int ship_index;
if (ship_entry->status == ShipStatus::NOT_YET_PRESENT)
{
other_index = ship_entry->p_objp()->ship_class;
ship_index = ship_entry->p_objp()->ship_class;
}
else if (ship_entry->exited_index >= 0)
{
other_index = Ships_exited[ship_entry->exited_index].ship_class;
ship_index = Ships_exited[ship_entry->exited_index].ship_class;
}
else if (ship_entry->has_shipp())
{
other_index = ship_entry->shipp()->ship_info_index;
ship_index = ship_entry->shipp()->ship_info_index;
}
else
return SEXP_NAN_FOREVER;

// maybe convert from class to type
if (!ship_class)
other_index = ship_class_query_general_type(other_index);
ship_index = ship_class_query_general_type(ship_index);

// if it doesn't match, return false
if (index != other_index)
return SEXP_FALSE;
if (!ship_class && constraint_index == Ship_type_fighter_bomber)
{
if (ship_index != Ship_type_fighter && ship_index != Ship_type_bomber)
return SEXP_FALSE;
}
else
{
if (ship_index != constraint_index)
return SEXP_FALSE;
}

// increment
n = CDR(n);
Expand Down Expand Up @@ -24147,22 +24193,41 @@ int sexp_return_player_data(int node, int type)
int sexp_num_type_kills(int node)
{
auto p = get_player_from_ship_node(node, true);
if (!p) {
if (!p)
return 0;
}

// lookup ship type name
int st_index = ship_type_name_lookup(CTEXT(CDR(node)));
if (st_index < 0) {
return 0;
// look up ship type name
auto name = CTEXT(CDR(node));
int constraint_type = ship_type_name_lookup(name);
if (constraint_type < 0)
{
if (Fighter_bomber_valid && !stricmp(name, Fighter_bomber_type_name))
constraint_type = Ship_type_fighter_bomber;
else
return 0;
}

// look stuff up
int total = 0;
for (int idx = 0; idx < ship_info_size(); idx++) {
if ((p->stats.m_okKills[idx] > 0) && ship_class_query_general_type(idx) == st_index) {
total += p->stats.m_okKills[idx];
for (int idx = 0; idx < ship_info_size(); idx++)
{
if (p->stats.m_okKills[idx] <= 0)
continue;

int ship_type = ship_class_query_general_type(idx);

if (constraint_type == Ship_type_fighter_bomber)
{
if (ship_type != Ship_type_fighter && ship_type != Ship_type_bomber)
continue;
}
else
{
if (ship_type != constraint_type)
continue;
}

total += p->stats.m_okKills[idx];
}

// total
Expand Down Expand Up @@ -37708,9 +37773,10 @@ SCP_vector<sexp_help_struct> Sexp_help = {
"\t4:\tHow many times the ship has completed the waypoint path (optional)." },

{ OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n"
"\tBecomes true when the specified percentage of ship types in this mission (including ships not yet in-mission) "
"have been destroyed. The ship type is a generic type such as fighter/bomber, "
"transport, etc. Fighters and bombers count as the same type.\r\n\r\n"
"\tBecomes true when the specified percentage of ship types in this mission (including ships not yet in-mission) have been destroyed. The "
"ship type is a generic type such as fighter, bomber, transport, etc. Note that the percentage is updated when a ship finishes exploding, "
"in contrast to the mission log and the *-destroyed-delay SEXPs which are updated when a ship starts exploding. Note also that player ships "
"and ships with the \"Ignore for counting goals\" flag are not included in the calculation.\r\n\r\n"
"Returns a boolean value. Takes 2 arguments...\r\n"
"\t1:\tPercentage of ships that must be destroyed.\r\n"
"\t2:\tShip type to check for." },
Expand Down
2 changes: 1 addition & 1 deletion code/parse/sexp.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ enum sexp_opf_t : int {
OPF_SHIP_WING_SHIPONTEAM_POINT, // name of a ship, wing, any ship on a team, or a point
OPF_SHIP_WING_POINT,
OPF_SHIP_WING_POINT_OR_NONE, // WMC - Ship, wing, point or none
OPF_SHIP_TYPE, // type of ship (fighter/bomber/etc)
OPF_SHIP_TYPE, // type of ship (fighter/bomber/etc)... NOTE: the type "fighter/bomber" is allowed even though it's not a real ship type; SEXPs must account for this
OPF_KEYPRESS, // a default key
OPF_EVENT_NAME, // name of an event
OPF_AI_ORDER, // a squadmsg order player can give to a ship
Expand Down
26 changes: 19 additions & 7 deletions code/ship/ship.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ reinforcements Reinforcements[MAX_REINFORCEMENTS];
SCP_vector<ship_info> Ship_templates;

SCP_vector<ship_type_info> Ship_types;
bool Fighter_bomber_valid = false;
const char *Fighter_bomber_type_name = "fighter/bomber";
int Ship_type_fighter = -1, Ship_type_bomber = -1, Ship_type_fighter_bomber = -1;

SCP_vector<ArmorType> Armor_types;
SCP_vector<DamageTypeStruct> Damage_types;
Expand Down Expand Up @@ -6232,6 +6235,16 @@ void ship_init()
Ship_types.erase(Ship_types.begin() + idx);
}

// See whether this mod defines an explicit "fighter/bomber" type (which would take priority over the special usage), and if not, whether the component types exist
if (ship_type_name_lookup(Fighter_bomber_type_name) < 0)
{
Ship_type_fighter_bomber = static_cast<int>(Ship_types.size()) + 1; // This is a convenient way to indicate that we want the fighter/bomber special case; it is not an actual valid Ship_types index
Ship_type_fighter = ship_type_name_lookup("fighter");
Ship_type_bomber = ship_type_name_lookup("bomber");
if (Ship_type_fighter >= 0 && Ship_type_bomber >= 0)
Fighter_bomber_valid = true;
}

// DO ALL THE STUFF WE NEED TO DO AFTER LOADING Ship_types
ship_type_info *stp;

Expand Down Expand Up @@ -16177,8 +16190,8 @@ void ship_add_ship_type_count( int ship_info_index, int num )
{
int type = ship_class_query_general_type(ship_info_index);

//Ship has no type or something
if(type < 0) {
//Ship has no type or the vector isn't set up
if (!SCP_vector_inbounds(Ship_type_counts, type)) {
return;
}

Expand All @@ -16190,14 +16203,13 @@ static void ship_add_ship_type_kill_count( int ship_info_index )
{
int type = ship_class_query_general_type(ship_info_index);

//Ship has no type or something
if(type < 0) {
//Ship has no type or the vector isn't set up
if (!SCP_vector_inbounds(Ship_type_counts, type)) {
return;
}

//Add it if we are actually in gameplay
if (Ship_type_counts.size() > static_cast<size_t>(type))
Ship_type_counts[type].killed++;
//Add it
Ship_type_counts[type].killed++;
}

int ship_query_general_type(int ship)
Expand Down
3 changes: 3 additions & 0 deletions code/ship/ship.h
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,9 @@ typedef struct ship_type_info {
} ship_type_info;

extern SCP_vector<ship_type_info> Ship_types;
extern bool Fighter_bomber_valid; // Whether "fighter/bomber" can be used as a union of "fighter" and "bomber"
extern const char *Fighter_bomber_type_name;
extern int Ship_type_fighter, Ship_type_bomber, Ship_type_fighter_bomber;

class rcs_thruster_info {
public:
Expand Down
3 changes: 3 additions & 0 deletions fred2/sexp_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7061,6 +7061,9 @@ sexp_list_item *sexp_tree::get_listing_opf_ship_type()
for (i=0; i<Ship_types.size(); i++){
head.add_data(Ship_types[i].name);
}
if (Fighter_bomber_valid) {
head.add_data(Fighter_bomber_type_name);
}

return head.next;
}
Expand Down
3 changes: 3 additions & 0 deletions qtfred/src/ui/widgets/sexp_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4690,6 +4690,9 @@ sexp_list_item* sexp_tree::get_listing_opf_ship_type() {
for (i = 0; i < Ship_types.size(); i++) {
head.add_data(Ship_types[i].name);
}
if (Fighter_bomber_valid) {
head.add_data(Fighter_bomber_type_name);
}

return head.next;
}
Expand Down

0 comments on commit a9d2605

Please sign in to comment.