View Issue Details

IDProjectCategoryView StatusLast Update
0003083FSSCPtablespublic2015-08-31 05:02
ReporterMageKing17 Assigned ToMageKing17  
PrioritylowSeverityfeatureReproducibilityN/A
Status resolvedResolutionfixed 
Target Version3.7.4Fixed in Version3.7.3 
Summary0003083: Working implementation of ship templates.
DescriptionI guess I was bored, because I wrote a(n apparently working) copy operator for ship_info and restored ship templates. Tested (including templates copying templates) and apparently working. Corrected a couple of other minor issues in ship.cpp while I was at it. Patch file attached.
TagsNo tags attached.

Relationships

related to 0002064 closedTurey Ship template tech descriptions not showing up correctly, debug crash 

Activities

MageKing17

2014-08-05 23:25

developer  

template_test-shp.tbm (6,006 bytes)

MageKing17

2014-08-05 23:30

developer   ~0016176

I've attached the .tbm I used to test templates-inheriting-templates (basically just a modified version of the http://hard-light.net/wiki/index.php/Ship_Templates example on the wiki). I combined it with a retail ships.tbl with those two ships removed, but that part isn't necessary (unless you want to confirm that the retail data isn't being loaded). I also tested the example by itself, of course.

If anybody has any ideas for changes to the ship template system, now would be a good time, since there's nothing to support backwards-compatibility-wise yet.

MageKing17

2014-08-06 00:25

developer   ~0016177

Last edited: 2014-08-06 00:58

Aaaand fixed a silly bug I introduced into the patch after I last tested it but before I uploaded it.

EDIT: And after further reflection, changed an Int3() in the reintroduced code into an error_display(), and turned the second, redundant check for a pre-existing template into an Assertion().

Goober5000

2014-08-06 01:39

administrator   ~0016178

lol wut

I'm impressed, that ship_info copy method would have been a lot of tedious work to set up. We'll need to take a look at this carefully to double-check that child classes are copied properly and that nothing that has class stuff (member functions and member classes) is memcpy'd. And we need to check that for all of ship_info's descendant data structures too.

MageKing17

2014-10-23 00:08

developer   ~0016345

Patch updated; now cleans up after itself (frees memory allocated to ship templates after parsing is finished), and also removed a redundant for loop in ship_close() while I was at it.

Goober5000

2014-10-23 03:23

administrator   ~0016346

:yes:

This is still on my to-do list, unless someone else can review it first.

MageKing17

2014-10-31 22:01

developer   ~0016367

New version of the patch makes a couple minor changes; no longer relying on the broken behavior of the default copy constructor to avoid leaking memory in parse_ship_template() (which is to say, I don't think it was a problem before, but it could have turned into one later if ship_info changed).

I almost went whole hog and gave ship_info proper constructors and a destructor, but that would've required turning Ship_info into a vector and getting rid of MAX_SHIP_CLASSES, which turns out to be a very involved change.

MageKing17

2014-12-23 19:32

developer  

ship.patch (45,760 bytes)   
Index: code/ship/ship.cpp
===================================================================
--- code/ship/ship.cpp	(revision 11204)
+++ code/ship/ship.cpp	(working copy)
@@ -162,6 +162,7 @@
 
 ship_info		Ship_info[MAX_SHIP_CLASSES];
 reinforcements	Reinforcements[MAX_REINFORCEMENTS];
+SCP_vector<ship_info>	Ship_templates;
 
 static char **tspecies_names = NULL;
 
@@ -655,7 +656,325 @@
 #define SHIP_MULTITEXT_LENGTH 4096
 #define DEFAULT_DELTA_BANK_CONST	0.5f
 
+#define CHECK_THEN_COPY(attribute) \
+do {\
+	if (other.attribute != NULL)\
+		attribute = vm_strdup( other.attribute );\
+} while(false)
 
+void ship_info::clone(const ship_info& other)
+{
+	strcpy_s(name, other.name);
+	strcpy_s(alt_name, other.alt_name);
+	strcpy_s(short_name, other.short_name);
+	species = other.species;
+	class_type = other.class_type;
+
+	CHECK_THEN_COPY(type_str);
+	CHECK_THEN_COPY(maneuverability_str);
+	CHECK_THEN_COPY(armor_str);
+	CHECK_THEN_COPY(manufacturer_str);
+	CHECK_THEN_COPY(desc);
+	CHECK_THEN_COPY(tech_desc);
+
+	strcpy_s(tech_title, other.tech_title);
+
+	CHECK_THEN_COPY(ship_length);
+	CHECK_THEN_COPY(gun_mounts);
+	CHECK_THEN_COPY(missile_banks);
+
+	strcpy_s(cockpit_pof_file, other.cockpit_pof_file);
+	cockpit_offset = other.cockpit_offset;
+	strcpy_s(pof_file, other.pof_file);
+	strcpy_s(pof_file_hud, other.pof_file_hud);
+	num_detail_levels = other.num_detail_levels;
+	memcpy(detail_distance, other.detail_distance, sizeof(int) * MAX_SHIP_DETAIL_LEVELS);
+
+	// I'm not sure if these three are A) a good idea or B) relevant at all. -MageKing17
+	cockpit_model_num = other.cockpit_model_num;
+	model_num = other.model_num;
+	model_num_hud = other.model_num_hud;
+
+	hud_target_lod = other.hud_target_lod;
+	density = other.density;
+	damp = other.damp;
+	rotdamp = other.rotdamp;
+	delta_bank_const = other.delta_bank_const;
+	max_vel = other.max_vel;
+	max_rotvel = other.max_rotvel;
+	rotation_time = other.rotation_time;
+	srotation_time = other.srotation_time;
+	max_rear_vel = other.max_rear_vel;
+	forward_accel = other.forward_accel;
+	forward_decel = other.forward_decel;
+	slide_accel = other.slide_accel;
+	slide_decel = other.slide_decel;
+
+	strcpy_s(warpin_anim, other.warpin_anim);
+	warpin_radius = other.warpin_radius;
+	warpin_snd_start = other.warpin_snd_start;
+	warpin_snd_end = other.warpin_snd_end;
+	warpin_speed = other.warpin_speed;
+	warpin_time = other.warpin_time;
+	warpin_decel_exp = other.warpin_decel_exp;
+	warpin_type = other.warpin_type;
+
+	strcpy_s(warpout_anim, other.warpout_anim);
+	warpout_radius = other.warpout_radius;
+	warpout_snd_start = other.warpout_snd_start;
+	warpout_snd_end = other.warpout_snd_end;
+	warpout_engage_time = other.warpout_engage_time;
+	warpout_speed = other.warpout_speed;
+	warpout_time = other.warpout_time;
+	warpout_accel_exp = other.warpout_accel_exp;
+	warpout_type = other.warpout_type;
+
+	warpout_player_speed = other.warpout_player_speed;
+
+	flags = other.flags;
+	flags2 = other.flags2;
+	ai_class = other.ai_class;
+	max_speed = other.max_speed;
+	min_speed = other.min_speed;
+	max_accel = other.max_accel;
+
+	collision_damage_type_idx = other.collision_damage_type_idx;
+	collision_physics = other.collision_physics;
+
+	memcpy(&shockwave, &other.shockwave, sizeof(shockwave_create_info));
+	explosion_propagates = other.explosion_propagates;
+	big_exp_visual_rad = other.big_exp_visual_rad;
+	prop_exp_rad_mult = other.prop_exp_rad_mult;
+	death_roll_r_mult = other.death_roll_r_mult;
+	death_fx_r_mult = other.death_fx_r_mult;
+	death_roll_time_mult = other.death_roll_time_mult;
+	death_roll_base_time = other.death_roll_base_time;
+	death_fx_count = other.death_fx_count;
+	shockwave_count = other.shockwave_count;
+	explosion_bitmap_anims = other.explosion_bitmap_anims;
+	vaporize_chance = other.vaporize_chance;
+
+	impact_spew = other.impact_spew;
+	damage_spew = other.damage_spew;
+	split_particles = other.split_particles;
+	knossos_end_particles = other.knossos_end_particles;
+	regular_end_particles = other.regular_end_particles;
+
+	debris_min_lifetime = other.debris_min_lifetime;
+	debris_max_lifetime = other.debris_max_lifetime;
+	debris_min_speed = other.debris_min_speed;
+	debris_max_speed = other.debris_max_speed;
+	debris_min_rotspeed = other.debris_min_rotspeed;
+	debris_max_rotspeed = other.debris_max_rotspeed;
+	debris_damage_type_idx = other.debris_damage_type_idx;
+	debris_min_hitpoints = other.debris_min_hitpoints;
+	debris_max_hitpoints = other.debris_max_hitpoints;
+	debris_damage_mult = other.debris_damage_mult;
+	debris_arc_percent = other.debris_arc_percent;
+
+	if ( other.n_subsystems > 0 ) {
+		if( n_subsystems < 1 ) {
+			subsystems = (model_subsystem *)vm_malloc(sizeof(model_subsystem) * other.n_subsystems );
+		} else {
+			subsystems = (model_subsystem *)vm_realloc(subsystems, sizeof(model_subsystem) * other.n_subsystems);
+		}
+		Assert( subsystems != NULL );
+
+		for ( int i = 0; i < other.n_subsystems; i++ ){
+			memcpy(&(subsystems[i]), &(other.subsystems[i]), sizeof(model_subsystem));
+		}
+	}
+	n_subsystems = other.n_subsystems;
+
+	power_output = other.power_output;
+	max_overclocked_speed = other.max_overclocked_speed;
+	max_weapon_reserve = other.max_weapon_reserve;
+	max_shield_regen_per_second = other.max_shield_regen_per_second;
+	max_weapon_regen_per_second = other.max_weapon_regen_per_second;
+
+	afterburner_max_vel = other.afterburner_max_vel;
+	afterburner_forward_accel = other.afterburner_forward_accel;
+	afterburner_fuel_capacity = other.afterburner_fuel_capacity;
+	afterburner_burn_rate = other.afterburner_burn_rate;
+	afterburner_recover_rate = other.afterburner_recover_rate;
+	afterburner_max_reverse_vel = other.afterburner_max_reverse_vel;
+	afterburner_reverse_accel = other.afterburner_reverse_accel;
+
+	cmeasure_type = other.cmeasure_type;
+	cmeasure_max = other.cmeasure_max;
+
+	num_primary_banks = other.num_primary_banks;
+	memcpy(primary_bank_weapons, other.primary_bank_weapons, sizeof(int) * MAX_SHIP_PRIMARY_BANKS);
+	memcpy(primary_bank_ammo_capacity, other.primary_bank_ammo_capacity, sizeof(int) * MAX_SHIP_PRIMARY_BANKS);
+
+	num_secondary_banks = other.num_secondary_banks;
+	memcpy(secondary_bank_weapons, other.secondary_bank_weapons, sizeof(int) * MAX_SHIP_SECONDARY_BANKS);
+	memcpy(secondary_bank_ammo_capacity, other.secondary_bank_ammo_capacity, sizeof(int) * MAX_SHIP_SECONDARY_BANKS);
+
+	memcpy(draw_primary_models, other.draw_primary_models, sizeof(bool) * MAX_SHIP_PRIMARY_BANKS);
+	memcpy(draw_secondary_models, other.draw_secondary_models, sizeof(bool) * MAX_SHIP_SECONDARY_BANKS);
+	weapon_model_draw_distance = other.weapon_model_draw_distance;
+
+	max_hull_strength = other.max_hull_strength;
+	max_shield_strength = other.max_shield_strength;
+	auto_shield_spread = other.auto_shield_spread;
+	auto_shield_spread_bypass = other.auto_shield_spread_bypass;
+	auto_shield_spread_from_lod = other.auto_shield_spread_from_lod;
+
+	// ...Hmm. A memcpy() seems slightly overkill here, but I've settled into the pattern of "array gets memcpy'd", so... -MageKing17
+	memcpy(shield_point_augment_ctrls, other.shield_point_augment_ctrls, sizeof(int) * 4);
+
+	hull_repair_rate = other.hull_repair_rate;
+	subsys_repair_rate = other.subsys_repair_rate;
+
+	sup_hull_repair_rate = other.sup_hull_repair_rate;
+	sup_shield_repair_rate = other.sup_shield_repair_rate;
+	sup_subsys_repair_rate = other.sup_subsys_repair_rate;
+
+	closeup_pos = other.closeup_pos;
+	closeup_zoom = other.closeup_zoom;
+
+	memcpy(allowed_weapons, other.allowed_weapons, sizeof(int) * MAX_WEAPON_TYPES);
+
+	memcpy(restricted_loadout_flag, other.restricted_loadout_flag, sizeof(int) * MAX_SHIP_WEAPONS);
+	memcpy(allowed_bank_restricted_weapons, other.allowed_bank_restricted_weapons, sizeof(int) * MAX_SHIP_WEAPONS * MAX_WEAPON_TYPES);
+
+	shield_icon_index = other.shield_icon_index;
+	strcpy_s(icon_filename, other.icon_filename);
+	strcpy_s(anim_filename, other.anim_filename);
+	strcpy_s(overhead_filename, other.overhead_filename);
+	selection_effect = other.selection_effect;
+
+	bii_index_ship = other.bii_index_ship;
+	bii_index_ship_with_cargo = other.bii_index_ship_with_cargo;
+	bii_index_wing = other.bii_index_wing;
+	bii_index_wing_with_cargo = other.bii_index_wing_with_cargo;
+
+	score = other.score;
+
+	scan_time = other.scan_time;
+
+	memcpy(ct_info, other.ct_info, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
+	ct_count = other.ct_count;
+
+	num_nondark_colors = other.num_nondark_colors;
+	memcpy(nondark_colors, other.nondark_colors, sizeof(ubyte) * 3);
+
+	memcpy(shield_color, other.shield_color, sizeof(ubyte) * 3);
+
+	uses_team_colors = other.uses_team_colors;
+	default_team_name = other.default_team_name;
+
+	afterburner_trail = other.afterburner_trail;
+	afterburner_trail_width_factor = other.afterburner_trail_width_factor;
+	afterburner_trail_alpha_factor = other.afterburner_trail_alpha_factor;
+	afterburner_trail_life = other.afterburner_trail_life;
+	afterburner_trail_faded_out_sections = other.afterburner_trail_faded_out_sections;
+
+	normal_thruster_particles = other.normal_thruster_particles;
+	afterburner_thruster_particles = other.afterburner_thruster_particles;
+
+	memcpy(&thruster_flame_info, &other.thruster_flame_info, sizeof(thrust_pair));
+	memcpy(&thruster_glow_info, &other.thruster_glow_info, sizeof(thrust_pair));
+	memcpy(&thruster_secondary_glow_info, &other.thruster_secondary_glow_info, sizeof(thrust_pair_bitmap));
+	memcpy(&thruster_tertiary_glow_info, &other.thruster_tertiary_glow_info, sizeof(thrust_pair_bitmap));
+	memcpy(&thruster_distortion_info, &other.thruster_distortion_info, sizeof(thrust_pair_bitmap));
+
+	thruster01_glow_rad_factor = other.thruster01_glow_rad_factor;
+	thruster02_glow_rad_factor = other.thruster02_glow_rad_factor;
+	thruster03_glow_rad_factor = other.thruster03_glow_rad_factor;
+	thruster02_glow_len_factor = other.thruster02_glow_len_factor;
+	thruster_dist_rad_factor = other.thruster_dist_rad_factor;
+	thruster_dist_len_factor = other.thruster_dist_len_factor;
+
+	draw_distortion = other.draw_distortion;
+
+	splodeing_texture = other.splodeing_texture;
+	strcpy_s(splodeing_texture_name, other.splodeing_texture_name);
+
+	replacement_textures = other.replacement_textures;
+
+	armor_type_idx = other.armor_type_idx;
+	shield_armor_type_idx = other.shield_armor_type_idx;
+	
+	can_glide = other.can_glide;
+	glide_cap = other.glide_cap;
+	glide_dynamic_cap = other.glide_dynamic_cap;
+	glide_accel_mult = other.glide_accel_mult;
+	use_newtonian_damp = other.use_newtonian_damp;
+	newtonian_damp_override = other.newtonian_damp_override;
+
+	autoaim_fov = other.autoaim_fov;
+
+	topdown_offset_def = other.topdown_offset_def;
+	topdown_offset = other.topdown_offset;
+
+	engine_snd = other.engine_snd;
+	glide_start_snd = other.glide_start_snd;
+	glide_end_snd = other.glide_end_snd;
+
+	ship_sounds = other.ship_sounds;
+
+	num_maneuvering = other.num_maneuvering;
+	memcpy(maneuvering, other.maneuvering, sizeof(man_thruster) * MAX_MAN_THRUSTERS);
+
+	radar_image_2d_idx = other.radar_image_2d_idx;
+	radar_color_image_2d_idx = other.radar_color_image_2d_idx;
+	radar_image_size = other.radar_image_size;
+	radar_projection_size_mult = other.radar_projection_size_mult;
+
+	memcpy(ship_iff_info, other.ship_iff_info, sizeof(int) * MAX_IFFS * MAX_IFFS);
+
+	aiming_flags = other.aiming_flags;
+	minimum_convergence_distance = other.minimum_convergence_distance;
+	convergence_distance = other.convergence_distance;
+	convergence_offset = other.convergence_offset;
+
+	emp_resistance_mod = other.emp_resistance_mod;
+
+	piercing_damage_draw_limit = other.piercing_damage_draw_limit;
+
+	damage_lightning_type = other.damage_lightning_type;
+
+	hud_gauges = other.hud_gauges;
+	hud_enabled = other.hud_enabled;
+	hud_retail = other.hud_retail;
+
+	displays = other.displays;
+
+	pathMetadata = other.pathMetadata;
+}
+
+#define CHECK_THEN_FREE(attribute) \
+do {\
+	if (attribute != NULL) {\
+		vm_free(attribute);\
+		attribute = NULL;\
+	}\
+} while(false)
+
+void ship_info::free_strings()
+{
+	CHECK_THEN_FREE(type_str);
+	CHECK_THEN_FREE(maneuverability_str);
+	CHECK_THEN_FREE(armor_str);
+	CHECK_THEN_FREE(manufacturer_str);
+	CHECK_THEN_FREE(desc);
+	CHECK_THEN_FREE(tech_desc);
+	CHECK_THEN_FREE(ship_length);
+	CHECK_THEN_FREE(gun_mounts);
+	CHECK_THEN_FREE(missile_banks);
+}
+
+const ship_info &ship_info::operator= (const ship_info& other)
+{
+	if (this != &other) {
+		free_strings();
+		clone(other);
+	}
+	return *this;
+}
+
 /**
  * Writes default info to a ship entry
  * 
@@ -1101,14 +1420,90 @@
 
 	// Use a template for this ship.
 	if( optional_string( "+Use Template:" ) ) {
-		Warning(LOCATION, "Ignoring '+Use Template' field for '%s'.  Ship templates have been broken since they were added, and are not currently supported.", sip->name);
+		if ( !create_if_not_found ) {
+			Warning(LOCATION, "Both '+nocreate' and '+Use Template:' were specified for ship class '%s', ignoring '+Use Template:'\n", buf);
+		}
+		else {
+			char template_name[SHIP_MULTITEXT_LENGTH];
+			stuff_string(template_name, F_NAME, SHIP_MULTITEXT_LENGTH);
+			int template_id = ship_template_lookup(template_name);
+			if ( template_id != -1 ) {
+				first_time = false;
+				*sip = Ship_templates[template_id];
+				strcpy_s(sip->name, buf);
+			}
+			else {
+				Warning(LOCATION, "Unable to find ship template '%s' requested by ship class '%s', ignoring template request...", template_name, buf);
+			}
+		}
 	}
 
-	rtn = parse_ship_values(sip, first_time, replace);
+	rtn = parse_ship_values(sip, false, first_time, replace);
 
 	return rtn;	//0 for success
 }
 
+/**
+ * Parse the information for a specific ship type template.
+ */
+int parse_ship_template()
+{
+	char buf[SHIP_MULTITEXT_LENGTH];
+	ship_info *sip;
+	int rtn = 0;
+	bool first_time = false;
+
+	required_string("$Template:");
+	stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
+	
+	if( optional_string("+nocreate") ) {
+		Warning(LOCATION, "+nocreate flag used on ship template. Ship templates can not be modified. Ignoring +nocreate.");
+	}
+	
+	diag_printf ("Ship template name -- %s\n", buf);
+	//Check if the template exists already
+	int template_id;
+	template_id = ship_template_lookup( buf );
+	
+	if( template_id != -1 ) {
+		sip = &Ship_templates[template_id];
+		Warning(LOCATION, "WARNING: Ship template %s already exists. All ship template names must be unique.", sip->name);
+		if ( !skip_to_start_of_string_either("$Template:", "#End")) {
+			error_display(1, "Missing [#End] or [$Template] after duplicate entry for %s", sip->name);
+		}
+		return -1;
+	}
+	else {
+		
+		Ship_templates.push_back(ship_info());
+		sip = &Ship_templates.back();
+		
+		init_ship_entry(sip);
+		strcpy_s(sip->name, buf);
+		//Use another template for this template. This allows for template hierarchies. - Turey
+		if( optional_string("+Use Template:") ) {
+			char template_name[SHIP_MULTITEXT_LENGTH];
+			stuff_string(template_name, F_NAME, SHIP_MULTITEXT_LENGTH);
+			template_id = ship_template_lookup( template_name);
+			
+			if ( template_id != -1 ) {
+				first_time = false;
+				*sip = Ship_templates[template_id];
+				strcpy_s(sip->name, buf);
+			}
+			else {
+				Warning(LOCATION, "Unable to find ship template '%s' requested by ship template '%s', ignoring template request...", template_name, buf);
+			}
+		}
+	}
+	
+	rtn = parse_ship_values( sip, true, first_time, false );
+	
+	Assertion(ship_template_lookup(sip->name) == -1, "Somehow, despite checking for duplicate templates before parsing, we wound up with one (%s) anyway. Get a coder!\n", sip->name);
+	
+	return rtn;
+}
+
 void parse_ship_sound(char *name, GameSoundsIndex id, ship_info *sip)
 {
 	Assert( name != NULL );
@@ -1349,13 +1744,24 @@
 /**
  * Puts values into a ship_info.
  */
-int parse_ship_values(ship_info* sip, bool first_time, bool replace)
+int parse_ship_values(ship_info* sip, const bool is_template, const bool first_time, const bool replace)
 {
 	char buf[SHIP_MULTITEXT_LENGTH];
+	char* info_type_name;
+	char* type_name;
 	int i, j, num_allowed;
 	int allowed_weapons[MAX_WEAPON_TYPES];
 	int rtn = 0;
 	char name_tmp[NAME_LENGTH];
+
+	if ( ! is_template ) {
+		info_type_name = "Ship Class";
+		type_name = "$Name";
+	}
+	else {
+		info_type_name = "Ship Template";
+		type_name = "$Template";
+	}
 	
 	if(optional_string("$Alt name:"))
 		stuff_string(sip->alt_name, F_NAME, NAME_LENGTH);
@@ -1362,14 +1768,13 @@
 
 	if(optional_string("$Short name:"))
 		stuff_string(sip->short_name, F_NAME, NAME_LENGTH);
-	else if(first_time)
+	else if (first_time)
 	{
-		char *srcpos, *srcend, *destpos, *destend;
+		char *srcpos, *srcend, *destpos;
 		srcpos = sip->name;
 		destpos = sip->short_name;
 		srcend = srcpos + strlen(sip->name);
-		destend = destpos + sizeof(sip->short_name) - 1;
-		while(srcpos < srcend)
+		while(srcpos <= srcend)
 		{
 			if(*srcpos != ' ')
 				*destpos++ = *srcpos++;
@@ -1769,32 +2174,32 @@
 		if(optional_string("+Min Lifetime:"))	{
 			stuff_float(&sip->debris_min_lifetime);
 			if(sip->debris_min_lifetime < 0.0f)
-				Warning(LOCATION, "Debris min lifetime on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris min lifetime on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Max Lifetime:"))	{
 			stuff_float(&sip->debris_max_lifetime);
 			if(sip->debris_max_lifetime < 0.0f)
-				Warning(LOCATION, "Debris max lifetime on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris max lifetime on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Min Speed:"))	{
 			stuff_float(&sip->debris_min_speed);
 			if(sip->debris_min_speed < 0.0f)
-				Warning(LOCATION, "Debris min speed on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris min speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Max Speed:"))	{
 			stuff_float(&sip->debris_max_speed);
 			if(sip->debris_max_speed < 0.0f)
-				Warning(LOCATION, "Debris max speed on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris max speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Min Rotation speed:"))	{
 			stuff_float(&sip->debris_min_rotspeed);
 			if(sip->debris_min_rotspeed < 0.0f)
-				Warning(LOCATION, "Debris min speed on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris min speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Max Rotation speed:"))	{
 			stuff_float(&sip->debris_max_rotspeed);
 			if(sip->debris_max_rotspeed < 0.0f)
-				Warning(LOCATION, "Debris max speed on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris max speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Damage Type:")) {
 			stuff_string(buf, F_NAME, NAME_LENGTH);
@@ -1803,22 +2208,22 @@
 		if(optional_string("+Min Hitpoints:")) {
 			stuff_float(&sip->debris_min_hitpoints);
 			if(sip->debris_min_hitpoints < 0.0f)
-				Warning(LOCATION, "Debris min hitpoints on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris min hitpoints on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Max Hitpoints:")) {
 			stuff_float(&sip->debris_max_hitpoints);
 			if(sip->debris_max_hitpoints < 0.0f)
-				Warning(LOCATION, "Debris max hitpoints on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris max hitpoints on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Damage Multiplier:")) {
 			stuff_float(&sip->debris_damage_mult);
 			if(sip->debris_damage_mult < 0.0f)
-				Warning(LOCATION, "Debris damage multiplier on ship class '%s' is below 0 and will be ignored", sip->name);
+				Warning(LOCATION, "Debris damage multiplier on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
 		}
 		if(optional_string("+Lightning Arc Percent:")) {
 			stuff_float(&sip->debris_arc_percent);
 			if(sip->debris_arc_percent < 0.0f || sip->debris_arc_percent > 100.0f) {
-				Warning(LOCATION, "Lightning Arc Percent on ship class '%s' should be between 0 and 100.0 (read %f). Entry will be ignored.", sip->name, sip->debris_arc_percent);
+				Warning(LOCATION, "Lightning Arc Percent on %s '%s' should be between 0 and 100.0 (read %f). Entry will be ignored.", info_type_name, sip->name, sip->debris_arc_percent);
 				sip->debris_arc_percent = 50.0;
 			}
 			//Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0
@@ -1828,19 +2233,19 @@
 	}
 	//WMC - sanity checking
 	if(sip->debris_min_speed > sip->debris_max_speed && sip->debris_max_speed >= 0.0f) {
-		Warning(LOCATION, "Debris min speed (%f) on ship class '%s' is greater than debris max speed (%f), and will be set to debris max speed.", sip->debris_min_speed, sip->name, sip->debris_max_speed);
+		Warning(LOCATION, "Debris min speed (%f) on %s '%s' is greater than debris max speed (%f), and will be set to debris max speed.", sip->debris_min_speed, info_type_name, sip->name, sip->debris_max_speed);
 		sip->debris_min_speed = sip->debris_max_speed;
 	}
 	if(sip->debris_min_rotspeed > sip->debris_max_rotspeed && sip->debris_max_rotspeed >= 0.0f) {
-		Warning(LOCATION, "Debris min rotation speed (%f) on ship class '%s' is greater than debris max rotation speed (%f), and will be set to debris max rotation speed.", sip->debris_min_rotspeed, sip->name, sip->debris_max_rotspeed);
+		Warning(LOCATION, "Debris min rotation speed (%f) on %s '%s' is greater than debris max rotation speed (%f), and will be set to debris max rotation speed.", sip->debris_min_rotspeed, info_type_name, sip->name, sip->debris_max_rotspeed);
 		sip->debris_min_rotspeed = sip->debris_max_rotspeed;
 	}
 	if(sip->debris_min_lifetime > sip->debris_max_lifetime && sip->debris_max_lifetime >= 0.0f) {
-		Warning(LOCATION, "Debris min lifetime (%f) on ship class '%s' is greater than debris max lifetime (%f), and will be set to debris max lifetime.", sip->debris_min_lifetime, sip->name, sip->debris_max_lifetime);
+		Warning(LOCATION, "Debris min lifetime (%f) on %s '%s' is greater than debris max lifetime (%f), and will be set to debris max lifetime.", sip->debris_min_lifetime, info_type_name, sip->name, sip->debris_max_lifetime);
 		sip->debris_min_lifetime = sip->debris_max_lifetime;
 	}
 	if(sip->debris_min_hitpoints > sip->debris_max_hitpoints && sip->debris_max_hitpoints >= 0.0f) {
-		Warning(LOCATION, "Debris min hitpoints (%f) on ship class '%s' is greater than debris max hitpoints (%f), and will be set to debris max hitpoints.", sip->debris_min_hitpoints, sip->name, sip->debris_max_hitpoints);
+		Warning(LOCATION, "Debris min hitpoints (%f) on %s '%s' is greater than debris max hitpoints (%f), and will be set to debris max hitpoints.", sip->debris_min_hitpoints, info_type_name, sip->name, sip->debris_max_hitpoints);
 		sip->debris_min_hitpoints = sip->debris_max_hitpoints;
 	}
 
@@ -1858,7 +2263,7 @@
 
 	if(optional_string("$Banking Constant:"))
 		stuff_float( &(sip->delta_bank_const) );
-	diag_printf ("Ship class '%s' delta_bank_const -- %7.3f\n", sip->name, sip->delta_bank_const);
+	diag_printf ("%s '%s' delta_bank_const -- %7.3f\n", info_type_name, sip->name, sip->delta_bank_const);
 
 	if(optional_string("$Max Velocity:"))
 	{
@@ -1875,7 +2280,7 @@
 
 		// div/0 safety check.
 		if ((sip->rotation_time.xyz.x == 0) || (sip->rotation_time.xyz.y == 0) || (sip->rotation_time.xyz.z == 0))
-			Warning(LOCATION, "Rotation time must have non-zero values in each of the three variables.\nFix this in ship %s\n", sip->name);
+			Warning(LOCATION, "Rotation time must have non-zero values in each of the three variables.\nFix this in %s %s\n", info_type_name, sip->name);
 
 		sip->srotation_time = (sip->rotation_time.xyz.x + sip->rotation_time.xyz.y)/2.0f;
 
@@ -1982,7 +2387,7 @@
 		if(j >= 0) {
 			sip->warpin_type = j;
 		} else {
-			Warning(LOCATION, "Invalid warpin type '%s' specified for ship '%s'", buf, sip->name);
+			Warning(LOCATION, "Invalid warpin type '%s' specified for %s '%s'", buf, info_type_name, sip->name);
 			sip->warpin_type = WT_DEFAULT;
 		}
 	}
@@ -2001,7 +2406,7 @@
 		stuff_float(&t_time);
 		sip->warpin_time = fl2i(t_time*1000.0f);
 		if(sip->warpin_time <= 0) {
-			Warning(LOCATION, "Warp-in time specified as 0 or less on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-in time specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
 		}
 	}
 
@@ -2009,7 +2414,7 @@
 	{
 		stuff_float(&sip->warpin_decel_exp);
 		if (sip->warpin_decel_exp < 0.0f) {
-			Warning(LOCATION, "Warp-in deceleration exponent specified as less than 0 on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-in deceleration exponent specified as less than 0 on %s '%s'; value ignored", info_type_name, sip->name);
 			sip->warpin_decel_exp = 1.0f;
 		}
 	}
@@ -2018,7 +2423,7 @@
 	{
 		stuff_float(&sip->warpin_radius);
 		if(sip->warpin_radius <= 0.0f) {
-			Warning(LOCATION, "Warp-in radius specified as 0 or less on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-in radius specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
 		}
 	}
 
@@ -2034,7 +2439,7 @@
 		if(j >= 0) {
 			sip->warpout_type = j;
 		} else {
-			Warning(LOCATION, "Invalid warpout type '%s' specified for ship '%s'", buf, sip->name);
+			Warning(LOCATION, "Invalid warpout type '%s' specified for %s '%s'", buf, info_type_name, sip->name);
 			sip->warpout_type = WT_DEFAULT;
 		}
 	}
@@ -2049,7 +2454,7 @@
 		if (t_time >= 0)
 			sip->warpout_engage_time = fl2i(t_time*1000.0f);
 		else
-			Warning(LOCATION, "Warp-out engage time specified as 0 or less on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-out engage time specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
 	} else {
 		sip->warpout_engage_time = -1;
 	}
@@ -2065,7 +2470,7 @@
 		stuff_float(&t_time);
 		sip->warpout_time = fl2i(t_time*1000.0f);
 		if(sip->warpout_time <= 0) {
-			Warning(LOCATION, "Warp-out time specified as 0 or less on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-out time specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
 		}
 	}
 
@@ -2073,7 +2478,7 @@
 	{
 		stuff_float(&sip->warpout_accel_exp);
 		if (sip->warpout_accel_exp < 0.0f) {
-			Warning(LOCATION, "Warp-out acceleration exponent specified as less than 0 on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-out acceleration exponent specified as less than 0 on %s '%s'; value ignored", info_type_name, sip->name);
 			sip->warpout_accel_exp = 1.0f;
 		}
 	}
@@ -2082,7 +2487,7 @@
 	{
 		stuff_float(&sip->warpout_radius);
 		if(sip->warpout_radius <= 0.0f) {
-			Warning(LOCATION, "Warp-out radius specified as 0 or less on ship '%s'; value ignored", sip->name);
+			Warning(LOCATION, "Warp-out radius specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
 		}
 	}
 
@@ -2126,7 +2531,7 @@
 		stuff_float(&sip->prop_exp_rad_mult);
 		if(sip->prop_exp_rad_mult <= 0) {
 			// on invalid value return to default setting
-			Warning(LOCATION, "Propagating explosion radius multiplier was set to non-positive value.\nDefaulting multiplier to 1.0 on ship '%s'.\n", sip->name);
+			Warning(LOCATION, "Propagating explosion radius multiplier was set to non-positive value.\nDefaulting multiplier to 1.0 on %s '%s'.\n", info_type_name, sip->name);
 			sip->prop_exp_rad_mult = 1.0f;
 		}
 	}
@@ -2492,7 +2897,7 @@
 		stuff_float(&sip->max_hull_strength);
 		if (sip->max_hull_strength < 0.0f)
 		{
-			Warning(LOCATION, "Max hull strength on ship %s cannot be less than 0.  Defaulting to 100.\n", sip->name, sip->max_hull_strength);
+			Warning(LOCATION, "Max hull strength on %s '%s' cannot be less than 0.  Defaulting to 100.\n", info_type_name, sip->name, sip->max_hull_strength);
 			sip->max_hull_strength = 100.0f;
 		}
 	}
@@ -2546,7 +2951,7 @@
 		sip->armor_type_idx = armor_type_get_idx(buf);
 
 		if(sip->armor_type_idx == -1)
-			Warning(LOCATION,"Invalid armor name %s specified for hull in ship class %s", buf, sip->name);
+			Warning(LOCATION,"Invalid armor name %s specified for hull in %s '%s'", buf, info_type_name, sip->name);
 	}
 
 	if(optional_string("$Shield Armor Type:"))
@@ -2555,7 +2960,7 @@
 		sip->shield_armor_type_idx = armor_type_get_idx(buf);
 
 		if(sip->shield_armor_type_idx == -1)
-			Warning(LOCATION,"Invalid armor name %s specified for shield in ship class %s", buf, sip->name);
+			Warning(LOCATION,"Invalid armor name %s specified for shield in %s '%s'", buf, info_type_name, sip->name);
 	}
 
 	if (optional_string("$Flags:"))
@@ -2606,7 +3011,7 @@
 					flag_found = true;
 
 					if (Ship_flags[idx].var == 255)
-						Warning(LOCATION, "Use of '%s' flag for ship '%s' - this flag is no longer needed.", Ship_flags[idx].name, sip->name);
+						Warning(LOCATION, "Use of '%s' flag for %s '%s' - this flag is no longer needed.", Ship_flags[idx].name, info_type_name, sip->name);
 					else if (Ship_flags[idx].var == 0)
 						sip->flags |= Ship_flags[idx].def;
 					else if (Ship_flags[idx].var == 1)
@@ -2628,7 +3033,7 @@
 	// Goober5000 - ensure number of banks checks out
 	if (sip->num_primary_banks > MAX_SHIP_PRIMARY_BANKS)
 	{
-		Error(LOCATION, "Ship Class %s has too many primary banks (%d).  Maximum for ships is currently %d.\n", sip->name, sip->num_primary_banks, MAX_SHIP_PRIMARY_BANKS);
+		Error(LOCATION, "%s '%s' has too many primary banks (%d).  Maximum for ships is currently %d.\n", info_type_name, sip->name, sip->num_primary_banks, MAX_SHIP_PRIMARY_BANKS);
 	}
 
 	// copy to regular allowed_weapons array
@@ -2699,7 +3104,7 @@
 		}
 
 		if (!(sip->afterburner_fuel_capacity) ) {
-			Warning(LOCATION, "Ship class %s has an afterburner but has no afterburner fuel. Setting fuel to 1", sip->name);
+			Warning(LOCATION, "%s '%s' has an afterburner but has no afterburner fuel. Setting fuel to 1", info_type_name, sip->name);
 			sip->afterburner_fuel_capacity = 1.0f;
 		}
 	}
@@ -2734,7 +3139,7 @@
 		}
 
 		if (trails_warning)
-			Warning(LOCATION, "Ship %s entry has $Trails field specified, but no properties given.", sip->name);
+			Warning(LOCATION, "%s %s entry has $Trails field specified, but no properties given.", info_type_name, sip->name);
 	}
 
 	if (optional_string("$Countermeasure type:")) {
@@ -2741,9 +3146,9 @@
 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
 		int res = weapon_info_lookup(buf);
 		if (res < 0) {
-			Warning(LOCATION, "Could not find weapon type '%s' to use as countermeasure on ship class '%s'", buf, sip->name);
+			Warning(LOCATION, "Could not find weapon type '%s' to use as countermeasure on %s '%s'", buf, info_type_name, sip->name);
 		} else if (Weapon_info[res].wi_flags & WIF_BEAM) {
-			Warning(LOCATION, "Attempt made to set a beam weapon as a countermeasure on ship class '%s'", sip->name);
+			Warning(LOCATION, "Attempt made to set a beam weapon as a countermeasure on %s '%s'", info_type_name, sip->name);
 		} else {
 			sip->cmeasure_type = res;
 		}
@@ -2838,9 +3243,9 @@
 
 	// check for inconsistencies
 	if ((sip->bii_index_wing_with_cargo >= 0) && (sip->bii_index_wing < 0 || sip->bii_index_ship_with_cargo < 0))
-		Warning(LOCATION, "Ship '%s' has a wing-with-cargo briefing icon but is missing a wing briefing icon or a ship-with-cargo briefing icon!", sip->name);
+		Warning(LOCATION, "%s '%s' has a wing-with-cargo briefing icon but is missing a wing briefing icon or a ship-with-cargo briefing icon!", info_type_name, sip->name);
 	if ((sip->bii_index_wing_with_cargo < 0) && (sip->bii_index_wing >= 0) && (sip->bii_index_ship_with_cargo >= 0))
-		Warning(LOCATION, "Ship '%s' has both a wing briefing icon and a ship-with-cargo briefing icon but does not have a wing-with-cargo briefing icon!", sip->name);
+		Warning(LOCATION, "%s '%s' has both a wing briefing icon and a ship-with-cargo briefing icon but does not have a wing-with-cargo briefing icon!", info_type_name, sip->name);
 
 	if ( optional_string("$Score:") ){
 		stuff_int( &sip->score );
@@ -2974,7 +3379,7 @@
 		else if ( optional_string("$Afterburner Particle Bitmap:") )
 			afterburner = true;
 		else
-			Error( LOCATION, "formatting error in the thruster's particle section for ship %s\n", sip->name );
+			Error( LOCATION, "formatting error in the thruster's particle section for %s '%s'\n", info_type_name, sip->name );
 
 		generic_anim_init(&tpart.thruster_bitmap, NULL);
 		stuff_string(tpart.thruster_bitmap.filename, F_NAME, MAX_FILENAME_LEN);
@@ -3007,7 +3412,7 @@
 	}
 	
 	else if ( optional_string("$Stealth") ) {
-		Warning(LOCATION, "Ship %s is missing the colon after \"$Stealth\". Note that you may also use the ship flag \"stealth\".", sip->name);
+		Warning(LOCATION, "%s '%s' is missing the colon after \"$Stealth\". Note that you may also use the ship flag \"stealth\".", info_type_name, sip->name);
 		sip->flags |= SIF_STEALTH;
 	}
 
@@ -3029,7 +3434,7 @@
 
 		// this means you've reached the max # of contrails for a ship
 		if (sip->ct_count >= MAX_SHIP_CONTRAILS) {
-			Warning(LOCATION, "%s has more contrails than the max of %d", sip->name, MAX_SHIP_CONTRAILS);
+			Warning(LOCATION, "%s '%s' has more contrails than the max of %d", info_type_name, sip->name, MAX_SHIP_CONTRAILS);
 			break;
 		}
 
@@ -3081,11 +3486,11 @@
 			if(sip->num_maneuvering < MAX_MAN_THRUSTERS) {
 				mtp = &sip->maneuvering[sip->num_maneuvering++];
 			} else {
-				Warning(LOCATION, "Too many maneuvering thrusters on ship '%s'; maximum is %d", sip->name, MAX_MAN_THRUSTERS);
+				Warning(LOCATION, "Too many maneuvering thrusters on %s '%s'; maximum is %d", info_type_name, sip->name, MAX_MAN_THRUSTERS);
 			}
 		} else {
 			mtp = &manwich;
-			Warning(LOCATION, "Invalid index (%d) specified for maneuvering thruster on ship %s", idx, sip->name);
+			Warning(LOCATION, "Invalid index (%d) specified for maneuvering thruster on %s '%s'", idx, info_type_name, sip->name);
 		}
 
 		if(optional_string("+Used for:")) {
@@ -3167,10 +3572,10 @@
 		iff_data[1] = iff_lookup(iff_2);
 
 		if (iff_data[0] == -1)
-			WarningEx(LOCATION, "Ship %s\nIFF colour seen by \"%s\" invalid!", sip->name, iff_1);
+			WarningEx(LOCATION, "%s '%s'\nIFF colour seen by \"%s\" invalid!", info_type_name, sip->name, iff_1);
 
 		if (iff_data[1] == -1)
-			WarningEx(LOCATION, "Ship %s\nIFF colour when IFF is \"%s\" invalid!", sip->name, iff_2);
+			WarningEx(LOCATION, "%s '%s'\nIFF colour when IFF is \"%s\" invalid!", info_type_name, sip->name, iff_2);
 
 		// Set the color
 		required_string("+As Color:");
@@ -3210,7 +3615,7 @@
 				}
 			}
 			if (i == num_groups) {
-				Warning(LOCATION,"Unidentified priority group '%s' set for ship class '%s'\n", target_group_strings[j].c_str(), sip->name);
+				Warning(LOCATION,"Unidentified priority group '%s' set for %s '%s'\n", target_group_strings[j].c_str(), info_type_name, sip->name);
 			}
 		}
 	}
@@ -3259,7 +3664,7 @@
 	}
 
 	while (cont_flag) {
-		int r = required_string_3("#End", "$Subsystem:", "$Name" );
+		int r = required_string_one_of(3, "#End", "$Subsystem:", type_name);
 		switch (r) {
 		case 0:
 			cont_flag = 0;
@@ -3285,7 +3690,7 @@
 			{
 				if( sip->n_subsystems + n_subsystems >= MAX_MODEL_SUBSYSTEMS )
 				{
-					Warning(LOCATION, "Number of subsystems for ship entry '%s' (%d) exceeds max of %d; only the first %d will be used", sip->name, sip->n_subsystems, n_subsystems, MAX_MODEL_SUBSYSTEMS);
+					Warning(LOCATION, "Number of subsystems for %s '%s' (%d) exceeds max of %d; only the first %d will be used", info_type_name, sip->name, sip->n_subsystems, n_subsystems, MAX_MODEL_SUBSYSTEMS);
 					break;
 				}
 				sp = &subsystems[n_subsystems++];			// subsystems a local -- when done, we will malloc and copy
@@ -3364,9 +3769,9 @@
 				}
 				else
 				{
-					Error(LOCATION, "Malformed $Subsystem entry '%s' %s.\n\n"
+					Error(LOCATION, "Malformed $Subsystem entry '%s' in %s '%s'.\n\n"
 						"Specify a turning rate or remove the trailing comma.",
-						sp->subobj_name, sip->name);
+						sp->subobj_name, info_type_name, sip->name);
 				}
 			}
 
@@ -3385,7 +3790,7 @@
 				sp->armor_type_idx = armor_type_get_idx(buf);
 
 				if (sp->armor_type_idx == -1)
-					WarningEx(LOCATION, "Ship %s, subsystem %s\nInvalid armor type %s!", sip->name, sp->subobj_name, buf);
+					WarningEx(LOCATION, "%s '%s', subsystem %s\nInvalid armor type %s!", info_type_name, sip->name, sp->subobj_name, buf);
 			}
 
 			//	Get primary bank weapons
@@ -3401,7 +3806,7 @@
 				sp->engine_wash_pointer = get_engine_wash_pointer(name_tmp);
 
 				if(sp->engine_wash_pointer == NULL)
-					WarningEx(LOCATION,"Invalid engine wash name %s specified for subsystem %s in ship class %s", name_tmp, sp->subobj_name, sip->name);
+					WarningEx(LOCATION,"Invalid engine wash name %s specified for subsystem %s in %s '%s'", name_tmp, sp->subobj_name, info_type_name, sip->name);
 			}
 
 			parse_sound("$AliveSnd:", &sp->alive_snd, sp->subobj_name);
@@ -3478,7 +3883,7 @@
 						}
 					}
 					if (j == num_groups) {
-						Warning(LOCATION, "Unidentified target priority '%s' set for\nsubsystem '%s' in ship class '%s'.", tgt_priorities[i].c_str(), sp->subobj_name, sip->name);
+						Warning(LOCATION, "Unidentified target priority '%s' set for\nsubsystem '%s' in %s '%s'.", tgt_priorities[i].c_str(), sp->subobj_name, info_type_name, sip->name);
 					}
 				}
 			}
@@ -3501,13 +3906,13 @@
 						stuff_float(&tempf);
 
 						if (tempf < 0) {
-							mprintf(("RoF multiplier clamped to 0 for subsystem '%s' in ship class '%s'.\n", sp->subobj_name, sip->name));
+							mprintf(("RoF multiplier clamped to 0 for subsystem '%s' in %s '%s'.\n", sp->subobj_name, info_type_name, sip->name));
 							sp->turret_rof_scaler = 0;
 						} else {
 							sp->turret_rof_scaler = tempf;
 						}
 					} else {
-						Warning(LOCATION, "RoF multiplier not set for subsystem\n'%s' in ship class '%s'.", sp->subobj_name, sip->name);
+						Warning(LOCATION, "RoF multiplier not set for subsystem\n'%s' in %s '%s'.", sp->subobj_name, info_type_name, sip->name);
 					}
 				}
 			}
@@ -3578,17 +3983,17 @@
 			}
 
 			if ((sp->flags & MSS_FLAG_TURRET_FIXED_FP) && !(sp->flags & MSS_FLAG_USE_MULTIPLE_GUNS)) {
-				Warning(LOCATION, "\"fixed firingpoints\" flag used without \"use multiple guns\" flag on a subsystem on ship %s.\n\"use multiple guns\" flags added by default\n", sip->name);
+				Warning(LOCATION, "\"fixed firingpoints\" flag used without \"use multiple guns\" flag on a subsystem on %s '%s'.\n\"use multiple guns\" flags added by default\n", info_type_name, sip->name);
 				sp->flags |= MSS_FLAG_USE_MULTIPLE_GUNS;
 			}
 
 			if (old_flags) {
 				mprintf(("Use of deprecated subsystem syntax.  Please use the $Flags: field for subsystem flags.\n\n" \
-				"At least one of the following tags was used on ship %s, subsystem %s:\n" \
+				"At least one of the following tags was used on %s '%s', subsystem %s:\n" \
 				"\t+untargetable\n" \
 				"\t+carry-no-damage\n" \
 				"\t+use-multiple-guns\n" \
-				"\t+fire-down-normals\n", sip->name, sp->subobj_name));
+				"\t+fire-down-normals\n", info_type_name, sip->name, sp->subobj_name));
 			}
 
 			while(optional_string("$animation:"))
@@ -3757,8 +4162,11 @@
 		case 2:
 			cont_flag = 0;
 			break;
+		case -1:
+			// Possible if -noparseerrors is used.
+			break;
 		default:
-			Int3();	// Impossible return value from required_string_3.
+			Assertion(false, "This should never happen.\n");	// Impossible return value from required_string_one_of().
 		}
 	}	
 
@@ -4158,6 +4566,17 @@
 		required_string("#End");
 	}
 
+	if ( optional_string("#Ship Templates") ) {
+
+		while ( required_string_either("#End", "$Template:") ) {
+			if ( parse_ship_template() ) {
+				continue;
+			}
+		}
+
+		required_string("#End");
+	}
+
 	//Add ship classes
 	if(optional_string("#Ship Classes"))
 	{
@@ -4276,6 +4695,25 @@
 			}
 		}
 	}
+
+	// Clear out ship templates, since they're no longer needed. -MageKing17
+	for ( SCP_vector<ship_info>::iterator it = Ship_templates.begin(); it != Ship_templates.end(); ++it ) {
+		// free memory alloced for subsystem storage
+		if ( it->subsystems != NULL ) {
+			for(int n = 0; n < it->n_subsystems; n++) {
+				if (it->subsystems[n].triggers != NULL) {
+					vm_free(it->subsystems[n].triggers);
+					it->subsystems[n].triggers = NULL;
+				}
+			}
+
+			vm_free(it->subsystems);
+			it->subsystems = NULL;
+		}
+
+		it->free_strings();
+	}
+	Ship_templates.clear();
 }
 
 /**
@@ -11886,6 +12324,21 @@
 	return -1;
 }
 
+/**
+ * Return the index of Ship_templates[].name that is *token.
+ */
+int ship_template_lookup(const char *token)
+{
+	int	i;
+
+	for ( i = 0; i < (int)Ship_templates.size(); i++ ) {
+		if ( !stricmp(token, Ship_templates[i].name) ) {
+			return i;
+		}
+	}
+	return -1;
+}
+
 // Goober5000
 int ship_info_lookup(const char *token)
 {
@@ -13285,8 +13738,9 @@
 	// free this too! -- Goober5000
 	ship_clear_subsystems();
 
-	// free memory alloced for subsystem storage
+	// free info from parsed table data
 	for ( i = 0; i < Num_ship_classes; i++ ) {
+		// free memory alloced for subsystem storage
 		if ( Ship_info[i].subsystems != NULL ) {
 			for(n = 0; n < Ship_info[i].n_subsystems; n++) {
 				if (Ship_info[i].subsystems[n].triggers != NULL) {
@@ -13299,105 +13753,9 @@
 			Ship_info[i].subsystems = NULL;
 		}
 
-		// free info from parsed table data
-		if (Ship_info[i].type_str != NULL) {
-			vm_free(Ship_info[i].type_str);
-			Ship_info[i].type_str = NULL;
-		}
-
-		if (Ship_info[i].maneuverability_str != NULL) {
-			vm_free(Ship_info[i].maneuverability_str);
-			Ship_info[i].maneuverability_str = NULL;
-		}
-
-		if (Ship_info[i].armor_str != NULL) {
-			vm_free(Ship_info[i].armor_str);
-			Ship_info[i].armor_str = NULL;
-		}
-
-		if (Ship_info[i].manufacturer_str != NULL) {
-			vm_free(Ship_info[i].manufacturer_str);
-			Ship_info[i].manufacturer_str = NULL;
-		}
-
-		if (Ship_info[i].desc != NULL) {
-			vm_free(Ship_info[i].desc);
-			Ship_info[i].desc = NULL;
-		}
-
-		if (Ship_info[i].tech_desc != NULL) {
-			vm_free(Ship_info[i].tech_desc);
-			Ship_info[i].tech_desc = NULL;
-		}
-
-		if (Ship_info[i].ship_length != NULL) {
-			vm_free(Ship_info[i].ship_length);
-			Ship_info[i].ship_length = NULL;
-		}
-
-		if (Ship_info[i].gun_mounts != NULL) {
-			vm_free(Ship_info[i].gun_mounts);
-			Ship_info[i].gun_mounts = NULL;
-		}
-
-		if (Ship_info[i].missile_banks != NULL) {
-			vm_free(Ship_info[i].missile_banks);
-			Ship_info[i].missile_banks = NULL;
-		}
+		Ship_info[i].free_strings();
 	}
 
-	// free info from parsed table data
-	for (i=0; i<MAX_SHIP_CLASSES; i++) {
-		if ( Ship_info[i].subsystems != NULL ) {
-			for(n = 0; n < Ship_info[i].n_subsystems; n++) {
-				if (Ship_info[i].subsystems[n].triggers != NULL) {
-					vm_free(Ship_info[i].subsystems[n].triggers);
-					Ship_info[i].subsystems[n].triggers = NULL;
-				}
-			}
-			
-			vm_free(Ship_info[i].subsystems);
-			Ship_info[i].subsystems = NULL;
-		}
-		
-		if(Ship_info[i].type_str != NULL){
-			vm_free(Ship_info[i].type_str);
-			Ship_info[i].type_str = NULL;
-		}
-		if(Ship_info[i].maneuverability_str != NULL){
-			vm_free(Ship_info[i].maneuverability_str);
-			Ship_info[i].maneuverability_str = NULL;
-		}
-		if(Ship_info[i].armor_str != NULL){
-			vm_free(Ship_info[i].armor_str);
-			Ship_info[i].armor_str = NULL;
-		}
-		if(Ship_info[i].manufacturer_str != NULL){
-			vm_free(Ship_info[i].manufacturer_str);
-			Ship_info[i].manufacturer_str = NULL;
-		}
-		if(Ship_info[i].desc != NULL){
-			vm_free(Ship_info[i].desc);
-			Ship_info[i].desc = NULL;
-		}
-		if(Ship_info[i].tech_desc != NULL){
-			vm_free(Ship_info[i].tech_desc);
-			Ship_info[i].tech_desc = NULL;
-		}
-		if(Ship_info[i].ship_length != NULL){
-			vm_free(Ship_info[i].ship_length);
-			Ship_info[i].ship_length = NULL;
-		}
-		if(Ship_info[i].gun_mounts != NULL){
-			vm_free(Ship_info[i].gun_mounts);
-			Ship_info[i].gun_mounts = NULL;
-		}
-		if(Ship_info[i].missile_banks != NULL){
-			vm_free(Ship_info[i].missile_banks);
-			Ship_info[i].missile_banks = NULL;
-		}
-	}
-
 	for (i = 0; i < (int)Ship_types.size(); i++) {
 		Ship_types[i].ai_actively_pursues.clear();
 		Ship_types[i].ai_actively_pursues_temp.clear();
Index: code/ship/ship.h
===================================================================
--- code/ship/ship.h	(revision 11204)
+++ code/ship/ship.h	(working copy)
@@ -1431,6 +1431,11 @@
 	SCP_vector<cockpit_display_info> displays;
 
 	SCP_map<SCP_string, path_metadata> pathMetadata;
+
+	const ship_info &operator=(const ship_info& other);
+	void free_strings();
+private:
+	void clone(const ship_info& other);
 };
 
 extern int Num_wings;
@@ -1620,7 +1625,8 @@
 //	Stuff vector *pos with absolute position.
 extern int get_subsystem_pos(vec3d *pos, object *objp, ship_subsys *subsysp);
 
-int parse_ship_values(ship_info* sip, bool first_time, bool replace);
+int parse_ship_values(ship_info* sip, const bool is_template, const bool first_time, const bool replace);
+int ship_template_lookup(const char *name = NULL);
 void parse_ship_particle_effect(ship_info* sip, particle_effect* pe, char *id_string);
 
 extern int ship_info_lookup(const char *name = NULL);
ship.patch (45,760 bytes)   

MageKing17

2014-12-23 19:33

developer   ~0016433

Just updating to account for r11204.

MageKing17

2015-04-23 21:37

developer   ~0016659

This needs some additional work before it's actually ready for code review.

MageKing17

2015-04-27 07:10

developer   ~0016681

This now has a pull request:

https://github.com/scp-fs2open/fs2open.github.com/pull/41

MageKing17

2015-08-31 05:02

developer   ~0016767

This was merged a couple weeks ago: https://github.com/scp-fs2open/fs2open.github.com/commit/0a2b97c3bc26332eadae0ed1f8d3c7a640d6ed00

Issue History

Date Modified Username Field Change
2014-08-04 05:50 MageKing17 New Issue
2014-08-04 05:50 MageKing17 Status new => assigned
2014-08-04 05:50 MageKing17 Assigned To => MageKing17
2014-08-04 05:50 MageKing17 File Added: ship.patch
2014-08-04 05:50 MageKing17 Status assigned => code review
2014-08-04 05:54 MageKing17 File Deleted: ship.patch
2014-08-04 05:54 MageKing17 File Added: ship.patch
2014-08-05 23:25 MageKing17 File Added: template_test-shp.tbm
2014-08-05 23:30 MageKing17 Note Added: 0016176
2014-08-05 23:30 MageKing17 Relationship added related to 0002064
2014-08-06 00:24 MageKing17 File Deleted: ship.patch
2014-08-06 00:24 MageKing17 File Added: ship.patch
2014-08-06 00:25 MageKing17 Note Added: 0016177
2014-08-06 00:57 MageKing17 File Deleted: ship.patch
2014-08-06 00:57 MageKing17 File Added: ship.patch
2014-08-06 00:58 MageKing17 Note Edited: 0016177
2014-08-06 01:39 Goober5000 Note Added: 0016178
2014-10-23 00:01 MageKing17 File Deleted: ship.patch
2014-10-23 00:01 MageKing17 File Added: ship.patch
2014-10-23 00:04 MageKing17 File Deleted: ship.patch
2014-10-23 00:08 MageKing17 File Added: ship.patch
2014-10-23 00:08 MageKing17 Note Added: 0016345
2014-10-23 03:23 Goober5000 Note Added: 0016346
2014-10-31 21:55 MageKing17 File Deleted: ship.patch
2014-10-31 21:55 MageKing17 File Added: ship.patch
2014-10-31 22:01 MageKing17 Note Added: 0016367
2014-12-23 19:32 MageKing17 File Deleted: ship.patch
2014-12-23 19:32 MageKing17 File Added: ship.patch
2014-12-23 19:33 MageKing17 Note Added: 0016433
2015-04-23 21:37 MageKing17 Note Added: 0016659
2015-04-23 21:37 MageKing17 Status code review => assigned
2015-04-27 07:10 MageKing17 Note Added: 0016681
2015-04-27 07:10 MageKing17 Status assigned => code review
2015-08-31 05:02 MageKing17 Note Added: 0016767
2015-08-31 05:02 MageKing17 Status code review => resolved
2015-08-31 05:02 MageKing17 Resolution open => fixed
2015-08-31 05:02 MageKing17 Status resolved => feedback
2015-08-31 05:02 MageKing17 Resolution fixed => reopened
2015-08-31 05:02 MageKing17 Status feedback => resolved
2015-08-31 05:02 MageKing17 Resolution reopened => fixed
2015-08-31 05:02 MageKing17 Fixed in Version => 3.7.3
2015-08-31 05:02 MageKing17 Target Version => 3.7.4