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