1 /*
2  * Species_Defs.CPP
3  * Extended Species Support for FS2 Open
4  *
5  * You may not sell or otherwise commercially exploit the source or things you
6  * create based on the source.
7  *
8  */
9 
10 
11 #include "cfile/cfile.h"
12 #include "def_files/def_files.h"
13 #include "iff_defs/iff_defs.h"
14 #include "localization/localize.h"
15 #include "parse/parselo.h"
16 #include "species_defs/species_defs.h"
17 
18 
19 SCP_vector<species_info> Species_info;
20 
21 //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22 
23 // This function parses the data from the species_defs.tbl
24 // Names only - actual loading is done elsewhere
25 
parse_thrust_anims(species_info * species,bool no_create)26 void parse_thrust_anims(species_info *species, bool no_create)
27 {
28 	if (!no_create)
29 	{
30 		required_string("$ThrustAnims:");
31 
32 		generic_anim_init(&species->thruster_info.flames.normal, NULL);
33 		generic_anim_init(&species->thruster_info.flames.afterburn, NULL);
34 		generic_bitmap_init(&species->thruster_secondary_glow_info.normal, NULL);
35 		generic_bitmap_init(&species->thruster_secondary_glow_info.afterburn, NULL);
36 		generic_bitmap_init(&species->thruster_tertiary_glow_info.normal, NULL);
37 		generic_bitmap_init(&species->thruster_tertiary_glow_info.afterburn, NULL);
38 		generic_bitmap_init(&species->thruster_distortion_info.normal, NULL);
39 		generic_bitmap_init(&species->thruster_distortion_info.afterburn, NULL);
40 	}
41 	else if (!optional_string("$ThrustAnims:"))
42 	{
43 		return;
44 	}
45 
46 	// favor new style
47 	if (no_create)
48 	{
49 		if (optional_string("+Pri_Normal:") || optional_string("+Normal:"))
50 			stuff_string(species->thruster_info.flames.normal.filename, F_NAME, MAX_FILENAME_LEN);
51 	}
52 	else
53 	{
54 		if (!optional_string("+Pri_Normal:"))
55 			required_string("+Normal:");
56 
57 		stuff_string(species->thruster_info.flames.normal.filename, F_NAME, MAX_FILENAME_LEN);
58 	}
59 
60 	// if no primary thruster anim is wanted then clear it
61 	if ( !VALID_FNAME(species->thruster_info.flames.normal.filename) )
62 		generic_anim_init(&species->thruster_info.flames.normal, NULL);
63 
64 	// and again
65 	if (no_create)
66 	{
67 		if (optional_string("+Pri_Afterburn:") || optional_string("+Afterburn:"))
68 			stuff_string(species->thruster_info.flames.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
69 	}
70 	else
71 	{
72 		if (!optional_string("+Pri_Afterburn:"))
73 			required_string("+Afterburn:");
74 
75 		stuff_string(species->thruster_info.flames.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
76 	}
77 
78 	// if no primary thruster anim is wanted then clear it
79 	if ( !VALID_FNAME(species->thruster_info.flames.afterburn.filename) )
80 		generic_anim_init(&species->thruster_info.flames.afterburn, NULL);
81 
82 	// extra thruster stuff, bah
83 	if (optional_string("+Sec_Normal:"))
84 		stuff_string(species->thruster_secondary_glow_info.normal.filename, F_NAME, MAX_FILENAME_LEN);
85 
86 	// etc.
87 	if (optional_string("+Sec_Afterburn:"))
88 		stuff_string(species->thruster_secondary_glow_info.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
89 
90 	// etc.
91 	if (optional_string("+Ter_Normal:"))
92 		stuff_string(species->thruster_tertiary_glow_info.normal.filename, F_NAME, MAX_FILENAME_LEN);
93 
94 	// etc.
95 	if (optional_string("+Ter_Afterburn:"))
96 		stuff_string(species->thruster_tertiary_glow_info.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
97 
98 	// etc.
99 	if (optional_string("+Dist_Normal:"))
100 		stuff_string(species->thruster_distortion_info.normal.filename, F_NAME, MAX_FILENAME_LEN);
101 
102 	// etc.
103 	if (optional_string("+Dist_Afterburn:"))
104 		stuff_string(species->thruster_distortion_info.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
105 }
106 
parse_thrust_glows(species_info * species,bool no_create)107 void parse_thrust_glows(species_info *species, bool no_create)
108 {
109 	if (!no_create)
110 	{
111 		required_string("$ThrustGlows:");
112 
113 		generic_anim_init(&species->thruster_info.glow.normal, NULL);
114 		generic_anim_init(&species->thruster_info.glow.afterburn, NULL);
115 	}
116 	else if (!optional_string("$ThrustGlows:"))
117 	{
118 		return;
119 	}
120 
121 	if (no_create)
122 	{
123 		if (optional_string("+Normal:"))
124 			stuff_string(species->thruster_info.glow.normal.filename, F_NAME, MAX_FILENAME_LEN);
125 	}
126 	else
127 	{
128 		required_string("+Normal:");
129 		stuff_string(species->thruster_info.glow.normal.filename, F_NAME, MAX_FILENAME_LEN);
130 	}
131 
132 	// if no glow is wanted then clear it
133 	if ( !VALID_FNAME(species->thruster_info.glow.normal.filename) )
134 		generic_anim_init(&species->thruster_info.glow.normal, NULL);
135 
136 	if (no_create)
137 	{
138 		if (optional_string("+Afterburn:"))
139 			stuff_string(species->thruster_info.glow.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
140 	}
141 	else
142 	{
143 		required_string("+Afterburn:");
144 		stuff_string(species->thruster_info.glow.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
145 	}
146 
147 	// if no glow is wanted then clear it
148 	if ( !VALID_FNAME(species->thruster_info.glow.afterburn.filename) )
149 		generic_anim_init(&species->thruster_info.glow.afterburn, NULL);
150 }
151 
parse_species_tbl(const char * filename)152 void parse_species_tbl(const char *filename)
153 {
154 	char species_name[NAME_LENGTH];
155 
156 	try
157 	{
158 		if (filename == NULL)
159 			read_file_text_from_default(defaults_get_file("species_defs.tbl"));
160 		else
161 			read_file_text(filename, CF_TYPE_TABLES);
162 
163 		reset_parse();
164 
165 
166 		// start parsing
167 		required_string("#SPECIES DEFS");
168 
169 		// no longer required: counted automatically
170 		if (optional_string("$NumSpecies:"))
171 		{
172 			int temp;
173 			stuff_int(&temp);
174 		}
175 
176 		// begin reading data
177 		while (required_string_either("#END", "$Species_Name:"))
178 		{
179 			bool no_create = false;
180 			species_info *species, new_species;
181 
182 			species = &new_species;
183 
184 			// Start Species - Get its name
185 			required_string("$Species_Name:");
186 			stuff_string(species_name, F_NAME, NAME_LENGTH);
187 
188 			if (optional_string("+nocreate"))
189 			{
190 				no_create = true;
191 
192 				int i = species_info_lookup(species_name);
193 				if (i >= 0)
194 					species = &Species_info[i];
195 			}
196 			else
197 			{
198 				strcpy_s(species->species_name, species_name);
199 			}
200 
201 			// Goober5000 - IFF
202 			if (optional_string("$Default IFF:"))
203 			{
204 				bool iff_found = false;
205 				char temp_name[NAME_LENGTH];
206 				stuff_string(temp_name, F_NAME, NAME_LENGTH);
207 
208 				// search for it in iffs
209 				for (int iLoop = 0; iLoop < Num_iffs; iLoop++)
210 				{
211 					if (!stricmp(Iff_info[iLoop].iff_name, temp_name))
212 					{
213 						species->default_iff = iLoop;
214 						iff_found = true;
215 					}
216 				}
217 
218 				if (!iff_found)
219 				{
220 					species->default_iff = 0;
221 					Warning(LOCATION, "Species %s default IFF %s not found in iff_defs.tbl!  Defaulting to %s.\n", species->species_name, temp_name, Iff_info[species->default_iff].iff_name);
222 				}
223 			}
224 			else if (!no_create)
225 			{
226 				// we have no idea which it could be, so default to 0
227 				species->default_iff = 0;
228 
229 				// let them know
230 				Warning(LOCATION, "$Default IFF not specified for species %s in species_defs.tbl!  Defaulting to %s.\n", species->species_name, Iff_info[species->default_iff].iff_name);
231 			}
232 
233 			// Goober5000 - FRED color
234 			if (optional_string("$FRED Color:") || optional_string("$FRED Colour:"))
235 			{
236 				stuff_int_list(species->fred_color.a1d, 3, RAW_INTEGER_TYPE);
237 			}
238 			else if (!no_create)
239 			{
240 				// set defaults to Volition's originals
241 				if (!stricmp(species->species_name, "Terran"))
242 				{
243 					species->fred_color.rgb.r = 0;
244 					species->fred_color.rgb.g = 0;
245 					species->fred_color.rgb.b = 192;
246 				}
247 				else if (!stricmp(species->species_name, "Vasudan"))
248 				{
249 					species->fred_color.rgb.r = 0;
250 					species->fred_color.rgb.g = 128;
251 					species->fred_color.rgb.b = 0;
252 				}
253 				else if (!stricmp(species->species_name, "Shivan"))
254 				{
255 					species->fred_color.rgb.r = 192;
256 					species->fred_color.rgb.g = 0;
257 					species->fred_color.rgb.b = 0;
258 				}
259 				else if (!stricmp(species->species_name, "Ancients") || !stricmp(species->species_name, "Ancient"))
260 				{
261 					species->fred_color.rgb.r = 192;
262 					species->fred_color.rgb.g = 0;
263 					species->fred_color.rgb.b = 192;
264 				}
265 				else
266 				{
267 					species->fred_color.rgb.r = 0;
268 					species->fred_color.rgb.g = 0;
269 					species->fred_color.rgb.b = 0;
270 				}
271 
272 				// let them know
273 				Warning(LOCATION, "$FRED Color not specified for species %s in species_defs.tbl!  Defaulting to (%d, %d, %d).\n", species->species_name, species->fred_color.rgb.r, species->fred_color.rgb.g, species->fred_color.rgb.b);
274 			}
275 
276 			// stuff
277 			optional_string("$MiscAnims:");
278 
279 			// Get its Debris Texture
280 			if ((!no_create && required_string("+Debris_Texture:")) || optional_string("+Debris_Texture:"))
281 			{
282 				generic_bitmap_init(&species->debris_texture, NULL);
283 				stuff_string(species->debris_texture.filename, F_NAME, MAX_FILENAME_LEN);
284 			}
285 
286 
287 			// Shield Hit Animation
288 			if (optional_string("+Shield_Hit_ani:")) // Shouldn't be required -- LPine
289 			{
290 				generic_anim_init(&species->shield_anim, NULL);
291 				stuff_string(species->shield_anim.filename, F_NAME, MAX_FILENAME_LEN);
292 			}
293 			else if (!no_create)
294 			{
295 				species->shield_anim.filename[0] = '\0';
296 				species->shield_anim.first_frame = -1; // Landmine to trip up anyone who does end up using this
297 			}
298 
299 
300 			// Thruster Anims
301 			parse_thrust_anims(species, no_create);
302 
303 			// Thruster Glow Anims
304 			parse_thrust_glows(species, no_create);
305 
306 
307 			// Goober5000 - AWACS multiplier
308 			if (optional_string("$AwacsMultiplier:"))
309 			{
310 				stuff_float(&species->awacs_multiplier);
311 			}
312 			else if (!no_create)
313 			{
314 				// set defaults to Volition's originals
315 				if (!stricmp(species->species_name, "Vasudan"))
316 					species->awacs_multiplier = 1.25f;
317 				else if (!stricmp(species->species_name, "Shivan"))
318 					species->awacs_multiplier = 1.50f;
319 				else
320 					species->awacs_multiplier = 1.0f;
321 
322 				// let them know
323 				Warning(LOCATION, "$AwacsMultiplier not specified for species %s in species_defs.tbl!  Defaulting to %.2f.\n", species->species_name, species->awacs_multiplier);
324 			}
325 
326 			// Goober5000 - countermeasure type
327 			// (we won't be able to resolve it until after we've parsed the weapons table)
328 			if (optional_string("$Countermeasure type:"))
329 				stuff_string(species->cmeasure_name, F_NAME, NAME_LENGTH);
330 
331 			if (optional_string("$Borrows Briefing Icons from:")) {
332 				char temp_name[NAME_LENGTH];
333 				stuff_string(temp_name, F_NAME, NAME_LENGTH);
334 				int idx = species_info_lookup(temp_name);
335 				if (idx >= 0)
336 					species->borrows_bii_index_species = idx;
337 				else {
338 					Warning(LOCATION, "Species %s for '$Borrows Briefing Icons from' in Species %s is either invalid or not yet parsed."
339 									  "The Species doing the borrowing must be defined after the Species it is borrowing from\n", temp_name, species->species_name);
340 				}
341 			}
342 
343 
344 			// don't add new entry if this is just a modified one
345 			if (!no_create)
346 				Species_info.push_back(new_species);
347 		}
348 
349 		required_string("#END");
350 	}
351 	catch (const parse::ParseException& e)
352 	{
353 		mprintf(("TABLES: Unable to parse '%s'!  Error message = %s.\n", (filename) ? filename : NOX("<default species_defs.tbl>"), e.what()));
354 		return;
355 	}
356 }
357 
358 int Species_initted = 0;
359 
species_init()360 void species_init()
361 {
362 	if (Species_initted)
363 		return;
364 
365 	Species_info.clear();
366 
367 
368 	if (cf_exists_full("species_defs.tbl", CF_TYPE_TABLES))
369 		parse_species_tbl("species_defs.tbl");
370 	else
371 		parse_species_tbl(NULL);
372 
373 	parse_modular_table("*-sdf.tbm", parse_species_tbl);
374 
375 
376 	Species_initted = 1;
377 }
378 
species_info_lookup(const char * species_name)379 int species_info_lookup(const char *species_name)
380 {
381 	for (int i = 0; i < static_cast<int>(Species_info.size()); i++)
382 	{
383 		if (!stricmp(Species_info[i].species_name, species_name))
384 			return i;
385 	}
386 
387 	return -1;
388 }
389