make fireball types dynamic

As requested by Event Horizon, this removes the limit on fireball types.  The fixed array has been replaced with a vector and the relevant code updated accordingly.  There is also a new version of `stuff_int_list()` to parse ints into a vector.

This also adds bounds checking for explosion animations that are parsed for ship classes, ship types, and asteroids, since previously there was no guarantee that the indexes would be in range.
This commit is contained in:
Goober5000 committed 2024-04-25 21:26:35 -04:00
1 parent 289d0b66d1
commit 3851832db5
14 files changed
+108 -93

No files matched your search

+3 -7
View File
@@ -1593,9 +1593,8 @@ static float asteroid_create_explosion(object *objp)
fireball_type = FIREBALL_ASTEROID;
}
if (fireball_type >= Num_fireball_types) {
Warning(LOCATION, "Invalid fireball type %i specified for an asteroid, only %i fireball types are defined.", fireball_type, Num_fireball_types);
if (fireball_type >= static_cast<int>(Fireball_info.size())) {
Warning(LOCATION, "Invalid fireball type %i specified for an asteroid; only " SIZE_T_ARG " fireball types are defined.", fireball_type, Fireball_info.size());
return 0;
}
@@ -2251,10 +2250,7 @@ static void asteroid_parse_section()
}
if(optional_string("$Explosion Animations:")){
int temp[MAX_FIREBALL_TYPES];
auto parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
asteroid_p->explosion_bitmap_anims.clear();
asteroid_p->explosion_bitmap_anims.insert(asteroid_p->explosion_bitmap_anims.begin(), temp, temp + parsed_ints);
stuff_fireball_index_list(asteroid_p->explosion_bitmap_anims, asteroid_p->name);
}
if (optional_string("$Explosion Radius Mult:")) {
+38 -34
View File
@@ -37,11 +37,7 @@ constexpr int INTITIAL_FIREBALL_CONTAINTER_SIZE = 256;
SCP_vector<fireball> Fireballs;
SCP_vector<int> Unused_fireball_indices;
fireball_info Fireball_info[MAX_FIREBALL_TYPES];
int fireball_used[MAX_FIREBALL_TYPES];
int Num_fireball_types = 0;
SCP_vector<fireball_info> Fireball_info;
bool fireballs_inited = false;
bool fireballs_parsed = false;
@@ -120,7 +116,7 @@ void fireball_play_warphole_close_sound(fireball *fb)
static void fireball_generate_unique_id(char *unique_id, int buffer_len, int fireball_index)
{
Assert((fireball_index >= 0) && (fireball_index < MAX_FIREBALL_TYPES));
Assertion(SCP_vector_inbounds(Fireball_info, fireball_index), "fireball_index is out of bounds!");
switch (fireball_index)
{
@@ -164,7 +160,7 @@ static void fireball_generate_unique_id(char *unique_id, int buffer_len, int fir
*/
static void fireball_set_default_color(int idx)
{
Assert((idx >= 0) && (idx < MAX_FIREBALL_TYPES));
Assertion(SCP_vector_inbounds(Fireball_info, idx), "idx is out of bounds!");
switch (idx)
{
@@ -200,7 +196,7 @@ static void fireball_set_default_color(int idx)
static void fireball_set_default_warp_attributes(int idx)
{
Assert((idx >= 0) && (idx < MAX_FIREBALL_TYPES));
Assertion(SCP_vector_inbounds(Fireball_info, idx), "idx is out of bounds!");
switch (idx)
{
@@ -232,9 +228,9 @@ void fireball_info_clear(fireball_info *fb)
int fireball_info_lookup(const char *unique_id)
{
for (int i = 0; i < Num_fireball_types; ++i)
for (size_t i = 0; i < Fireball_info.size(); ++i)
if (!stricmp(Fireball_info[i].unique_id, unique_id))
return i;
return static_cast<int>(i);
return -1;
}
@@ -308,25 +304,19 @@ static void parse_fireball_tbl(const char *table_filename)
// we are creating a new entry, so set some defaults
else
{
// make sure we don't exceed the max
if (Num_fireball_types >= MAX_FIREBALL_TYPES)
{
error_display(0, "Too many fireball entries! Max is %d", MAX_FIREBALL_TYPES);
return;
}
fi = &Fireball_info[Num_fireball_types];
int new_fireball_index = static_cast<int>(Fireball_info.size());
Fireball_info.emplace_back();
fi = &Fireball_info.back();
fireball_info_clear(fi);
// If the table didn't specify a unique ID, generate one. This will be assigned a few lines later.
if (strlen(unique_id) == 0)
fireball_generate_unique_id(unique_id, NAME_LENGTH, Num_fireball_types);
fireball_generate_unique_id(unique_id, NAME_LENGTH, new_fireball_index);
// Set remaining fireball defaults
fireball_set_default_color(Num_fireball_types);
fireball_set_default_warp_attributes(Num_fireball_types);
fireball_set_default_color(new_fireball_index);
fireball_set_default_warp_attributes(new_fireball_index);
Num_fireball_types++;
first_time = true;
}
@@ -399,9 +389,9 @@ void fireball_parse_tbl()
if (fireballs_parsed)
return;
// every newly parsed fireball_info will get cleared before being added
// must do this outside of parse_fireball_tbl because it's called twice
Num_fireball_types = 0;
// every newly parsed fireball_info will get cleared before being added;
// must clear the vector outside of parse_fireball_tbl because it's called more than once
Fireball_info.clear();
parse_fireball_tbl("fireball.tbl");
@@ -436,10 +426,11 @@ void fireball_parse_tbl()
void fireball_load_data()
{
int i, idx;
int i, n, idx;
fireball_info *fd;
for ( i = 0; i < Num_fireball_types; i++ ) {
n = static_cast<int>(Fireball_info.size());
for ( i = 0; i < n; i++ ) {
fd = &Fireball_info[i];
for(idx=0; idx<fd->lod_count; idx++){
@@ -798,8 +789,7 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
object *obj;
fireball_info *fd;
fireball_lod *fl;
Assert( fireball_type > -1 );
Assert( fireball_type < Num_fireball_types );
Assertion(SCP_vector_inbounds(Fireball_info, fireball_type), "fireball_type is out of bounds!");
fd = &Fireball_info[fireball_type];
@@ -946,13 +936,14 @@ void fireball_close()
void fireballs_page_in()
{
int i, idx;
int i, n, idx;
fireball_info *fd;
for ( i = 0; i < Num_fireball_types; i++ ) {
n = static_cast<int>(Fireball_info.size());
for ( i = 0; i < n; i++ ) {
fd = &Fireball_info[i];
if((i < NUM_DEFAULT_FIREBALLS) || fireball_used[i]) {
if ((i < NUM_DEFAULT_FIREBALLS) || fd->fireball_used) {
// if this is a Knossos ani, only load if Knossos_warp_ani_used is true
if ( (i == FIREBALL_KNOSSOS) && !Knossos_warp_ani_used)
continue;
@@ -984,8 +975,8 @@ void fireball_get_color(int idx, float *red, float *green, float *blue)
{
Assert( red && blue && green );
if ( (idx < 0) || (idx >= Num_fireball_types) ) {
Int3();
if (!SCP_vector_inbounds(Fireball_info, idx)) {
UNREACHABLE("idx is out of bounds!");
*red = 1.0f;
*green = 1.0f;
@@ -1120,3 +1111,16 @@ int fireball_get_count()
return count;
}
void stuff_fireball_index_list(SCP_vector<int> &list, const char *name)
{
stuff_int_list(list, RAW_INTEGER_TYPE);
list.erase(std::remove_if(list.begin(), list.end(), [&](int index) {
if (!SCP_vector_inbounds(Fireball_info, index)) {
Warning(LOCATION, "Fireball index %d for %s was out of bounds. Removing this index.", index, name);
return true;
}
return false;
}), list.end());
}
+5 -5
View File
@@ -33,15 +33,12 @@ class asteroid_info;
#define FIREBALL_EXPLOSION_LARGE1 4 // Used for the big explosion when a ship breaks into pieces
#define FIREBALL_EXPLOSION_LARGE2 5 // Used for the big explosion when a ship breaks into pieces
#define MAX_FIREBALL_TYPES 32 // The maximum number of fireballs that can be defined
#define NUM_DEFAULT_FIREBALLS 6
#define MAX_FIREBALL_LOD 4
#define FIREBALL_NUM_LARGE_EXPLOSIONS 2
extern int fireball_used[MAX_FIREBALL_TYPES];
// all this moved here by Goober5000 because it makes more sense in the H file
typedef struct fireball_lod {
char filename[MAX_FILENAME_LEN];
@@ -57,6 +54,7 @@ typedef struct fireball_info {
float exp_color[3]; // red, green, blue
bool use_3d_warp;
bool fireball_used;
char warp_glow[NAME_LENGTH];
int warp_glow_bitmap;
@@ -66,8 +64,7 @@ typedef struct fireball_info {
int warp_model_id;
} fireball_info;
extern fireball_info Fireball_info[MAX_FIREBALL_TYPES];
extern int Num_fireball_types;
extern SCP_vector<fireball_info> Fireball_info;
// flag values for fireball struct flags member
#define FBF_WARP_CLOSE_SOUND_PLAYED (1<<0)
@@ -153,4 +150,7 @@ extern bool Fireball_warp_flash;
// Cyborg - get a count of how many valid fireballs are in the mission.
int fireball_get_count();
// Goober5000 - stuffs fireballs and checks that indexes are in bounds
void stuff_fireball_index_list(SCP_vector<int> &list, const char *name);
#endif /* _FIREBALLS_H */
+2 -2
View File
@@ -132,8 +132,8 @@ void parse_ssm(const char *filename)
{
int temp = atoi(unique_id);
if ((temp < 0) || (temp >= Num_fireball_types))
error_display(0, "Fireball index [%d] out of range (should be 0-%d) for SSM strike [%s]", temp, Num_fireball_types - 1, s->name);
if (!SCP_vector_inbounds(Fireball_info, temp))
error_display(0, "Fireball index [%d] out of range (should be 0-%d) for SSM strike [%s]", temp, static_cast<int>(Fireball_info.size()) - 1, s->name);
else
s->fireball_type = temp;
}
+28 -7
View File
@@ -3064,13 +3064,16 @@ const char* get_lookup_type_name(int lookup_type)
return "Unknown lookup type, tell a coder!";
}
// Stuffs an integer list.
// This is of the form ( i* )
// where i is an integer.
// For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
size_t stuff_int_list(int *ilp, size_t max_ints, int lookup_type)
// use a functor here so that we don't need to re-roll the parsing function for both variants of stuff_int_lists
struct StuffIntListParser
{
return stuff_token_list(ilp, max_ints, [&](int *buf)->bool {
int lookup_type;
StuffIntListParser(int _lookup_type)
: lookup_type(_lookup_type)
{}
bool operator()(int* buf) {
if (*Mp == '"') {
int num = 0;
bool valid_negative = false;
@@ -3123,7 +3126,25 @@ size_t stuff_int_list(int *ilp, size_t max_ints, int lookup_type)
}
return true;
}, get_lookup_type_name(lookup_type));
}
};
// Stuffs an integer list.
// This is of the form ( i* )
// where i is an integer.
// For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
size_t stuff_int_list(int *ilp, size_t max_ints, int lookup_type)
{
return stuff_token_list(ilp, max_ints, StuffIntListParser(lookup_type), get_lookup_type_name(lookup_type));
}
// Stuffs an integer list.
// This is of the form ( i* )
// where i is an integer.
// For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
void stuff_int_list(SCP_vector<int> &ilp, int lookup_type)
{
stuff_token_list(ilp, StuffIntListParser(lookup_type), get_lookup_type_name(lookup_type));
}
// Karajorma/Goober5000 - Stuffs a loadout list by parsing a list of ship or weapon choices.
+1
View File
@@ -234,6 +234,7 @@ void stuff_flagset(T *dest) {
}
extern size_t stuff_int_list(int *ilp, size_t max_ints, int lookup_type = RAW_INTEGER_TYPE);
extern void stuff_int_list(SCP_vector<int> &ilp, int lookup_type = RAW_INTEGER_TYPE);
extern size_t stuff_float_list(float* flp, size_t max_floats);
extern void stuff_float_list(SCP_vector<float>& flp);
extern size_t stuff_vec3d_list(vec3d *vlp, size_t max_vecs);
+3 -3
View File
@@ -3995,7 +3995,7 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, s
if (type2 == SEXP_ATOM_NUMBER || can_construe_as_integer(CTEXT(node)))
{
int num = atoi(CTEXT(node));
if (num < 0 || num >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, num))
{
return SEXP_CHECK_NUM_RANGE_INVALID;
}
@@ -14234,7 +14234,7 @@ void sexp_explosion_effect(int n)
{
fireball_type = FIREBALL_EXPLOSION_LARGE2;
}
else if (num >= Num_fireball_types)
else if (!SCP_vector_inbounds(Fireball_info, num))
{
Warning(LOCATION, "explosion-effect fireball type is out of range; quitting the explosion...\n");
return;
@@ -14431,7 +14431,7 @@ void sexp_warp_effect(int n)
{
fireball_type = FIREBALL_KNOSSOS;
}
else if (num >= Num_fireball_types)
else if (!SCP_vector_inbounds(Fireball_info, num))
{
Warning(LOCATION, "warp-effect fireball type is out of range; quitting the warp...\n");
return;
+1 -1
View File
@@ -220,7 +220,7 @@ ADE_FUNC(__len, l_Tables_FireballClasses, NULL, "Number of fireball classes", "n
if (!fireballs_inited)
return ade_set_args(L, "i", 0);
return ade_set_args(L, "i", Num_fireball_types);
return ade_set_args(L, "i", static_cast<int>(Fireball_info.size()));
}
//*****SUBLIBRARY: Tables/SimulatedSpeechOverrides
+9 -9
View File
@@ -18,7 +18,7 @@ ADE_FUNC(__tostring, l_Fireballclass, NULL, "Fireball class name", "string", "Fi
if(!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ade_set_error(L, "s", "");
if(idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ade_set_error(L, "s", "");
return ade_set_args(L, "s", Fireball_info[idx].unique_id);
@@ -30,10 +30,10 @@ ADE_FUNC(__eq, l_Fireballclass, "fireballclass, fireballclass", "Checks if the t
if(!ade_get_args(L, "oo", l_Fireballclass.Get(&idx1), l_Fireballclass.Get(&idx2)))
return ade_set_error(L, "b", false);
if(idx1 < 0 || idx1 >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx1))
return ade_set_error(L, "b", false);
if(idx2 < 0 || idx2 >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx2))
return ade_set_error(L, "b", false);
return ade_set_args(L, "b", idx1 == idx2);
@@ -45,7 +45,7 @@ ADE_VIRTVAR(UniqueID, l_Fireballclass, "string", "Fireball class name", "string"
if(!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ade_set_error(L, "s", "");
if(idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ade_set_error(L, "s", "");
if (ADE_SETTING_VAR)
@@ -60,7 +60,7 @@ ADE_VIRTVAR(Filename, l_Fireballclass, NULL, "Fireball class animation filename
if(!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ade_set_error(L, "s", "");
if(idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ade_set_error(L, "s", "");
//Currently not settable as the bitmaps are only loaded once at level start
@@ -76,7 +76,7 @@ ADE_VIRTVAR(NumberFrames, l_Fireballclass, NULL, "Amount of frames the animation
if(!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ade_set_error(L, "i", -1);
if(idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ade_set_error(L, "i", -1);
if (ADE_SETTING_VAR)
@@ -91,7 +91,7 @@ ADE_VIRTVAR(FPS, l_Fireballclass, NULL, "The FPS with which this fireball's anim
if (!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ade_set_error(L, "i", -1);
if (idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ade_set_error(L, "i", -1);
if (ADE_SETTING_VAR)
@@ -106,7 +106,7 @@ ADE_FUNC(isValid, l_Fireballclass, NULL, "Detects whether handle is valid", "boo
if(!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ADE_RETURN_NIL;
if(idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ADE_RETURN_FALSE;
return ADE_RETURN_TRUE;
@@ -118,7 +118,7 @@ ADE_FUNC(getTableIndex, l_Fireballclass, NULL, "Gets the index value of the fire
if(!ade_get_args(L, "o", l_Fireballclass.Get(&idx)))
return ade_set_args(L, "i", -1);
if(idx < 0 || idx >= Num_fireball_types)
if (!SCP_vector_inbounds(Fireball_info, idx))
return ade_set_args(L, "i", -1);
return ade_set_args(L, "i", idx + 1);
+10 -17
View File
@@ -3776,10 +3776,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
parse_game_sound("$Shockwave Sound:", &sci->blast_sound_id);
if(optional_string("$Explosion Animations:")){
int temp[MAX_FIREBALL_TYPES];
auto parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
sip->explosion_bitmap_anims.clear();
sip->explosion_bitmap_anims.insert(sip->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
stuff_fireball_index_list(sip->explosion_bitmap_anims, sci->name);
}
if (optional_string("$Weapon Model Draw Distance:")) {
@@ -5808,10 +5805,7 @@ static void parse_ship_type(const char *filename, const bool replace)
if(optional_string("$Explosion Animations:"))
{
int temp[MAX_FIREBALL_TYPES];
auto parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
stp->explosion_bitmap_anims.clear();
stp->explosion_bitmap_anims.insert(stp->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
stuff_fireball_index_list(stp->explosion_bitmap_anims, stp->name);
}
auto skip_str = "$Skip Death Roll Percent Chance:";
@@ -18142,7 +18136,8 @@ void ship_page_in()
int num_ship_types_used = 0;
int test_id __UNUSED = -1;
memset( fireball_used, 0, sizeof(int) * MAX_FIREBALL_TYPES );
for (auto& fi : Fireball_info)
fi.fireball_used = false;
i = 0;
for (auto sip = Ship_info.begin(); sip != Ship_info.end(); i++, ++sip) {
@@ -18256,15 +18251,13 @@ void ship_page_in()
// Page in the shockwave stuff. -C
shockwave_create_info_load(&sip->shockwave);
if(!sip->explosion_bitmap_anims.empty()) {
int num_fireballs = (int)sip->explosion_bitmap_anims.size();
for(j = 0; j < num_fireballs; j++){
fireball_used[sip->explosion_bitmap_anims[j]] = 1;
if (!sip->explosion_bitmap_anims.empty()) {
for (int explosion_bitmap_anim: sip->explosion_bitmap_anims) {
Fireball_info[explosion_bitmap_anim].fireball_used = true;
}
} else if(sip->class_type >= 0 && !Ship_types[sip->class_type].explosion_bitmap_anims.empty()) {
int num_fireballs = (int)Ship_types[sip->class_type].explosion_bitmap_anims.size();
for(j = 0; j < num_fireballs; j++){
fireball_used[Ship_types[sip->class_type].explosion_bitmap_anims[j]] = 1;
} else if (sip->class_type >= 0 && !Ship_types[sip->class_type].explosion_bitmap_anims.empty()) {
for (int explosion_bitmap_anim: Ship_types[sip->class_type].explosion_bitmap_anims) {
Fireball_info[explosion_bitmap_anim].fireball_used = true;
}
}
}
+2 -2
View File
@@ -2634,8 +2634,8 @@ int parse_weapon(int subtype, bool replace, const char *filename)
}
}
else {
if ((temp < 0) || (temp >= Num_fireball_types)) {
error_display(0, "Fireball index [%d] out of range (should be 0-%d) for LSSM weapon [%s].", temp, Num_fireball_types - 1, wip->name);
if (!SCP_vector_inbounds(Fireball_info, temp)) {
error_display(0, "Fireball index [%d] out of range (should be 0-%d) for LSSM weapon [%s].", temp, static_cast<int>(Fireball_info.size()) - 1, wip->name);
wip->lssm_warpeffect = FIREBALL_WARP;
}
else {
+2 -2
View File
@@ -7654,9 +7654,9 @@ sexp_list_item *sexp_tree::get_listing_opf_fireball()
{
sexp_list_item head;
for (int i = 0; i < Num_fireball_types; ++i)
for (const auto &fi: Fireball_info)
{
char *unique_id = Fireball_info[i].unique_id;
auto unique_id = fi.unique_id;
if (strlen(unique_id) > 0)
head.add_data(unique_id);
+2 -2
View File
@@ -73,8 +73,8 @@ BOOL warp_params_dlg::OnInitDialog()
ptr->ResetContent();
for (int i = 0; i < Num_warp_types; ++i)
ptr->AddString(Warp_types[i]);
for (int i = 0; i < Num_fireball_types; ++i)
ptr->AddString(Fireball_info[i].unique_id);
for (const auto &fi: Fireball_info)
ptr->AddString(fi.unique_id);
// find the params of the first marked ship
WarpParams *params = nullptr;
+2 -2
View File
@@ -5264,9 +5264,9 @@ sexp_list_item *sexp_tree::get_listing_opf_fireball()
{
sexp_list_item head;
for (int i = 0; i < Num_fireball_types; ++i)
for (const auto &fi: Fireball_info)
{
char *unique_id = Fireball_info[i].unique_id;
auto unique_id = fi.unique_id;
if (strlen(unique_id) > 0)
head.add_data(unique_id);