@@ -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.
39824002void 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 }
0 commit comments