Skip to content

Commit 4bc45b3

Browse files
authored
Merge pull request scp-fs2open#7459 from Goober5000/fix/comm_node
fix the Shivan Comm Node
2 parents 0018f55 + c3b8922 commit 4bc45b3

9 files changed

Lines changed: 109 additions & 37 deletions

File tree

code/mod_table/mod_table.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ float Shield_percent_skips_damage;
184184
float Min_radius_for_persistent_debris;
185185
bool Zero_radius_explosions_skip_fireballs;
186186
bool Render_insignias_as_decals;
187+
bool Link_special_point_subsystems_to_destroyed_submodels;
187188

188189

189190
#ifdef WITH_DISCORD
@@ -1649,6 +1650,10 @@ void parse_mod_table(const char *filename)
16491650
stuff_boolean(&Zero_radius_explosions_skip_fireballs);
16501651
}
16511652

1653+
if (optional_string("$Link special-point subsystems to -destroyed submodels:")) {
1654+
stuff_boolean(&Link_special_point_subsystems_to_destroyed_submodels);
1655+
}
1656+
16521657
// end of options ----------------------------------------
16531658

16541659
// if we've been through once already and are at the same place, force a move
@@ -1897,6 +1902,7 @@ void mod_table_reset()
18971902
Min_radius_for_persistent_debris = 50.0f;
18981903
Zero_radius_explosions_skip_fireballs = false;
18991904
Render_insignias_as_decals = false;
1905+
Link_special_point_subsystems_to_destroyed_submodels = false;
19001906
}
19011907

19021908
void mod_table_set_version_flags()

code/mod_table/mod_table.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ extern float Shield_percent_skips_damage;
206206
extern float Min_radius_for_persistent_debris;
207207
extern bool Zero_radius_explosions_skip_fireballs;
208208
extern bool Render_insignias_as_decals;
209+
extern bool Link_special_point_subsystems_to_destroyed_submodels;
209210

210211
void mod_table_init();
211212
void mod_table_post_process();

code/model/model.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ struct submodel_instance
136136
float shift_accel = 0.0f;
137137
TIMESTAMP stepped_translation_started;
138138

139-
bool blown_off = false; // If set, this subobject is blown off
139+
bool blown_off = false; // If set, this subobject is not rendered or used for collision
140140

141141
// These fields are the true standard reference for submodel rotation. They should seldom be read directly
142142
// and should almost never be written directly. In most cases, coders should prefer cur_angle and prev_angle.
@@ -433,7 +433,7 @@ class bsp_info
433433
public:
434434
bsp_info()
435435
: bsp_data_size(0), bsp_data(nullptr), collision_tree_index(-1),
436-
rad(0.0f), my_replacement(-1), i_replace(-1), num_live_debris(0),
436+
rad(0.0f), next_form(-1), prev_form(-1), num_live_debris(0),
437437
parent(-1), num_children(0), first_child(-1), next_sibling(-1), num_details(0),
438438
outline_buffer(nullptr), n_verts_outline(0), render_sphere_radius(0.0f), use_render_box(0), use_render_sphere(0)
439439
{
@@ -484,8 +484,8 @@ class bsp_info
484484
vec3d max; // The max point of this object's geometry
485485
vec3d bounding_box[8]; // calculated fron min/max
486486

487-
int my_replacement; // If not -1 this subobject is what should get rendered instead of this one
488-
int i_replace; // If this is not -1, then this subobject will replace i_replace when it is damaged
487+
int next_form; // If not -1, this submodel can transform into it
488+
int prev_form; // If not -1, another submodel that can transform into this one
489489

490490
int num_live_debris; // num live debris models assocaiated with a submodel
491491
int live_debris[MAX_LIVE_DEBRIS]; // array of live debris submodels for a submodel
@@ -1137,14 +1137,19 @@ extern int model_find_2d_bound_min(int model_num,matrix *orient, vec3d * pos,int
11371137
// rect.
11381138
int submodel_find_2d_bound_min(int model_num,int submodel, matrix *orient, vec3d * pos,int *x1, int *y1, int *x2, int *y2);
11391139

1140-
11411140
// Returns zero is x1,y1,x2,y2 are valid
11421141
// Returns 2 for point offscreen.
11431142
// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates!
11441143
// This function just looks at the radius, and not the orientation, so the
11451144
// bounding box won't change depending on the obj's orient.
11461145
int subobj_find_2d_bound(float radius, matrix *orient, vec3d * pos,int *x1, int *y1, int *x2, int *y2);
11471146

1147+
// Returns the index of the -destroyed version of a submodel name, if it exists
1148+
int submodel_find_destroyed_form(int model_num, const char *name_stem);
1149+
1150+
// Returns whether this submodel name is a -destroyed version
1151+
bool submodel_is_destroyed_form(const char *name);
1152+
11481153
// stats variables
11491154
#ifndef NDEBUG
11501155
extern int modelstats_num_polys;

code/model/modelread.cpp

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,7 +2088,7 @@ modelread_status read_model_file_no_subsys(polymodel * pm, const char* filename,
20882088
sm->flags.set(Model::Submodel_flags::Nocollide_this_only);
20892089
}
20902090

2091-
sm->flags.set(Model::Submodel_flags::Is_damaged, in(sm->name, "-destroyed"));
2091+
sm->flags.set(Model::Submodel_flags::Is_damaged, submodel_is_destroyed_form(sm->name));
20922092

20932093
break;
20942094
}
@@ -3333,22 +3333,16 @@ int model_load(const char* filename, ship_info* sip, ErrorType error_type, bool
33333333

33343334
// Set up the default values
33353335
for (i=0; i<pm->n_models; i++ ) {
3336-
pm->submodel[i].my_replacement = -1; // assume nothing replaces this
3337-
pm->submodel[i].i_replace = -1; // assume this doesn't replaces anything
3336+
pm->submodel[i].next_form = -1; // assume nothing replaces this
3337+
pm->submodel[i].prev_form = -1; // assume this doesn't replace anything
33383338
}
33393339

33403340
// Search for models that have destroyed versions
33413341
for (i=0; i<pm->n_models; i++ ) {
3342-
int j;
3343-
char destroyed_name[128];
3344-
3345-
strcpy_s( destroyed_name, pm->submodel[i].name );
3346-
strcat_s( destroyed_name, "-destroyed" );
3347-
for (j=0; j<pm->n_models; j++ ) {
3348-
if ( !stricmp( pm->submodel[j].name, destroyed_name )) {
3349-
pm->submodel[i].my_replacement = j;
3350-
pm->submodel[j].i_replace = i;
3351-
}
3342+
int j = submodel_find_destroyed_form(pm->id, pm->submodel[i].name);
3343+
if (j >= 0) {
3344+
pm->submodel[i].next_form = j;
3345+
pm->submodel[j].prev_form = i;
33523346
}
33533347

33543348
// Search for models with live debris
@@ -3366,12 +3360,8 @@ int model_load(const char* filename, ship_info* sip, ErrorType error_type, bool
33663360
Assert(pm->submodel[i].num_live_debris < MAX_LIVE_DEBRIS);
33673361
pm->submodel[i].live_debris[pm->submodel[i].num_live_debris++] = j;
33683362
pm->submodel[j].flags.set(Model::Submodel_flags::Is_live_debris);
3369-
3370-
// make sure live debris doesn't have a parent
3371-
pm->submodel[j].parent = -1;
33723363
}
33733364
}
3374-
33753365
}
33763366

33773367
// maybe generate vertex buffers
@@ -3554,9 +3544,17 @@ int model_create_instance(int objnum, int model_num)
35543544
}
35553545
pmi->id = open_slot;
35563546

3557-
if (pm->n_models > 0)
3547+
if (pm->n_models > 0) {
35583548
pmi->submodel = new submodel_instance[pm->n_models];
35593549

3550+
// "damaged" submodels (like -destroyed variants, or debris) are blown-off by default
3551+
for (int i = 0; i < pm->n_models; i++) {
3552+
if (pm->submodel[i].flags[Model::Submodel_flags::Is_damaged]) {
3553+
pmi->submodel[i].blown_off = true;
3554+
}
3555+
}
3556+
}
3557+
35603558
// add intrinsic_motion instances if this model is intrinsic-moving
35613559
if (pm->flags & PM_FLAG_HAS_INTRINSIC_MOTION) {
35623560
intrinsic_motion motion(objnum >= 0, open_slot);
@@ -3977,6 +3975,28 @@ int subobj_find_2d_bound(float radius ,matrix * /*orient*/, vec3d * pos,int *x1,
39773975
return 0;
39783976
}
39793977

3978+
int submodel_find_destroyed_form(int model_num, const char *name_stem)
3979+
{
3980+
const auto pm = model_get(model_num);
3981+
Assertion(pm, "model_num must be valid!");
3982+
3983+
SCP_string destroyed_name(name_stem);
3984+
destroyed_name += "-destroyed";
3985+
3986+
return find_item_with_string(pm->submodel.get(), i2sz(pm->n_models), &bsp_info::name, destroyed_name);
3987+
}
3988+
3989+
bool submodel_is_destroyed_form(const char *name)
3990+
{
3991+
constexpr auto suffix = "-destroyed";
3992+
constexpr auto suffix_len = std::char_traits<char>::length(suffix);
3993+
3994+
auto len = strlen(name);
3995+
if (len <= suffix_len)
3996+
return false;
3997+
3998+
return stricmp(name + len - suffix_len, suffix) == 0;
3999+
}
39804000

39814001
// Given a rotating submodel, find the local and world axes of rotation.
39824002
void model_get_rotating_submodel_axis(vec3d *model_axis, vec3d *world_axis, const polymodel *pm, const polymodel_instance *pmi, int submodel_num, const matrix *objorient)
@@ -4836,8 +4856,9 @@ void model_get_moving_submodel_list(SCP_vector<int> &submodel_vector, const obje
48364856
const auto& child_submodel = pm->submodel[submodel];
48374857
const auto& child_submodel_instance = pmi->submodel[submodel];
48384858

4839-
// Don't check it or its children if it is destroyed or it is a replacement (non-moving)
4840-
if (child_submodel.flags[Model::Submodel_flags::No_collisions] || child_submodel_instance.blown_off || child_submodel.i_replace != -1) {
4859+
// Don't check it or its children if it is destroyed or it is a replacement
4860+
// (we currently assume replacements are -destroyed versions of submodels that might otherwise move)
4861+
if (child_submodel.flags[Model::Submodel_flags::No_collisions] || child_submodel_instance.blown_off || child_submodel.prev_form != -1) {
48414862
skipChildren = true;
48424863
return;
48434864
}
@@ -4964,7 +4985,10 @@ void model_set_up_techroom_instance(ship_info *sip, int model_instance_num)
49644985

49654986
model_iterate_submodel_tree(pm, pm->detail[0], [&](int submodel, int /*level*/, bool /*isLeaf*/)
49664987
{
4967-
model_replicate_submodel_instance(pm, pmi, submodel, empty);
4988+
auto sm = &pm->submodel[submodel];
4989+
4990+
if (sm->flags[Model::Submodel_flags::Can_move])
4991+
model_replicate_submodel_instance(pm, pmi, submodel, empty);
49684992
});
49694993
}
49704994

@@ -4985,17 +5009,21 @@ void model_replicate_submodel_instance_sub(polymodel *pm, polymodel_instance *pm
49855009

49865010
submodel_instance *smi = &pmi->submodel[submodel_num];
49875011
bsp_info *sm = &pm->submodel[submodel_num];
4988-
4989-
// Set the "blown out" flags.
5012+
5013+
// Set the "blown off" flags
49905014
if ( flags[Ship::Subsystem_Flags::No_disappear] ) {
49915015
smi->blown_off = false;
49925016
} else if ( copy_from ) {
49935017
smi->blown_off = copy_from->blown_off;
49945018
}
49955019

5020+
// In the future, we could expand the submodel instance to have a "blown_off_index"
5021+
// to indicate which form is currently visible, but for now, we'll follow the retail
5022+
// convention of having just two forms, the second of which is opposite from the first.
5023+
49965024
if ( smi->blown_off ) {
4997-
if ( sm->my_replacement >= 0 && !(flags[Ship::Subsystem_Flags::No_replace]) ) {
4998-
auto r_smi = &pmi->submodel[sm->my_replacement];
5025+
if ( sm->next_form >= 0 && !(flags[Ship::Subsystem_Flags::No_replace]) ) {
5026+
auto r_smi = &pmi->submodel[sm->next_form];
49995027
r_smi->blown_off = false;
50005028
if ( copy_from ) {
50015029
r_smi->cur_angle = copy_from->cur_angle;
@@ -5016,10 +5044,10 @@ void model_replicate_submodel_instance_sub(polymodel *pm, polymodel_instance *pm
50165044
}
50175045
}
50185046
} else {
5019-
// If submodel isn't yet blown off and has a -destroyed replacement model, we prevent
5020-
// the replacement model from being drawn by marking it as having been blown off
5021-
if ( sm->my_replacement >= 0 && sm->my_replacement != submodel_num) {
5022-
auto r_smi = &pmi->submodel[sm->my_replacement];
5047+
// If submodel isn't yet blown off and has a next form (like a -destroyed replacement model),
5048+
// we prevent the replacement model from being drawn by marking it as having been blown off
5049+
if ( sm->next_form >= 0 && sm->next_form != submodel_num) {
5050+
auto r_smi = &pmi->submodel[sm->next_form];
50235051
r_smi->blown_off = true;
50245052
}
50255053
}

code/model/modelreplace.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,11 @@ CHANGE_HELPER(change_submodel_numbers, bsp_info, int)
267267
for (auto& detail : input.details)
268268
REPLACE_IF_EQ(detail);
269269
REPLACE_IF_EQ(input.first_child);
270-
REPLACE_IF_EQ(input.i_replace);
270+
REPLACE_IF_EQ(input.prev_form);
271271
for (auto& debris : input.live_debris)
272272
REPLACE_IF_EQ(debris);
273273
REPLACE_IF_EQ(input.look_at_submodel);
274-
REPLACE_IF_EQ(input.my_replacement);
274+
REPLACE_IF_EQ(input.next_form);
275275
REPLACE_IF_EQ(input.next_sibling);
276276
REPLACE_IF_EQ(input.parent);
277277
CHANGE_HELPER_END

code/parse/sexp.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15762,6 +15762,9 @@ void set_subsys_strength_and_maybe_ancestors(ship *shipp, ship_subsys *ss, polym
1576215762
if (ss->submodel_instance_2)
1576315763
ss->submodel_instance_2->blown_off = false;
1576415764

15765+
// special case for subsystems that don't correspond to a submodel
15766+
check_subsystem_submodel_link(shipp, ss, false);
15767+
1576515768
// see if we are handling ancestors and if this subsystem has a submodel
1576615769
int subobj = ss->system_info->subobj_num;
1576715770
if (repair_ancestors && subobj >= 0)

code/ship/shiphit.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,28 @@ static bool is_subsys_destroyed(ship *shipp, int submodel)
114114
return false;
115115
}
116116

117+
void check_subsystem_submodel_link(const ship *shipp, const ship_subsys *subsys, bool was_destroyed)
118+
{
119+
if (!Link_special_point_subsystems_to_destroyed_submodels)
120+
return;
121+
122+
Assertion(shipp && subsys, "the ship and subsystem must exist!");
123+
auto pmi = model_get_instance(shipp->model_instance_num);
124+
Assertion(pmi, "the ship's model instance must exist!");
125+
126+
// check subsystem-submodel link, but only for special-point subsystems
127+
// (not subsystems corresponding to a submodel)
128+
auto psub = subsys->system_info;
129+
if (psub->subobj_num >= 0)
130+
return;
131+
int j = submodel_find_destroyed_form(pmi->model_num, psub->subobj_name);
132+
if (j < 0)
133+
return;
134+
135+
// show the submodel, or not, depending on what happened to the subsystem
136+
pmi->submodel[j].blown_off = !was_destroyed;
137+
}
138+
117139
// do_subobj_destroyed_stuff is called when a subobject for a ship is killed. Separated out
118140
// to separate function on 10/15/97 by MWA for easy multiplayer access. It does all of the
119141
// cool things like blowing off the model (if applicable, writing the logs, etc)
@@ -346,6 +368,9 @@ void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, const vec3d*
346368
if ((psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >= 0)) {
347369
subsys->submodel_instance_2->blown_off = true;
348370
}
371+
372+
// special case for subsystems that don't correspond to a submodel
373+
check_subsystem_submodel_link(ship_p, subsys, true);
349374
}
350375

351376
if (notify && !no_explosion) {

code/ship/shiphit.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ constexpr float DEATHROLL_ROTVEL_CAP = 6.3f; // maximum added deathroll rotve
3232
// of whoever is calling these functions. These functions are strictly
3333
// for damaging ship's hulls, shields, and subsystems. Nothing more.
3434

35+
// handle a -destroyed submodel in the special case where it is related to a special-point subsystem
36+
// (the usual submodel-submodel case is handled through the normal code paths)
37+
void check_subsystem_submodel_link(const ship *shipp, const ship_subsys *subsys, bool was_destroyed);
38+
3539
// function to destroy a subsystem. Called internally and from multiplayer messaging code
3640
extern void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, const vec3d *hitpos, bool no_explosion = false );
3741

freespace2/freespace.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6732,7 +6732,7 @@ void game_spew_pof_info_sub(int model_num, polymodel *pm, int sm, CFILE *out, in
67326732

67336733
// find the # of faces for this _individual_ object
67346734
total = submodel_get_num_polys(model_num, sm);
6735-
if(strstr(pm->submodel[sm].name, "-destroyed")){
6735+
if (submodel_is_destroyed_form(pm->submodel[sm].name)) {
67366736
sub_total_destroyed = total;
67376737
}
67386738

0 commit comments

Comments
 (0)