diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 8f0269f3ce..f0efa918ca 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -1083,8 +1083,6 @@ const float DEFAULT_ASK_HELP_HULL_PERCENT = 0.3f; // percent hull at which shi const float AWACS_HELP_HULL_HI = 0.75f; // percent hull at which ship will ask for help const float AWACS_HELP_HULL_LOW = 0.25f; // percent hull at which ship will ask for help -#define CLONE_UNIQUE_PTR(attribute) attribute = util::unique_copy(other.attribute.get(), true) - void ship_info::clone(const ship_info& other) { strcpy_s(name, other.name); diff --git a/code/utils/string_utils.h b/code/utils/string_utils.h index 707e9b8052..59949999f7 100644 --- a/code/utils/string_utils.h +++ b/code/utils/string_utils.h @@ -39,3 +39,5 @@ std::unique_ptr unique_copy(const char *str, bool null_if_empty); SCP_vm_unique_ptr vm_unique_copy(const char *str, bool null_if_empty); } // namespace util + +#define CLONE_UNIQUE_PTR(attribute) attribute = util::unique_copy(other.attribute.get(), true) diff --git a/code/weapon/weapon.h b/code/weapon/weapon.h index ecb122486f..1df50bf5f4 100644 --- a/code/weapon/weapon.h +++ b/code/weapon/weapon.h @@ -961,6 +961,7 @@ struct weapon_info bool has_display_name() const; void reset(); + void clone(const weapon_info& other); }; extern special_flag_def_list_new&> Weapon_Info_Flags[]; diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index f147f081c8..a7d094b364 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -66,6 +66,7 @@ #include "particle/volumes/PointVolume.h" #include "tracing/Monitor.h" #include "tracing/tracing.h" +#include "utils/string_utils.h" #include "weapon.h" #include "model/modelrender.h" @@ -10158,6 +10159,336 @@ void weapon_info::reset() this->on_create_program = actions::ProgramSet(); } +void weapon_info::clone(const weapon_info& other) +{ + strcpy_s(name, other.name); + display_name = other.display_name; + strcpy_s(title, other.title); + CLONE_UNIQUE_PTR(desc); + strcpy_s(altSubsysName, other.altSubsysName); + + strcpy_s(pofbitmap_name, other.pofbitmap_name); + model_num = other.model_num; + strcpy_s(external_model_name, other.external_model_name); + external_model_num = other.external_model_num; + + CLONE_UNIQUE_PTR(tech_desc); + strcpy_s(tech_anim_filename, other.tech_anim_filename); + strcpy_s(tech_title, other.tech_title); + strcpy_s(tech_model, other.tech_model); + + hud_target_lod = other.hud_target_lod; + num_detail_levels = other.num_detail_levels; + memcpy(detail_distance, other.detail_distance, sizeof(detail_distance)); + subtype = other.subtype; + render_type = other.render_type; + + closeup_pos = other.closeup_pos; + closeup_zoom = other.closeup_zoom; + icon_closeup_pos = other.icon_closeup_pos; + icon_closeup_zoom = other.icon_closeup_zoom; + + strcpy_s(hud_filename, other.hud_filename); + hud_image_index = other.hud_image_index; + + laser_bitmap = other.laser_bitmap; + laser_headon_bitmap = other.laser_headon_bitmap; + laser_glow_bitmap = other.laser_glow_bitmap; + laser_glow_headon_bitmap = other.laser_glow_headon_bitmap; + laser_headon_switch_rate = other.laser_headon_switch_rate; + laser_headon_switch_ang = other.laser_headon_switch_ang; + + laser_length = other.laser_length; + laser_length_by_frametime = other.laser_length_by_frametime; + bitmap_color = other.bitmap_color; + laser_color_1 = other.laser_color_1; + laser_color_2 = other.laser_color_2; + laser_head_radius = other.laser_head_radius; + laser_tail_radius = other.laser_tail_radius; + laser_glow_length_scale = other.laser_glow_length_scale; + laser_glow_head_scale = other.laser_glow_head_scale; + laser_glow_tail_scale = other.laser_glow_tail_scale; + laser_min_pixel_size = other.laser_min_pixel_size; + laser_pos_offset = other.laser_pos_offset; + + collision_radius_override = other.collision_radius_override; + + light_color_set = other.light_color_set; + light_color = other.light_color; + light_radius = other.light_radius; + + max_speed = other.max_speed; + acceleration_time = other.acceleration_time; + vel_inherit_amount = other.vel_inherit_amount; + free_flight_time = other.free_flight_time; + free_flight_speed_factor = other.free_flight_speed_factor; + gravity_const = other.gravity_const; + mass = other.mass; + fire_wait = other.fire_wait; + max_delay = other.max_delay; + min_delay = other.min_delay; + + damage = other.damage; + damage_time = other.damage_time; + atten_damage = other.atten_damage; + damage_incidence_max = other.damage_incidence_max; + damage_incidence_min = other.damage_incidence_min; + + shockwave = other.shockwave; + dinky_shockwave = other.dinky_shockwave; + + arm_time = other.arm_time; + arm_dist = other.arm_dist; + arm_radius = other.arm_radius; + det_range = other.det_range; + det_radius = other.det_radius; + proximity_radius = other.proximity_radius; + proximity_iff = other.proximity_iff; + proximity_species = other.proximity_species; + proximity_type = other.proximity_type; + proximity_class = other.proximity_class; + proximity_relation_mask = other.proximity_relation_mask; + proximity_detonate_chance = other.proximity_detonate_chance; + proximity_stealth_multiplier = other.proximity_stealth_multiplier; + mine_sensors_range = other.mine_sensors_range; + mine_targetable_range = other.mine_targetable_range; + mine_chase_duration = other.mine_chase_duration; + mine_detonates_on_chase_timeout = other.mine_detonates_on_chase_timeout; + mine_chase_cooldown = other.mine_chase_cooldown; + flak_detonation_accuracy = other.flak_detonation_accuracy; + flak_targeting_accuracy = other.flak_targeting_accuracy; + untargeted_flak_range_penalty = other.untargeted_flak_range_penalty; + + armor_factor = other.armor_factor; + shield_factor = other.shield_factor; + subsystem_factor = other.subsystem_factor; + + life_min = other.life_min; + life_max = other.life_max; + lifetime = other.lifetime; + + energy_consumed = other.energy_consumed; + + wi_flags = other.wi_flags; + + turn_time = other.turn_time; + turn_accel_time = other.turn_accel_time; + cargo_size = other.cargo_size; + autoaim_fov = other.autoaim_fov; + rearm_rate = other.rearm_rate; + disallow_rearm = other.disallow_rearm; + reloaded_per_batch = other.reloaded_per_batch; + weapon_range = other.weapon_range; + optimum_range = other.optimum_range; + weapon_min_range = other.weapon_min_range; + + aiming_flags = other.aiming_flags; + minimum_convergence_distance = other.minimum_convergence_distance; + convergence_distance = other.convergence_distance; + + pierce_objects = other.pierce_objects; + spawn_children_on_pierce = other.spawn_children_on_pierce; + + num_spawn_weapons_defined = other.num_spawn_weapons_defined; + maximum_children_spawned = other.maximum_children_spawned; + memcpy(spawn_info, other.spawn_info, sizeof(spawn_info)); + + lifetime_variation_factor_when_child = other.lifetime_variation_factor_when_child; + + swarm_count = other.swarm_count; + SwarmWait = other.SwarmWait; + + target_restrict = other.target_restrict; + target_restrict_objecttypes = other.target_restrict_objecttypes; + multi_lock = other.multi_lock; + max_seeking = other.max_seeking; + max_seekers_per_target = other.max_seekers_per_target; + + ship_restrict = other.ship_restrict; + ship_restrict_strings = other.ship_restrict_strings; + + trigger_lock = other.trigger_lock; + launch_reset_locks = other.launch_reset_locks; + + auto_target_method = other.auto_target_method; + + acquire_method = other.acquire_method; + min_lock_time = other.min_lock_time; + lock_pixels_per_sec = other.lock_pixels_per_sec; + catchup_pixels_per_sec = other.catchup_pixels_per_sec; + catchup_pixel_penalty = other.catchup_pixel_penalty; + lock_fov = other.lock_fov; + + fov = other.fov; + + seeker_strength = other.seeker_strength; + + pre_launch_snd = other.pre_launch_snd; + pre_launch_snd_min_interval = other.pre_launch_snd_min_interval; + launch_snd = other.launch_snd; + cockpit_launch_snd = other.cockpit_launch_snd; + impact_snd = other.impact_snd; + disarmed_impact_snd = other.disarmed_impact_snd; + shield_impact_snd = other.shield_impact_snd; + flyby_snd = other.flyby_snd; + ambient_snd = other.ambient_snd; + start_firing_snd = other.start_firing_snd; + loop_firing_snd = other.loop_firing_snd; + linked_loop_firing_snd = other.linked_loop_firing_snd; + end_firing_snd = other.end_firing_snd; + + hud_tracking_snd = other.hud_tracking_snd; + hud_locked_snd = other.hud_locked_snd; + hud_in_flight_snd = other.hud_in_flight_snd; + in_flight_play_type = other.in_flight_play_type; + + tr_info = other.tr_info; + + strcpy_s(icon_filename, other.icon_filename); + strcpy_s(anim_filename, other.anim_filename); + selection_effect = other.selection_effect; + fs2_effect_grid_color = other.fs2_effect_grid_color; + fs2_effect_scanline_color = other.fs2_effect_scanline_color; + fs2_effect_grid_density = other.fs2_effect_grid_density; + fs2_effect_wireframe_color = other.fs2_effect_wireframe_color; + + shield_impact_effect_radius = other.shield_impact_effect_radius; + shield_impact_explosion_radius = other.shield_impact_explosion_radius; + + impact_weapon_expl_effect = other.impact_weapon_expl_effect; + dinky_impact_weapon_expl_effect = other.dinky_impact_weapon_expl_effect; + flash_impact_weapon_expl_effect = other.flash_impact_weapon_expl_effect; + + piercing_impact_effect = other.piercing_impact_effect; + piercing_impact_secondary_effect = other.piercing_impact_secondary_effect; + + conditional_impacts = other.conditional_impacts; + + muzzle_effect = other.muzzle_effect; + + state_effects = other.state_effects; + + emp_intensity = other.emp_intensity; + emp_time = other.emp_time; + + recoil_modifier = other.recoil_modifier; + + shudder_modifier = other.shudder_modifier; + + weapon_reduce = other.weapon_reduce; + afterburner_reduce = other.afterburner_reduce; + + vamp_regen = other.vamp_regen; + + tag_time = other.tag_time; + tag_level = other.tag_level; + + field_of_fire = other.field_of_fire; + fof_spread_rate = other.fof_spread_rate; + fof_reset_rate = other.fof_reset_rate; + max_fof_spread = other.max_fof_spread; + firing_pattern = other.firing_pattern; + shots = other.shots; + cycle_multishot = other.cycle_multishot; + + cs_num_fired = other.cs_num_fired; + cs_radius = other.cs_radius; + cs_twist = other.cs_twist; + cs_crotate = other.cs_crotate; + cs_delay = other.cs_delay; + cs_random_angle = other.cs_random_angle; + cs_angle = other.cs_angle; + + elec_time = other.elec_time; + elec_eng_mult = other.elec_eng_mult; + elec_weap_mult = other.elec_weap_mult; + elec_beam_mult = other.elec_beam_mult; + elec_sensors_mult = other.elec_sensors_mult; + elec_randomness = other.elec_randomness; + elec_use_new_style = other.elec_use_new_style; + + SSM_index = other.SSM_index; + + lssm_warpout_delay = other.lssm_warpout_delay; + lssm_warpin_delay = other.lssm_warpin_delay; + lssm_stage5_vel = other.lssm_stage5_vel; + lssm_warpin_radius = other.lssm_warpin_radius; + lssm_lock_range = other.lssm_lock_range; + lssm_warpeffect = other.lssm_warpeffect; + + b_info = other.b_info; + + particle_spewers = other.particle_spewers; + + cm_aspect_effectiveness = other.cm_aspect_effectiveness; + cm_heat_effectiveness = other.cm_heat_effectiveness; + cm_effective_rad = other.cm_effective_rad; + cm_detonation_rad = other.cm_detonation_rad; + cm_launch_vec = other.cm_launch_vec; + cm_launch_speed = other.cm_launch_speed; + cm_launch_variance = other.cm_launch_variance; + cm_kill_single = other.cm_kill_single; + cmeasure_timer_interval = other.cmeasure_timer_interval; + cmeasure_firewait = other.cmeasure_firewait; + cmeasure_use_firewait = other.cmeasure_use_firewait; + cmeasure_failure_delay_multiplier_ai = other.cmeasure_failure_delay_multiplier_ai; + cmeasure_success_delay_multiplier_ai = other.cmeasure_success_delay_multiplier_ai; + + weapon_submodel_rotate_accell = other.weapon_submodel_rotate_accell; + weapon_submodel_rotate_vel = other.weapon_submodel_rotate_vel; + + damage_type_idx = other.damage_type_idx; + damage_type_idx_sav = other.damage_type_idx_sav; + + armor_type_idx = other.armor_type_idx; + + alpha_max = other.alpha_max; + alpha_min = other.alpha_min; + alpha_cycle = other.alpha_cycle; + + weapon_hitpoints = other.weapon_hitpoints; + + burst_shots = other.burst_shots; + burst_delay = other.burst_delay; + burst_flags = other.burst_flags; + + thruster_flame = other.thruster_flame; + thruster_glow = other.thruster_glow; + thruster_glow_factor = other.thruster_glow_factor; + + target_lead_scaler = other.target_lead_scaler; + num_targeting_priorities = other.num_targeting_priorities; + memcpy(targeting_priorities, other.targeting_priorities, sizeof(targeting_priorities)); + + failure_rate = other.failure_rate; + CLONE_UNIQUE_PTR(failure_sub_name); + failure_sub = other.failure_sub; + + num_substitution_patterns = other.num_substitution_patterns; + memcpy(weapon_substitution_pattern, other.weapon_substitution_pattern, sizeof(weapon_substitution_pattern)); + memcpy(weapon_substitution_pattern_names, + other.weapon_substitution_pattern_names, + sizeof(weapon_substitution_pattern_names)); + + score = other.score; + + custom_data = other.custom_data; + + custom_strings = other.custom_strings; + + impact_decal = other.impact_decal; + + on_create_program = other.on_create_program; + + animations = other.animations; + + weapon_launch_curves = other.weapon_launch_curves; + weapon_curves = other.weapon_curves; + weapon_hit_curves = other.weapon_hit_curves; + beam_curves = other.beam_curves; + beam_hit_curves = other.beam_hit_curves; +} + const char* weapon_info::get_display_name() const { if (has_display_name()) diff --git a/test/src/weapon/weapons.cpp b/test/src/weapon/weapons.cpp index f0722c1a97..d507b065d4 100644 --- a/test/src/weapon/weapons.cpp +++ b/test/src/weapon/weapons.cpp @@ -4,6 +4,7 @@ #include "util/FSTestFixture.h" #include "util/test_util.h" +#include "utils/string_utils.h" #include "weapon/weapon.h" class WeaponsParseTest : public test::FSTestFixture { @@ -34,3 +35,39 @@ TEST_F(WeaponsParseTest, description_too_many_lines) { ASSERT_THROW(weapon_init(), os::dialogs::WarningException); } + +TEST(WeaponInfoCloneTest, copies_members) { + weapon_info src; + strcpy_s(src.name, "Template Laser"); + src.display_name = "Shown Name"; + src.desc = util::unique_copy("a description", true); + src.failure_sub_name = util::unique_copy("Sub Weapon", true); + src.detail_distance[2] = 1234; + strcpy_s(src.weapon_substitution_pattern_names[1], "Other Laser"); + src.spawn_info[1].spawn_count = 7; + src.damage = 42.5f; + src.wi_flags.set(Weapon::Info_Flags::Homing_heat); + src.proximity_iff = {1, 3}; + src.b_info.beam_iff_miss_factor[2] = {{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}}; + src.b_info.t5info.burst_rot_pattern = {0.25f}; + src.custom_data["key"] = "value"; + + weapon_info dest; + dest.clone(src); + + EXPECT_STREQ(dest.name, "Template Laser"); + EXPECT_EQ(dest.display_name, "Shown Name"); + ASSERT_NE(dest.desc, nullptr); + EXPECT_STREQ(dest.desc.get(), "a description"); + ASSERT_NE(dest.failure_sub_name, nullptr); + EXPECT_STREQ(dest.failure_sub_name.get(), "Sub Weapon"); + EXPECT_EQ(dest.detail_distance[2], 1234); + EXPECT_STREQ(dest.weapon_substitution_pattern_names[1], "Other Laser"); + EXPECT_EQ(dest.spawn_info[1].spawn_count, 7); + EXPECT_EQ(dest.damage, 42.5f); + EXPECT_TRUE(dest.wi_flags[Weapon::Info_Flags::Homing_heat]); + EXPECT_EQ(dest.proximity_iff, src.proximity_iff); + EXPECT_EQ(dest.b_info.beam_iff_miss_factor, src.b_info.beam_iff_miss_factor); + EXPECT_EQ(dest.b_info.t5info.burst_rot_pattern, src.b_info.t5info.burst_rot_pattern); + EXPECT_EQ(dest.custom_data, src.custom_data); +}