FS2_Open
Open source remastering of the Freespace 2 engine
ship.cpp
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Volition, Inc. 1999. All rights reserved.
4  *
5  * All source code herein is the property of Volition, Inc. You may not sell
6  * or otherwise commercially exploit the source or things you created based on the
7  * source.
8  *
9 */
10 
11 
12 
13 #include <setjmp.h>
14 
15 #include "ai/aigoals.h"
16 #include "asteroid/asteroid.h"
17 #include "autopilot/autopilot.h"
18 #include "cmdline/cmdline.h"
19 #include "cmeasure/cmeasure.h"
20 #include "debugconsole/console.h"
21 #include "fireball/fireballs.h"
22 #include "freespace2/freespace.h"
24 #include "gamesnd/eventmusic.h"
25 #include "gamesnd/gamesnd.h"
26 #include "globalincs/alphacolors.h"
27 #include "globalincs/def_files.h"
28 #include "globalincs/linklist.h"
30 #include "hud/hud.h"
31 #include "hud/hudartillery.h"
32 #include "hud/hudets.h"
33 #include "hud/hudmessage.h"
34 #include "hud/hudshield.h"
35 #include "hud/hudsquadmsg.h"
36 #include "hud/hudtargetbox.h"
37 #include "hud/hudwingmanstatus.h"
38 #include "iff_defs/iff_defs.h"
39 #include "io/joy_ff.h"
40 #include "io/timer.h"
41 #include "jumpnode/jumpnode.h"
42 #include "lighting/lighting.h"
43 #include "localization/localize.h"
44 #include "math/fvi.h"
45 #include "math/staticrand.h"
46 #include "math/vecmat.h"
48 #include "mission/missionlog.h"
49 #include "mission/missionmessage.h"
51 #include "missionui/redalert.h"
52 #include "mod_table/mod_table.h"
53 #include "model/model.h"
54 #include "model/modelrender.h"
55 #include "nebula/neb.h"
56 #include "network/multimsgs.h"
57 #include "network/multiutil.h"
58 #include "object/deadobjectdock.h"
59 #include "object/objcollide.h"
60 #include "object/object.h"
61 #include "object/objectdock.h"
62 #include "object/objectshield.h"
63 #include "object/objectsnd.h"
64 #include "object/waypoint.h"
65 #include "parse/parselo.h"
66 #include "parse/scripting.h"
67 #include "particle/particle.h"
68 #include "playerman/player.h"
69 #include "radar/radar.h"
70 #include "radar/radarsetup.h"
71 #include "render/3d.h"
72 #include "ship/afterburner.h"
73 #include "ship/ship.h"
74 #include "ship/shipcontrails.h"
75 #include "ship/shipfx.h"
76 #include "ship/shiphit.h"
77 #include "ship/subsysdamage.h"
79 #include "weapon/beam.h"
80 #include "weapon/corkscrew.h"
81 #include "weapon/emp.h"
82 #include "weapon/flak.h" //phreak addded 11/05/02 for flak primaries
83 #include "weapon/shockwave.h"
84 #include "weapon/swarm.h"
85 #include "weapon/weapon.h"
86 
87 #define NUM_SHIP_SUBSYSTEM_SETS 20 // number of subobject sets to use (because of the fact that it's a linked list,
88  // we can't easily go fully dynamic)
89 
90 #define NUM_SHIP_SUBSYSTEMS_PER_SET 200 // Reduced from 1000 to 400 by MK on 4/1/98. DTP; bumped from 700 to 2100
91  // Reduced to 200 by taylor on 3/13/07 -- it's managed in dynamically allocated sets now
92  // Highest I saw was 164 in sm2-03a which Sandeep says has a lot of ships.
93  // JAS: sm3-01 needs 460. You cannot know this number until *all* ships
94  // have warped in. So I put code in the paging code which knows all ships
95  // that will warp in.
96 
97 static int Num_ship_subsystems = 0;
98 static int Num_ship_subsystems_allocated = 0;
99 
100 static ship_subsys *Ship_subsystems[NUM_SHIP_SUBSYSTEM_SETS] = { NULL };
102 
103 extern bool splodeing;
104 extern float splode_level;
105 extern int splodeingtexture;
106 
107 extern int Cmdline_nohtl;
108 
109 extern void fs2netd_add_table_validation(const char *tblname);
110 
111 #define SHIP_REPAIR_SUBSYSTEM_RATE 0.01f
112 
114 #ifndef NDEBUG
116 int Ship_auto_repair = 1; // flag to indicate auto-repair of subsystem should occur
117 extern void render_path_points(object *objp);
118 #endif
119 
120 int Num_wings = 0;
123 
127 
129 int ships_inited = 0;
130 int armor_inited = 0;
131 
132 int Starting_wings[MAX_STARTING_WINGS]; // wings player starts a mission with (-1 = none)
133 
134 // Goober5000
137 
138 // Goober5000
142 
144 engine_wash_info *get_engine_wash_pointer(char* engine_wash_name);
145 
146 void ship_reset_disabled_physics(object *objp, int ship_class);
147 
148 // forward declaring for parse_ship()
149 int ship_info_lookup_sub(const char *token);
150 
151 // information for ships which have exited the game
153 
157 int Player_ship_class; // needs to be player specific, move to player structure
158 
159 #define SHIP_OBJ_USED (1<<0) // flag used in ship_obj struct
160 #define MAX_SHIP_OBJS MAX_SHIPS // max number of ships tracked in ship list
161 ship_obj Ship_objs[MAX_SHIP_OBJS]; // array used to store ship object indexes
162 ship_obj Ship_obj_list; // head of linked list of ship_obj structs
163 
167 
169 
172 
174  { "ignore subsystem armor", SAF_IGNORE_SS_ARMOR, 0 }
175 };
176 
177 const int Num_armor_flags = sizeof(Armor_flags)/sizeof(flag_def_list);
178 
180  { "Bank right", MT_BANK_RIGHT, 0 },
181  { "Bank left", MT_BANK_LEFT, 0 },
182  { "Pitch up", MT_PITCH_UP, 0 },
183  { "Pitch down", MT_PITCH_DOWN, 0 },
184  { "Roll right", MT_ROLL_RIGHT, 0 },
185  { "Roll left", MT_ROLL_LEFT, 0 },
186  { "Slide right", MT_SLIDE_RIGHT, 0 },
187  { "Slide left", MT_SLIDE_LEFT, 0 },
188  { "Slide up", MT_SLIDE_UP, 0 },
189  { "Slide down", MT_SLIDE_DOWN, 0 },
190  { "Forward", MT_FORWARD, 0 },
191  { "Reverse", MT_REVERSE, 0 }
192 };
193 
194 const int Num_man_types = sizeof(Man_types)/sizeof(flag_def_list);
195 
196 // Goober5000 - I figured we should keep this separate
197 // from Comm_orders, considering how I redid it :p
198 // (and also because we may want to change either
199 // the order text or the flag text in the future)
201  // common stuff
202  { "attack ship", ATTACK_TARGET_ITEM, 0 },
203  { "disable ship", DISABLE_TARGET_ITEM, 0 },
204  { "disarm ship", DISARM_TARGET_ITEM, 0 },
205  { "disable subsys", DISABLE_SUBSYSTEM_ITEM, 0 },
206  { "guard ship", PROTECT_TARGET_ITEM, 0 },
207  { "ignore ship", IGNORE_TARGET_ITEM, 0 },
208  { "form on wing", FORMATION_ITEM, 0 },
209  { "cover me", COVER_ME_ITEM, 0 },
210  { "attack any", ENGAGE_ENEMY_ITEM, 0 },
211 
212  // transports mostly
213  { "dock", CAPTURE_TARGET_ITEM, 0 },
214 
215  // support ships
216  { "rearm me", REARM_REPAIR_ME_ITEM, 0 },
217  { "abort rearm", ABORT_REARM_REPAIR_ITEM, 0 },
218 
219  // all ships
220  { "depart", DEPART_ITEM, 0 },
221 
222  // extra stuff for support
223  { "stay near me", STAY_NEAR_ME_ITEM, 0 },
224  { "stay near ship", STAY_NEAR_TARGET_ITEM, 0 },
225  { "keep safe dist", KEEP_SAFE_DIST_ITEM, 0 }
226 };
227 
228 const int Num_player_orders = sizeof(Player_orders)/sizeof(flag_def_list);
229 
230 // Use the last parameter here to tell the parser whether to stuff the flag into flags or flags2
232  { "untargetable", MSS_FLAG_UNTARGETABLE, 0 },
233  { "carry no damage", MSS_FLAG_CARRY_NO_DAMAGE, 0 },
234  { "use multiple guns", MSS_FLAG_USE_MULTIPLE_GUNS, 0 },
235  { "fire down normals", MSS_FLAG_FIRE_ON_NORMAL, 0 },
236  { "check hull", MSS_FLAG_TURRET_HULL_CHECK, 0 },
237  { "fixed firingpoints", MSS_FLAG_TURRET_FIXED_FP, 0 },
238  { "salvo mode", MSS_FLAG_TURRET_SALVO, 0 },
239  { "no subsystem targeting", MSS_FLAG_NO_SS_TARGETING, 0 },
240  { "fire on target", MSS_FLAG_FIRE_ON_TARGET, 0 },
241  { "reset when idle", MSS_FLAG_TURRET_RESET_IDLE, 0 },
242  { "carry shockwave", MSS_FLAG_CARRY_SHOCKWAVE, 0 },
243  { "allow landing", MSS_FLAG_ALLOW_LANDING, 0 },
244  { "target requires fov", MSS_FLAG_FOV_REQUIRED, 0 },
245  { "fov edge checks", MSS_FLAG_FOV_EDGE_CHECK, 0 },
246  { "no replace", MSS_FLAG_NO_REPLACE, 0 },
247  { "no live debris", MSS_FLAG_NO_LIVE_DEBRIS, 0 },
248  { "ignore if dead", MSS_FLAG_IGNORE_IF_DEAD, 0 },
249  { "allow vanishing", MSS_FLAG_ALLOW_VANISHING, 0 },
250  { "damage as hull", MSS_FLAG_DAMAGE_AS_HULL, 0 },
251  { "starts locked", MSS_FLAG_TURRET_LOCKED, 0 },
252  { "no aggregate", MSS_FLAG_NO_AGGREGATE, 0 },
253  { "wait for animation", MSS_FLAG_TURRET_ANIM_WAIT, 0 },
254  { "play fire sound for player", MSS_FLAG2_PLAYER_TURRET_SOUND, 1},
255  { "only target if can fire", MSS_FLAG2_TURRET_ONLY_TARGET_IF_CAN_FIRE, 1},
256  { "no disappear", MSS_FLAG2_NO_DISAPPEAR, 1},
257  { "collide submodel", MSS_FLAG2_COLLIDE_SUBMODEL, 1},
258  { "allow destroyed rotation", MSS_FLAG2_DESTROYED_ROTATION, 1},
259  { "turret use ammo", MSS_FLAG2_TURRET_USE_AMMO, 1},
260  { "autorepair if disabled", MSS_FLAG2_AUTOREPAIR_IF_DISABLED, 1},
261  { "don't autorepair if disabled", MSS_FLAG2_NO_AUTOREPAIR_IF_DISABLED, 1},
262  { "share fire direction", MSS_FLAG2_SHARE_FIRE_DIRECTION, 1 }
263 };
264 
266 
267 
268 // NOTE: a var of:
269 // "0" means that it's a SIF_* flag
270 // "1" means that it's a SIF2_* flag
271 // "255" means that the option is obsolete and a warning should be generated
273  { "no_collide", SIF_NO_COLLIDE, 0 },
274  { "player_ship", SIF_PLAYER_SHIP, 0 },
275  { "default_player_ship", SIF_DEFAULT_PLAYER_SHIP, 0 },
276  { "repair_rearm", SIF_SUPPORT, 0 },
277  { "cargo", SIF_CARGO, 0 },
278  { "fighter", SIF_FIGHTER, 0 },
279  { "bomber", SIF_BOMBER, 0 },
280  { "transport", SIF_TRANSPORT, 0 },
281  { "freighter", SIF_FREIGHTER, 0 },
282  { "capital", SIF_CAPITAL, 0 },
283  { "supercap", SIF_SUPERCAP, 0 },
284  { "drydock", SIF_DRYDOCK, 0 },
285  { "cruiser", SIF_CRUISER, 0 },
286  { "navbuoy", SIF_NAVBUOY, 0 },
287  { "sentrygun", SIF_SENTRYGUN, 0 },
288  { "escapepod", SIF_ESCAPEPOD, 0 },
289  { "stealth", SIF_STEALTH, 0 },
290  { "no type", SIF_NO_SHIP_TYPE, 0 },
291  { "ship copy", SIF_SHIP_COPY, 0 },
292  { "in tech database", SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M, 0 },
293  { "in tech database multi", SIF_IN_TECH_DATABASE_M, 0 },
294  { "dont collide invisible", SIF_SHIP_CLASS_DONT_COLLIDE_INVIS, 0 },
295  { "big damage", SIF_BIG_DAMAGE, 0 },
296  { "corvette", SIF_CORVETTE, 0 },
297  { "gas miner", SIF_GAS_MINER, 0 },
298  { "awacs", SIF_AWACS, 0 },
299  { "knossos", SIF_KNOSSOS_DEVICE, 0 },
300  { "no_fred", SIF_NO_FRED, 0 },
301  { "flash", SIF2_FLASH, 1 },
302  { "surface shields", SIF2_SURFACE_SHIELDS, 1 },
303  { "show ship", SIF2_SHOW_SHIP_MODEL, 1 },
304  { "generate icon", SIF2_GENERATE_HUD_ICON, 1 },
305  { "no weapon damage scaling", SIF2_DISABLE_WEAPON_DAMAGE_SCALING, 1 },
306  { "gun convergence", SIF2_GUN_CONVERGENCE, 1 },
307  { "no thruster geometry noise", SIF2_NO_THRUSTER_GEO_NOISE, 1 },
308  { "intrinsic no shields", SIF2_INTRINSIC_NO_SHIELDS, 1 },
309  { "dynamic primary linking", SIF2_DYN_PRIMARY_LINKING, 1 },
310  { "no primary linking", SIF2_NO_PRIMARY_LINKING, 1 },
311  { "no pain flash", SIF2_NO_PAIN_FLASH, 1 },
312  { "no ets", SIF2_NO_ETS, 1 },
313  { "no lighting", SIF2_NO_LIGHTING, 1 },
314  { "auto spread shields", SIF2_AUTO_SPREAD_SHIELDS, 1 },
315  { "model point shields", SIF2_MODEL_POINT_SHIELDS, 1 },
316  { "repair disabled subsystems", SIF2_SUBSYS_REPAIR_WHEN_DISABLED, 1},
317 
318  // to keep things clean, obsolete options go last
319  { "ballistic primaries", -1, 255 }
320 };
321 
322 const int Num_ship_flags = sizeof(Ship_flags) / sizeof(flag_def_list);
323 
324 /*
325 ++Here be dragons.. err.. begins the section for the ai targeting revision
326 ++ First flag_def_list (& its size) for object types (ship/asteroid/weapon)
327 ++ List of reasonable object flags (from object.h)
328 ++ List of potentially useful ship class flags
329 ++ List of potentially useful weapon class flags
330 */
332  { "ship", OBJ_SHIP, 0 },
333  { "asteroid", OBJ_ASTEROID, 0 },
334  { "weapon", OBJ_WEAPON, 0 }
335 };
336 
337 const int num_ai_tgt_objects = sizeof(ai_tgt_objects) / sizeof(flag_def_list);
338 
340  { "no shields", OF_NO_SHIELDS, 0 },
341  { "targetable as bomb", OF_TARGETABLE_AS_BOMB, 0 }
342 };
343 
345 
347  { "afterburners", SIF_AFTERBURNER, 0 },
348  { "big damage", SIF_BIG_DAMAGE, 0 },
349  { "has awacs", SIF_HAS_AWACS, 0 }
350 };
351 
353 
355  { "bomb", WIF_BOMB, 0 },
356  { "huge damage", WIF_HUGE, 0 },
357  { "supercap damage", WIF_SUPERCAP, 0 },
358  { "bomber+", WIF_BOMBER_PLUS, 0 },
359  { "electronics", WIF_ELECTRONICS, 0 },
360  { "puncture", WIF_PUNCTURE, 0 },
361  { "emp", WIF_EMP, 0 },
362  { "heat seeking", WIF_HOMING_HEAT, 0 },
363  { "aspect seeking", WIF_HOMING_ASPECT, 0 },
364  { "engine seeking", WIF_HOMING_JAVELIN, 0 },
365  { "pierce shields", WIF2_PIERCE_SHIELDS, 1 },
366  { "local ssm", WIF2_LOCAL_SSM, 1 },
367  { "capital+", WIF2_CAPITAL_PLUS, 1 }
368 };
369 
370 // Constant for flag, Name of flag, In flags or flags2
371 // When adding new flags remember to bump MAX_SHIP_FLAG_NAMES in ship.h
373  {SF_VAPORIZE, "vaporize", 1, },
374  {SF_WARP_BROKEN, "break-warp", 1, },
375  {SF_WARP_NEVER, "never-warp", 1, },
376  {SF_SCANNABLE, "scannable", 1, },
377  {SF_CARGO_REVEALED, "cargo-known", 1, },
378  {SF_HIDDEN_FROM_SENSORS, "hidden-from-sensors", 1, },
379  {SF2_STEALTH, "stealth", 2, },
380  {SF2_FRIENDLY_STEALTH_INVIS, "friendly-stealth-invisible", 2, },
381  {SF2_HIDE_SHIP_NAME, "hide-ship-name", 2, },
382  {SF2_PRIMITIVE_SENSORS, "primitive-sensors", 2, },
383  {SF2_AFTERBURNER_LOCKED, "afterburners-locked", 2, },
384  {SF2_PRIMARIES_LOCKED, "primaries-locked", 2, },
385  {SF2_SECONDARIES_LOCKED, "secondaries-locked", 2, },
386  {SF2_NO_SUBSPACE_DRIVE, "no-subspace-drive", 2, },
387  {SF2_DONT_COLLIDE_INVIS, "don't-collide-invisible", 2, },
388  {SF2_NO_ETS, "no-ets", 2, },
389  {SF2_TOGGLE_SUBSYSTEM_SCANNING, "toggle-subsystem-scanning", 2, },
390  {SF2_NO_SECONDARY_LOCKON, "no-secondary-lock-on", 2, },
391  {SF2_NO_DISABLED_SELF_DESTRUCT, "no-disabled-self-destruct", 2, },
392 };
393 
395 
397 
398 static int Laser_energy_out_snd_timer; // timer so we play out of laser sound effect periodically
399 static int Missile_out_snd_timer; // timer so we play out of laser sound effect periodically
400 
402 
403 // I don't want to do an AI cargo check every frame, so I made a global timer to limit check to
404 // every SHIP_CARGO_CHECK_INTERVAL ms. Didn't want to make a timer in each ship struct. Ensure
405 // inited to 1 at mission start.
406 static int Ship_cargo_check_timer;
407 
408 static int Thrust_anim_inited = 0;
409 
410 int ship_get_subobj_model_num(ship_info* sip, char* subobj_name);
411 
413 
419 {
420  Ship_objs[index].flags = 0;
421  Ship_objs[index].next = NULL;
422  Ship_objs[index].prev = (ship_obj*)-1;
423 }
424 
429 {
430  int i;
431 
432  for (i=0; i<MAX_STARTING_WINGS; i++)
433  {
434  if (shipp->wingnum == Starting_wings[i])
435  return 1;
436  }
437 
438  for (i=0; i<MAX_TVT_WINGS; i++)
439  {
440  if (shipp->wingnum == TVT_wings[i])
441  return 1;
442  }
443 
444  // not in
445  return 0;
446 }
447 
452 {
453  int i;
454 
455  list_init(&Ship_obj_list);
456  for ( i = 0; i < MAX_SHIP_OBJS; i++ ) {
458  }
459 }
460 
465 int ship_obj_list_add(int objnum)
466 {
467  int i;
468 
469  for ( i = 0; i < MAX_SHIP_OBJS; i++ ) {
470  if ( !(Ship_objs[i].flags & SHIP_OBJ_USED) )
471  break;
472  }
473  if ( i == MAX_SHIP_OBJS ) {
474  Error(LOCATION, "Fatal Error: Ran out of ship object nodes\n");
475  return -1;
476  }
477 
478  Ship_objs[i].flags = 0;
479  Ship_objs[i].objnum = objnum;
480  list_append(&Ship_obj_list, &Ship_objs[i]);
481  Ship_objs[i].flags |= SHIP_OBJ_USED;
482 
483  return i;
484 }
485 
491 {
492  Assert(index >= 0 && index < MAX_SHIP_OBJS);
493  list_remove( Ship_obj_list, &Ship_objs[index]);
495 }
496 
501 {
502  object *objp;
503 
505 
506  for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
507  if ( objp->type == OBJ_SHIP ) {
508  Ships[objp->instance].ship_list_index = ship_obj_list_add(OBJ_INDEX(objp));
509  }
510  }
511 }
512 
514 {
515  Assert(index >= 0 && index < MAX_SHIP_OBJS);
516  return &Ship_objs[index];
517 }
518 
523 {
524  int count;
525  ship_obj *so;
526 
527  count = 0;
528  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )
529  count++;
530 
531  return count;
532 }
533 
535 {
536  ewi->name[0] = '\0';
537  ewi->angle = PI / 10.0f;
538  ewi->radius_mult = 1.0f;
539  ewi->length = 500.0f;
540  ewi->intensity = 1.0f;
541 }
542 
546 void parse_engine_wash(bool replace)
547 {
548  engine_wash_info ewt;
549  engine_wash_info_init(&ewt);
550 
551  engine_wash_info *ewp;
552  bool create_if_not_found = true;
553 
554  // name of engine wash info
555  required_string("$Name:");
557 
558  if(optional_string("+nocreate")) {
559  if(!replace) {
560  Warning(LOCATION, "+nocreate flag used for engine wash in non-modular table");
561  }
562  create_if_not_found = false;
563  }
564 
565  //Does this engine wash exist already?
566  //If so, load this new info into it
567  //Otherwise, increment Num_engine_wash_types
568  ewp = get_engine_wash_pointer(ewt.name);
569  if(ewp != NULL)
570  {
571  if(replace)
572  {
573  nprintf(("Warning", "More than one version of engine wash %s exists; using newer version.", ewt.name));
574  }
575  else
576  {
577  Error(LOCATION, "Error: Engine wash %s already exists. All engine wash names must be unique.", ewt.name);
578  }
579  }
580  else
581  {
582  //Don't create engine wash if it has +nocreate and is in a modular table.
583  if(!create_if_not_found && replace)
584  {
585  if ( !skip_to_start_of_string_either("$Name:", "#End")) {
586  Int3();
587  }
588  return;
589  }
590  Engine_wash_info.push_back(ewt);
591  ewp = &Engine_wash_info[Num_engine_wash_types++];
592  }
593 
594 
595  // half angle of cone of wash from thruster
596  if(optional_string("$Angle:"))
597  {
598  stuff_float(&ewp->angle);
599  ewp->angle *= (PI / 180.0f);
600  }
601 
602  // radius multiplier for hemisphere around thruster pt
603  if(optional_string("$Radius Mult:")) {
604  stuff_float(&ewp->radius_mult);
605  }
606 
607  // length of cone
608  if(optional_string("$Length:")) {
609  stuff_float(&ewp->length);
610  }
611 
612  // intensity inside hemisphere (or at 0 distance from frustated cone)
613  if(optional_string("$Intensity:")) {
614  stuff_float(&ewp->intensity);
615  }
616 }
617 
618 char *Warp_types[] = {
619  "Default",
620  "Knossos",
621  "Babylon5",
622  "Galactica",
623  "Homeworld",
624  "Hyperspace",
625 };
626 
627 int Num_warp_types = sizeof(Warp_types)/sizeof(char*);
628 
629 int warptype_match(char *p)
630 {
631  int i;
632  for(i = 0; i < Num_warp_types; i++)
633  {
634  if(!stricmp(Warp_types[i], p))
635  return i;
636  }
637 
638  return -1;
639 }
640 
641 char *Lightning_types[] = {
642  "None",
643  "Default",
644 };
645 
646 int Num_lightning_types = sizeof(Lightning_types)/sizeof(char*);
647 
649 {
650  int i;
651  for(i = 0; i < Num_lightning_types; i++)
652  {
653  if(!stricmp(Lightning_types[i], p))
654  return i;
655  }
656 
657  return -1;
658 }
659 
660 // Kazan -- Volition had this set to 1500, Set it to 4K for WC Saga
661 #define SHIP_MULTITEXT_LENGTH 4096
662 #define DEFAULT_DELTA_BANK_CONST 0.5f
663 
664 #define CHECK_THEN_COPY(attribute) \
665 do {\
666  if (other.attribute != NULL)\
667  attribute = vm_strdup( other.attribute );\
668 } while(false)
669 
670 void ship_info::clone(const ship_info& other)
671 {
672  strcpy_s(name, other.name);
673  strcpy_s(alt_name, other.alt_name);
675  species = other.species;
676  class_type = other.class_type;
677 
684 
686 
690 
693  strcpy_s(pof_file, other.pof_file);
696  memcpy(detail_distance, other.detail_distance, sizeof(int) * MAX_SHIP_DETAIL_LEVELS);
698 
699  // I'm not sure if these three are A) a good idea or B) relevant at all. -MageKing17
701  model_num = other.model_num;
703 
705  density = other.density;
706  damp = other.damp;
707  rotdamp = other.rotdamp;
709  max_vel = other.max_vel;
710  max_rotvel = other.max_rotvel;
713  max_rear_vel = other.max_rear_vel;
716  slide_accel = other.slide_accel;
717  slide_decel = other.slide_decel;
718 
723  warpin_speed = other.warpin_speed;
724  warpin_time = other.warpin_time;
726  warpin_type = other.warpin_type;
727 
734  warpout_time = other.warpout_time;
736  warpout_type = other.warpout_type;
737 
739 
740  flags = other.flags;
741  flags2 = other.flags2;
742  ai_class = other.ai_class;
743  max_speed = other.max_speed;
744  min_speed = other.min_speed;
745  max_accel = other.max_accel;
746 
749 
750  memcpy(&shockwave, &other.shockwave, sizeof(shockwave_create_info));
762 
763  impact_spew = other.impact_spew;
764  damage_spew = other.damage_spew;
768 
780 
781  if ( other.n_subsystems > 0 ) {
782  if( n_subsystems < 1 ) {
784  } else {
785  for ( int i = 0; i < n_subsystems; i++ ) {
786  for ( int j = 0; j < subsystems[i].n_triggers; j++ ) {
787  if ( i < other.n_subsystems ) {
789  } else {
790  vm_free(subsystems[i].triggers);
791  }
792  }
793  }
795  }
796  Assert( subsystems != NULL );
797  for ( int i = n_subsystems; i < other.n_subsystems; i++ ) {
799  }
800 
801  for ( int i = 0; i < other.n_subsystems; i++ ) {
802  queued_animation* triggers = subsystems[i].triggers;
803  subsystems[i] = other.subsystems[i];
804  subsystems[i].triggers = triggers;
805  memcpy(subsystems[i].triggers, other.subsystems[i].triggers, sizeof(queued_animation) * (subsystems[i].n_triggers));
806  }
807  }
808  n_subsystems = other.n_subsystems;
809 
810  power_output = other.power_output;
815 
823 
825  cmeasure_max = other.cmeasure_max;
826 
829  memcpy(primary_bank_ammo_capacity, other.primary_bank_ammo_capacity, sizeof(int) * MAX_SHIP_PRIMARY_BANKS);
830 
833  memcpy(secondary_bank_ammo_capacity, other.secondary_bank_ammo_capacity, sizeof(int) * MAX_SHIP_SECONDARY_BANKS);
834 
835  memcpy(draw_primary_models, other.draw_primary_models, sizeof(bool) * MAX_SHIP_PRIMARY_BANKS);
836  memcpy(draw_secondary_models, other.draw_secondary_models, sizeof(bool) * MAX_SHIP_SECONDARY_BANKS);
838 
846 
847  // ...Hmm. A memcpy() seems slightly overkill here, but I've settled into the pattern of "array gets memcpy'd", so... -MageKing17
848  memcpy(shield_point_augment_ctrls, other.shield_point_augment_ctrls, sizeof(int) * 4);
849 
852 
856 
857  closeup_pos = other.closeup_pos;
858  closeup_zoom = other.closeup_zoom;
859 
860  memcpy(allowed_weapons, other.allowed_weapons, sizeof(int) * MAX_WEAPON_TYPES);
861 
863  memcpy(allowed_bank_restricted_weapons, other.allowed_bank_restricted_weapons, sizeof(int) * MAX_SHIP_WEAPONS * MAX_WEAPON_TYPES);
864 
871 
876 
877  score = other.score;
878 
879  scan_time = other.scan_time;
880 
881  memcpy(ct_info, other.ct_info, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
882  ct_count = other.ct_count;
883 
885  memcpy(nondark_colors, other.nondark_colors, sizeof(ubyte) * MAX_NONDARK_COLORS * 3);
886 
887  memcpy(shield_color, other.shield_color, sizeof(ubyte) * 3);
888 
891 
897 
900 
901  memcpy(&thruster_flame_info, &other.thruster_flame_info, sizeof(thrust_pair));
902  memcpy(&thruster_glow_info, &other.thruster_glow_info, sizeof(thrust_pair));
906 
914 
916 
919 
921 
924 
925  can_glide = other.can_glide;
926  glide_cap = other.glide_cap;
931 
932  autoaim_fov = other.autoaim_fov;
933 
936 
937  engine_snd = other.engine_snd;
941 
942  ship_sounds = other.ship_sounds;
943 
945  memcpy(maneuvering, other.maneuvering, sizeof(man_thruster) * MAX_MAN_THRUSTERS);
946 
951 
952  memcpy(ship_iff_info, other.ship_iff_info, sizeof(int) * MAX_IFFS * MAX_IFFS);
953 
954  aiming_flags = other.aiming_flags;
958 
960 
962 
964 
966  hud_gauges = other.hud_gauges;
967  hud_enabled = other.hud_enabled;
968  hud_retail = other.hud_retail;
969 
970  displays = other.displays;
971 
972  pathMetadata = other.pathMetadata;
973 
975 }
976 
977 void ship_info::move(ship_info&& other)
978 {
979  std::swap(name, other.name);
980  std::swap(alt_name, other.alt_name);
981  std::swap(short_name, other.short_name);
982  species = other.species;
983  class_type = other.class_type;
984 
985  std::swap(type_str, other.type_str);
986  std::swap(maneuverability_str, other.maneuverability_str);
987  std::swap(armor_str, other.armor_str);
988  std::swap(manufacturer_str, other.manufacturer_str);
989  std::swap(desc, other.desc);
990  std::swap(tech_desc, other.tech_desc);
991 
992  std::swap(tech_title, other.tech_title);
993 
994  std::swap(ship_length, other.ship_length);
995  std::swap(gun_mounts, other.gun_mounts);
996  std::swap(missile_banks, other.missile_banks);
997 
998  std::swap(cockpit_pof_file, other.cockpit_pof_file);
999  std::swap(cockpit_offset, other.cockpit_offset);
1000  std::swap(pof_file, other.pof_file);
1001  std::swap(pof_file_hud, other.pof_file_hud);
1002  num_detail_levels = other.num_detail_levels;
1003  std::swap(detail_distance, other.detail_distance);
1004  collision_lod = other.collision_lod;
1005 
1006  cockpit_model_num = other.cockpit_model_num;
1007  model_num = other.model_num;
1008  model_num_hud = other.model_num_hud;
1009 
1010  hud_target_lod = other.hud_target_lod;
1011  density = other.density;
1012  damp = other.damp;
1013  rotdamp = other.rotdamp;
1014  delta_bank_const = other.delta_bank_const;
1015  std::swap(max_vel, other.max_vel);
1016  std::swap(max_rotvel, other.max_rotvel);
1017  std::swap(rotation_time, other.rotation_time);
1018  srotation_time = other.srotation_time;
1019  max_rear_vel = other.max_rear_vel;
1020  forward_accel = other.forward_accel;
1021  forward_decel = other.forward_decel;
1022  slide_accel = other.slide_accel;
1023  slide_decel = other.slide_decel;
1024 
1025  std::swap(warpin_anim, other.warpin_anim);
1026  warpin_radius = other.warpin_radius;
1027  warpin_snd_start = other.warpin_snd_start;
1028  warpin_snd_end = other.warpin_snd_end;
1029  warpin_speed = other.warpin_speed;
1030  warpin_time = other.warpin_time;
1031  warpin_decel_exp = other.warpin_decel_exp;
1032  warpin_type = other.warpin_type;
1033 
1034  std::swap(warpout_anim, other.warpout_anim);
1035  warpout_radius = other.warpout_radius;
1036  warpout_snd_start = other.warpout_snd_start;
1037  warpout_snd_end = other.warpout_snd_end;
1038  warpout_engage_time = other.warpout_engage_time;
1039  warpout_speed = other.warpout_speed;
1040  warpout_time = other.warpout_time;
1041  warpout_accel_exp = other.warpout_accel_exp;
1042  warpout_type = other.warpout_type;
1043 
1044  warpout_player_speed = other.warpout_player_speed;
1045 
1046  flags = other.flags;
1047  flags2 = other.flags2;
1048  ai_class = other.ai_class;
1049  max_speed = other.max_speed;
1050  min_speed = other.min_speed;
1051  max_accel = other.max_accel;
1052 
1053  collision_damage_type_idx = other.collision_damage_type_idx;
1054  std::swap(collision_physics, other.collision_physics);
1055 
1056  std::swap(shockwave, other.shockwave);
1057  explosion_propagates = other.explosion_propagates;
1058  big_exp_visual_rad = other.big_exp_visual_rad;
1059  prop_exp_rad_mult = other.prop_exp_rad_mult;
1060  death_roll_r_mult = other.death_roll_r_mult;
1061  death_fx_r_mult = other.death_fx_r_mult;
1062  death_roll_time_mult = other.death_roll_time_mult;
1063  death_roll_base_time = other.death_roll_base_time;
1064  death_fx_count = other.death_fx_count;
1065  shockwave_count = other.shockwave_count;
1066  std::swap(explosion_bitmap_anims, other.explosion_bitmap_anims);
1067  vaporize_chance = other.vaporize_chance;
1068 
1069  std::swap(impact_spew, other.impact_spew);
1070  std::swap(damage_spew, other.damage_spew);
1071  std::swap(split_particles, other.split_particles);
1072  std::swap(knossos_end_particles, other.knossos_end_particles);
1073  std::swap(regular_end_particles, other.regular_end_particles);
1074 
1075  debris_min_lifetime = other.debris_min_lifetime;
1076  debris_max_lifetime = other.debris_max_lifetime;
1077  debris_min_speed = other.debris_min_speed;
1078  debris_max_speed = other.debris_max_speed;
1079  debris_min_rotspeed = other.debris_min_rotspeed;
1080  debris_max_rotspeed = other.debris_max_rotspeed;
1081  debris_damage_type_idx = other.debris_damage_type_idx;
1082  debris_min_hitpoints = other.debris_min_hitpoints;
1083  debris_max_hitpoints = other.debris_max_hitpoints;
1084  debris_damage_mult = other.debris_damage_mult;
1085  debris_arc_percent = other.debris_arc_percent;
1086 
1087  std::swap(subsystems, other.subsystems);
1088  std::swap(n_subsystems, other.n_subsystems);
1089 
1090  power_output = other.power_output;
1091  max_overclocked_speed = other.max_overclocked_speed;
1092  max_weapon_reserve = other.max_weapon_reserve;
1093  max_shield_regen_per_second = other.max_shield_regen_per_second;
1094  max_weapon_regen_per_second = other.max_weapon_regen_per_second;
1095 
1096  std::swap(afterburner_max_vel, other.afterburner_max_vel);
1097  afterburner_forward_accel = other.afterburner_forward_accel;
1098  afterburner_fuel_capacity = other.afterburner_fuel_capacity;
1099  afterburner_burn_rate = other.afterburner_burn_rate;
1100  afterburner_recover_rate = other.afterburner_recover_rate;
1101  afterburner_max_reverse_vel = other.afterburner_max_reverse_vel;
1102  afterburner_reverse_accel = other.afterburner_reverse_accel;
1103 
1104  cmeasure_type = other.cmeasure_type;
1105  cmeasure_max = other.cmeasure_max;
1106 
1107  num_primary_banks = other.num_primary_banks;
1108  std::swap(primary_bank_weapons, other.primary_bank_weapons);
1109  std::swap(primary_bank_ammo_capacity, other.primary_bank_ammo_capacity);
1110 
1111  num_secondary_banks = other.num_secondary_banks;
1112  std::swap(secondary_bank_weapons, other.secondary_bank_weapons);
1113  std::swap(secondary_bank_ammo_capacity, other.secondary_bank_ammo_capacity);
1114 
1115  std::swap(draw_primary_models, other.draw_primary_models);
1116  std::swap(draw_secondary_models, other.draw_secondary_models);
1117  weapon_model_draw_distance = other.weapon_model_draw_distance;
1118 
1119  max_hull_strength = other.max_hull_strength;
1120  max_shield_strength = other.max_shield_strength;
1121  max_shield_recharge = other.max_shield_recharge;
1122  auto_shield_spread = other.auto_shield_spread;
1123  auto_shield_spread_bypass = other.auto_shield_spread_bypass;
1124  auto_shield_spread_from_lod = other.auto_shield_spread_from_lod;
1125  auto_shield_spread_min_span = other.auto_shield_spread_min_span;
1126 
1127  std::swap(shield_point_augment_ctrls, other.shield_point_augment_ctrls);
1128 
1129  hull_repair_rate = other.hull_repair_rate;
1130  subsys_repair_rate = other.subsys_repair_rate;
1131 
1132  sup_hull_repair_rate = other.sup_hull_repair_rate;
1133  sup_shield_repair_rate = other.sup_shield_repair_rate;
1134  sup_subsys_repair_rate = other.sup_subsys_repair_rate;
1135 
1136  std::swap(closeup_pos, other.closeup_pos);
1137  closeup_zoom = other.closeup_zoom;
1138 
1139  std::swap(allowed_weapons, other.allowed_weapons);
1140 
1141  std::swap(restricted_loadout_flag, other.restricted_loadout_flag);
1142  memcpy(allowed_bank_restricted_weapons, other.allowed_bank_restricted_weapons, sizeof(int) * MAX_SHIP_WEAPONS * MAX_WEAPON_TYPES);
1143 
1144  shield_icon_index = other.shield_icon_index;
1145  std::swap(icon_filename, other.icon_filename);
1146  model_icon_angles = other.model_icon_angles;
1147  std::swap(anim_filename, other.anim_filename);
1148  std::swap(overhead_filename, other.overhead_filename);
1149  selection_effect = other.selection_effect;
1150 
1151  bii_index_ship = other.bii_index_ship;
1152  bii_index_ship_with_cargo = other.bii_index_ship_with_cargo;
1153  bii_index_wing = other.bii_index_wing;
1154  bii_index_wing_with_cargo = other.bii_index_wing_with_cargo;
1155 
1156  score = other.score;
1157 
1158  scan_time = other.scan_time;
1159 
1160  std::swap(ct_info, other.ct_info);
1161  ct_count = other.ct_count;
1162 
1163  num_nondark_colors = other.num_nondark_colors;
1164  memcpy(nondark_colors, other.nondark_colors, sizeof(ubyte) * MAX_NONDARK_COLORS * 3);
1165 
1166  std::swap(shield_color, other.shield_color);
1167 
1168  uses_team_colors = other.uses_team_colors;
1169  std::swap(default_team_name, other.default_team_name);
1170 
1171  std::swap(afterburner_trail, other.afterburner_trail);
1172  afterburner_trail_width_factor = other.afterburner_trail_width_factor;
1173  afterburner_trail_alpha_factor = other.afterburner_trail_alpha_factor;
1174  afterburner_trail_life = other.afterburner_trail_life;
1175  afterburner_trail_faded_out_sections = other.afterburner_trail_faded_out_sections;
1176 
1177  std::swap(normal_thruster_particles, other.normal_thruster_particles);
1178  std::swap(afterburner_thruster_particles, other.afterburner_thruster_particles);
1179 
1180  std::swap(thruster_flame_info, other.thruster_flame_info);
1181  std::swap(thruster_glow_info, other.thruster_glow_info);
1182  std::swap(thruster_secondary_glow_info, other.thruster_secondary_glow_info);
1183  std::swap(thruster_tertiary_glow_info, other.thruster_tertiary_glow_info);
1184  std::swap(thruster_distortion_info, other.thruster_distortion_info);
1185 
1186  thruster01_glow_rad_factor = other.thruster01_glow_rad_factor;
1187  thruster02_glow_rad_factor = other.thruster02_glow_rad_factor;
1188  thruster03_glow_rad_factor = other.thruster03_glow_rad_factor;
1189  thruster02_glow_len_factor = other.thruster02_glow_len_factor;
1190  thruster_dist_rad_factor = other.thruster_dist_rad_factor;
1191  thruster_dist_len_factor = other.thruster_dist_len_factor;
1192  thruster_glow_noise_mult = other.thruster_glow_noise_mult;
1193 
1194  draw_distortion = other.draw_distortion;
1195 
1196  splodeing_texture = other.splodeing_texture;
1197  std::swap(splodeing_texture_name, other.splodeing_texture_name);
1198 
1199  std::swap(replacement_textures, other.replacement_textures);
1200 
1201  armor_type_idx = other.armor_type_idx;
1202  shield_armor_type_idx = other.shield_armor_type_idx;
1203 
1204  can_glide = other.can_glide;
1205  glide_cap = other.glide_cap;
1206  glide_dynamic_cap = other.glide_dynamic_cap;
1207  glide_accel_mult = other.glide_accel_mult;
1208  use_newtonian_damp = other.use_newtonian_damp;
1209  newtonian_damp_override = other.newtonian_damp_override;
1210 
1211  autoaim_fov = other.autoaim_fov;
1212 
1213  topdown_offset_def = other.topdown_offset_def;
1214  std::swap(topdown_offset, other.topdown_offset);
1215 
1216  engine_snd = other.engine_snd;
1217  min_engine_vol = other.min_engine_vol;
1218  glide_start_snd = other.glide_start_snd;
1219  glide_end_snd = other.glide_end_snd;
1220 
1221  std::swap(ship_sounds, other.ship_sounds);
1222 
1223  num_maneuvering = other.num_maneuvering;
1224  std::swap(maneuvering, other.maneuvering);
1225 
1226  radar_image_2d_idx = other.radar_image_2d_idx;
1227  radar_color_image_2d_idx = other.radar_color_image_2d_idx;
1228  radar_image_size = other.radar_image_size;
1229  radar_projection_size_mult = other.radar_projection_size_mult;
1230 
1231  memcpy(ship_iff_info, other.ship_iff_info, sizeof(int) * MAX_IFFS * MAX_IFFS);
1232 
1233  aiming_flags = other.aiming_flags;
1234  minimum_convergence_distance = other.minimum_convergence_distance;
1235  convergence_distance = other.convergence_distance;
1236  std::swap(convergence_offset, other.convergence_offset);
1237 
1238  emp_resistance_mod = other.emp_resistance_mod;
1239 
1240  piercing_damage_draw_limit = other.piercing_damage_draw_limit;
1241 
1242  damage_lightning_type = other.damage_lightning_type;
1243 
1244  shield_impact_explosion_anim = other.shield_impact_explosion_anim;
1245  std::swap(hud_gauges, other.hud_gauges);
1246  hud_enabled = other.hud_enabled;
1247  hud_retail = other.hud_retail;
1248 
1249  std::swap(displays, other.displays);
1250 
1251  std::swap(pathMetadata, other.pathMetadata);
1252 
1253  std::swap(glowpoint_bank_override_map, other.glowpoint_bank_override_map);
1254 }
1255 
1256 #define CHECK_THEN_FREE(attribute) \
1257 do {\
1258  if (attribute != NULL) {\
1259  vm_free(attribute);\
1260  attribute = NULL;\
1261  }\
1262 } while(false)
1263 
1265 {
1275 }
1276 
1278 {
1279  if (this != &other) {
1280  move(std::move(other));
1281  }
1282  return *this;
1283 }
1284 
1286 {
1287  // MageKing17 - Initialize these pointers to NULL because otherwise move() will leave them uninitialized.
1288  type_str = NULL;
1289  maneuverability_str = NULL;
1290  armor_str = NULL;
1291  manufacturer_str = NULL;
1292  desc = NULL;
1293  tech_desc = NULL;
1294  ship_length = NULL;
1295  gun_mounts = NULL;
1296  missile_banks = NULL;
1297  subsystems = NULL;
1298  n_subsystems = 0;
1299 
1300  // Then we swap everything (well, everything that matters to the deconstructor).
1301  move(std::move(other));
1302 }
1303 
1305 {
1306  int i,j;
1307 
1308  name[0] = '\0';
1309  alt_name[0] = '\0';
1310  sprintf(short_name, "ShipClass%d", static_cast<int>(Ship_info.size()));
1311  species = 0;
1312  class_type = -1;
1313 
1315  desc = tech_desc = NULL;
1316  tech_title[0] = 0;
1317 
1318  ship_length = NULL;
1319  gun_mounts = NULL;
1320  missile_banks = NULL;
1321 
1322  cockpit_pof_file[0] = '\0';
1324  pof_file[0] = '\0';
1325  pof_file_hud[0] = '\0';
1326  num_detail_levels = 1;
1327  detail_distance[0] = 0;
1328  collision_lod = -1;
1329  cockpit_model_num = -1;
1330  model_num = -1;
1331  model_num_hud = -1;
1332  hud_target_lod = -1;
1333 
1334  density = 1.0f;
1335  damp = 0.0f;
1336  rotdamp = 0.0f;
1338  vm_vec_zero(&max_vel);
1341  srotation_time = 0.0f;
1342  max_rear_vel = 0.0f;
1343  forward_accel = 0.0f;
1344  forward_decel = 0.0f;
1345  slide_accel = 0.0f;
1346  slide_decel = 0.0f;
1347 
1348  warpin_anim[0] = '\0';
1349  warpin_radius = 0.0f;
1350  warpin_snd_start = -1;
1351  warpin_snd_end = -1;
1352  warpin_speed = 0.0f;
1353  warpin_time = 0;
1354  warpin_decel_exp = 1;
1356 
1357  warpout_anim[0] = '\0';
1358  warpout_radius = 0.0f;
1359  warpout_snd_start = -1;
1360  warpout_snd_end = -1;
1361  warpout_engage_time = -1;
1362  warpout_speed = 0.0f;
1363  warpout_time = 0;
1364  warpout_accel_exp = 1;
1366 
1367  warpout_player_speed = 0.0f;
1368 
1371  ai_class = 0;
1372  max_speed = 0.0f;
1373  min_speed = 0.0f;
1374  max_accel = 0.0f;
1375 
1377  // Retail default collision physics and default landing parameters
1378  memset(&collision_physics, 0, sizeof(ship_collision_physics));
1380  collision_physics.bounce = 5.0;
1385 
1388  big_exp_visual_rad = -1.0f;
1389  prop_exp_rad_mult = 1.0f;
1390  death_roll_r_mult = 1.0f;
1391  death_fx_r_mult = 1.0f;
1392  death_roll_time_mult = 1.0f;
1393  death_roll_base_time = 3000;
1394  death_fx_count = 6;
1395  shockwave_count = 1;
1396  explosion_bitmap_anims.clear();
1397  vaporize_chance = 0;
1398 
1399  // default values from shipfx.cpp
1400  impact_spew.n_high = 30;
1401  impact_spew.n_low = 25;
1402  impact_spew.max_rad = 0.5f;
1403  impact_spew.min_rad = 0.2f;
1404  impact_spew.max_life = 0.55f;
1405  impact_spew.min_life = 0.05f;
1406  impact_spew.max_vel = 12.0f;
1407  impact_spew.min_vel = 2.0f;
1408  impact_spew.variance = 1.0f;
1409 
1410  // default values from shipfx.cpp
1411  damage_spew.n_high = 1; // 1 is used here to trigger retail behaviour
1412  damage_spew.n_low = 0;
1413  damage_spew.max_rad = 1.3f;
1414  damage_spew.min_rad = 0.7f;
1415  damage_spew.max_life = 0.0f;
1416  damage_spew.min_life = 0.0f;
1417  damage_spew.max_vel = 12.0f;
1418  damage_spew.min_vel = 3.0f;
1419  damage_spew.variance = 0.0f;
1420 
1421  split_particles.n_high = 80;
1422  split_particles.n_low = 40;
1423  split_particles.max_rad = 0.0f;
1424  split_particles.min_rad = 0.0f;
1425  split_particles.max_life = 0.0f;
1426  split_particles.min_life = 0.0f;
1427  split_particles.max_vel = 0.0f;
1428  split_particles.min_vel = 0.0f;
1429  split_particles.variance = 2.0f;
1430 
1433  knossos_end_particles.max_rad = 100.0f;
1437  knossos_end_particles.max_vel = 350.0f;
1440 
1450 
1451  debris_min_lifetime = -1.0f;
1452  debris_max_lifetime = -1.0f;
1453  debris_min_speed = -1.0f;
1454  debris_max_speed = -1.0f;
1455  debris_min_rotspeed = -1.0f;
1456  debris_max_rotspeed = -1.0f;
1458  debris_min_hitpoints = -1.0f;
1459  debris_max_hitpoints = -1.0f;
1460  debris_damage_mult = 1.0f;
1461  debris_arc_percent = 0.5f;
1462 
1463  n_subsystems = 0;
1464  subsystems = NULL;
1465 
1466  power_output = 0.0f;
1467  max_overclocked_speed = 0.0f;
1468  max_weapon_reserve = 0.0f;
1471 
1475  afterburner_burn_rate = 0.0f;
1476  afterburner_recover_rate = 0.0f;
1479 
1481  cmeasure_max = 0;
1482 
1483  num_primary_banks = 0;
1484  for ( i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++ )
1485  {
1486  primary_bank_weapons[i] = -1;
1487  draw_primary_models[i] = false;
1489  }
1490 
1491  num_secondary_banks = 0;
1492  for ( i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++ )
1493  {
1494  secondary_bank_weapons[i] = -1;
1495  draw_secondary_models[i] = false;
1497  }
1498 
1499  weapon_model_draw_distance = 200.0f;
1500 
1501  ship_recoil_modifier = 1.0f;
1502 
1503  max_hull_strength = 100.0f;
1504  max_shield_strength = 0.0f;
1505 
1506  max_shield_recharge = 1.0f;
1507 
1508  auto_shield_spread = 0.0f;
1509  auto_shield_spread_bypass = false;
1512 
1513  for (i = 0; i < 4; i++)
1514  {
1516  }
1517 
1518  hull_repair_rate = 0.0f;
1519  //-2 represents not set, in which case the default is used for the ship (if it is small)
1520  subsys_repair_rate = -2.0f;
1521 
1522  sup_hull_repair_rate = 0.15f;
1523  sup_shield_repair_rate = 0.20f;
1524  sup_subsys_repair_rate = 0.15f;
1525 
1527  closeup_zoom = 0.5f;
1528 
1529  memset(allowed_weapons, 0, sizeof(int) * MAX_WEAPON_TYPES);
1530 
1531  memset(restricted_loadout_flag, 0, sizeof(int) * MAX_SHIP_WEAPONS);
1532  memset(allowed_bank_restricted_weapons, 0, sizeof(int) * MAX_SHIP_WEAPONS * MAX_WEAPON_TYPES);
1533 
1534  shield_icon_index = 255; // stored as ubyte
1535  icon_filename[0] = '\0';
1536  memset(&model_icon_angles, 0, sizeof(angles));
1537  anim_filename[0] = '\0';
1538  overhead_filename[0] = '\0';
1539 
1541 
1542  bii_index_ship = -1;
1544  bii_index_wing = -1;
1546 
1547  score = 0;
1548 
1549  scan_time = 2000;
1550 
1551  memset(&ct_info, 0, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
1552  ct_count = 0;
1553 
1554  num_nondark_colors = 0;
1555  memset(nondark_colors, 0, sizeof(ubyte) * MAX_NONDARK_COLORS * 3);
1556 
1557  shield_color[0] = 255;
1558  shield_color[1] = 255;
1559  shield_color[2] = 255;
1560 
1561  // Team colors
1562  uses_team_colors = false;
1563  default_team_name = "";
1564 
1568  afterburner_trail_life = 5.0f;
1570 
1571  normal_thruster_particles.clear();
1573 
1574  // Bobboau's thruster stuff
1585 
1586  // Bobboau's thruster stuff
1591  thruster_dist_rad_factor = 2.0f;
1592  thruster_dist_len_factor = 2.0f;
1593  thruster_glow_noise_mult = 1.0f;
1594 
1595  draw_distortion = true;
1596 
1597  splodeing_texture = -1;
1599 
1600  replacement_textures.clear();
1601 
1602  armor_type_idx = -1;
1603  shield_armor_type_idx = -1;
1604 
1605  can_glide = false;
1606  glide_cap = 0.0f;
1607  glide_dynamic_cap = false;
1608  glide_accel_mult = 0.0f;
1609  use_newtonian_damp = false;
1610  newtonian_damp_override = false;
1611 
1612  autoaim_fov = 0.0f;
1613 
1614  topdown_offset_def = false;
1616 
1617  engine_snd = -1;
1618  min_engine_vol = -1.0f;
1619  glide_start_snd = -1;
1620  glide_end_snd = -1;
1621 
1622  ship_sounds.clear();
1623 
1624  num_maneuvering = 0;
1625  memset(maneuvering, 0, MAX_MAN_THRUSTERS * sizeof(man_thruster));
1626  for (i = 0; i < MAX_MAN_THRUSTERS; i++)
1627  {
1628  maneuvering[i].start_snd = -1;
1629  maneuvering[i].loop_snd = -1;
1630  maneuvering[i].stop_snd = -1;
1631  maneuvering[i].tex_id = -1;
1632  }
1633 
1634  radar_image_2d_idx = -1;
1636  radar_image_size = -1;
1638 
1639  for (i=0;i<MAX_IFFS;i++)
1640  {
1641  for (j=0;j<MAX_IFFS;j++)
1642  ship_iff_info[i][j] = -1;
1643  }
1644 
1645  aiming_flags = 0;
1647  convergence_distance = 100.0f;
1649 
1650  emp_resistance_mod = 0.0f;
1651 
1653 
1655 
1657  hud_gauges.clear();
1658  hud_enabled = false;
1659  hud_retail = false;
1660 
1661  displays.clear();
1662 
1663  pathMetadata.clear();
1664 
1666 }
1667 
1669 {
1670  if ( subsystems != NULL ) {
1671  for(int n = 0; n < n_subsystems; n++) {
1672  if (subsystems[n].triggers != NULL) {
1673  vm_free(subsystems[n].triggers);
1674  subsystems[n].triggers = NULL;
1675  }
1676  }
1677 
1679  subsystems = NULL;
1680  }
1681 
1682  free_strings();
1683 }
1684 
1688 int parse_ship(const char *filename, bool replace)
1689 {
1690  char buf[SHIP_MULTITEXT_LENGTH];
1691  ship_info *sip;
1692  bool create_if_not_found = true;
1693  int rtn = 0;
1694 
1695  required_string("$Name:");
1697 
1698  if(optional_string("+nocreate")) {
1699  if(!replace) {
1700  Warning(LOCATION, "+nocreate flag used for ship in non-modular table");
1701  }
1702  create_if_not_found = false;
1703  }
1704 
1705 #ifdef NDEBUG
1707  rtn = 1;
1708 #endif
1709 
1710  //Remove @ symbol
1711  //these used to be used to denote weapons that would
1712  //only be parsed in demo builds
1713  if ( buf[0] == '@' ) {
1714  backspace(buf);
1715  }
1716 
1717  diag_printf ("Ship name -- %s\n", buf);
1718  //Check if ship exists already
1719  int ship_id;
1720  bool first_time = false;
1721  ship_id = ship_info_lookup_sub( buf );
1722 
1723  if(ship_id != -1)
1724  {
1725  sip = &Ship_info[ship_id];
1726  if(!replace)
1727  {
1728  Warning(LOCATION, "Error: Ship name %s already exists in %s. All ship class names must be unique.", sip->name, filename);
1729  if ( !skip_to_start_of_string_either("$Name:", "#End")) {
1730  Int3();
1731  }
1732  return -1;
1733  }
1734  }
1735  else
1736  {
1737  //Don't create ship if it has +nocreate and is in a modular table.
1738  if(!create_if_not_found && replace)
1739  {
1740  if ( !skip_to_start_of_string_either("$Name:", "#End")) {
1741  Int3();
1742  }
1743 
1744  return -1;
1745  }
1746 
1747  //Check if there are too many ship classes
1748  if(Ship_info.size() >= MAX_SHIP_CLASSES) {
1749  Error(LOCATION, "Too many ship classes before '%s'; maximum is %d.\n", buf, MAX_SHIP_CLASSES);
1750  }
1751 
1752  //Init vars
1753  Ship_info.push_back(ship_info());
1754  sip = &Ship_info.back();
1755  first_time = true;
1756 
1757  strcpy_s(sip->name, buf);
1758  }
1759 
1760  // Use a template for this ship.
1761  if( optional_string( "+Use Template:" ) ) {
1762  if ( !create_if_not_found ) {
1763  Warning(LOCATION, "Both '+nocreate' and '+Use Template:' were specified for ship class '%s', ignoring '+Use Template:'\n", buf);
1764  }
1765  else {
1766  char template_name[SHIP_MULTITEXT_LENGTH];
1767  stuff_string(template_name, F_NAME, SHIP_MULTITEXT_LENGTH);
1768  int template_id = ship_template_lookup(template_name);
1769  if ( template_id != -1 ) {
1770  first_time = false;
1771  sip->clone(Ship_templates[template_id]);
1772  strcpy_s(sip->name, buf);
1773  }
1774  else {
1775  Warning(LOCATION, "Unable to find ship template '%s' requested by ship class '%s', ignoring template request...", template_name, buf);
1776  }
1777  }
1778  }
1779 
1780  rtn = parse_ship_values(sip, false, first_time, replace);
1781 
1782  return rtn; //0 for success
1783 }
1784 
1789 {
1790  char buf[SHIP_MULTITEXT_LENGTH];
1791  ship_info *sip;
1792  int rtn = 0;
1793  bool first_time = true;
1794 
1795  required_string("$Template:");
1797 
1798  if( optional_string("+nocreate") ) {
1799  Warning(LOCATION, "+nocreate flag used on ship template. Ship templates can not be modified. Ignoring +nocreate.");
1800  }
1801 
1802  diag_printf ("Ship template name -- %s\n", buf);
1803  //Check if the template exists already
1804  int template_id;
1805  template_id = ship_template_lookup( buf );
1806 
1807  if( template_id != -1 ) {
1808  sip = &Ship_templates[template_id];
1809  Warning(LOCATION, "WARNING: Ship template %s already exists. All ship template names must be unique.", sip->name);
1810  if ( !skip_to_start_of_string_either("$Template:", "#End")) {
1811  error_display(1, "Missing [#End] or [$Template] after duplicate entry for %s", sip->name);
1812  }
1813  return -1;
1814  }
1815  else {
1816 
1817  Ship_templates.push_back(ship_info());
1818  sip = &Ship_templates.back();
1819 
1820  strcpy_s(sip->name, buf);
1821  //Use another template for this template. This allows for template hierarchies. - Turey
1822  if( optional_string("+Use Template:") ) {
1823  char template_name[SHIP_MULTITEXT_LENGTH];
1824  stuff_string(template_name, F_NAME, SHIP_MULTITEXT_LENGTH);
1825  template_id = ship_template_lookup( template_name);
1826 
1827  if ( template_id != -1 ) {
1828  first_time = false;
1829  sip->clone(Ship_templates[template_id]);
1830  strcpy_s(sip->name, buf);
1831  }
1832  else {
1833  Warning(LOCATION, "Unable to find ship template '%s' requested by ship template '%s', ignoring template request...", template_name, buf);
1834  }
1835  }
1836  }
1837 
1838  rtn = parse_ship_values( sip, true, first_time, false );
1839 
1840  return rtn;
1841 }
1842 
1844 {
1845  Assert( name != NULL );
1846 
1847  int temp_index = -1;
1848 
1849  parse_sound(name, &temp_index, sip->name);
1850 
1851  if (temp_index >= 0)
1852  sip->ship_sounds.insert(std::pair<GameSoundsIndex, int>(id, temp_index));
1853 }
1854 
1856 {
1857  parse_ship_sound("$CockpitEngineSnd:", SND_ENGINE, sip);
1858  parse_ship_sound("$FullThrottleSnd:", SND_FULL_THROTTLE, sip);
1859  parse_ship_sound("$ZeroThrottleSnd:", SND_ZERO_THROTTLE, sip);
1860  parse_ship_sound("$ThrottleUpSnd:", SND_THROTTLE_UP, sip);
1861  parse_ship_sound("$ThrottleDownSnd:", SND_THROTTLE_DOWN, sip);
1862  parse_ship_sound("$AfterburnerSnd:", SND_ABURN_LOOP, sip);
1863  parse_ship_sound("$AfterburnerEngageSnd:", SND_ABURN_ENGAGE, sip);
1864  parse_ship_sound("$AfterburnerFailedSnd:", SND_ABURN_FAIL, sip);
1865  parse_ship_sound("$MissileTrackingSnd:", SND_MISSILE_TRACKING, sip);
1866  parse_ship_sound("$MissileLockedSnd:", SND_MISSILE_LOCK, sip);
1867  parse_ship_sound("$PrimaryCycleSnd:", SND_PRIMARY_CYCLE, sip);
1868  parse_ship_sound("$SecondaryCycleSnd:", SND_SECONDARY_CYCLE, sip);
1869  parse_ship_sound("$TargetAcquiredSnd:", SND_TARGET_ACQUIRE, sip);
1870  parse_ship_sound("$PrimaryFireFailedSnd:", SND_OUT_OF_WEAPON_ENERGY, sip);
1871  parse_ship_sound("$SecondaryFireFailedSnd:", SND_OUT_OF_MISSLES, sip);
1872  parse_ship_sound("$HeatSeekerLaunchWarningSnd:", SND_HEATLOCK_WARN, sip);
1873  parse_ship_sound("$AspectSeekerLaunchWarningSnd:", SND_ASPECTLOCK_WARN, sip);
1874  parse_ship_sound("$MissileLockWarningSnd:", SND_THREAT_FLASH, sip);
1875  parse_ship_sound("$HeatSeekerProximityWarningSnd:", SND_PROXIMITY_WARNING, sip);
1876  parse_ship_sound("$AspectSeekerProximityWarningSnd:", SND_PROXIMITY_ASPECT_WARNING, sip);
1877  parse_ship_sound("$MissileEvadedSnd:", SND_MISSILE_EVADED_POPUP, sip);
1878  parse_ship_sound("$CargoScanningSnd:", SND_CARGO_SCAN, sip);
1879 
1880  // Use SND_SHIP_EXPLODE_1 for custom explosion sounds
1881  parse_ship_sound("$ExplosionSnd:", SND_SHIP_EXPLODE_1, sip);
1882 }
1883 
1884 void parse_ship_particle_effect(ship_info* sip, particle_effect* pe, char *id_string)
1885 {
1886  float tempf;
1887  int temp;
1888  if(optional_string("+Max particles:"))
1889  {
1890  stuff_int(&temp);
1891  if (temp < 0) {
1892  Warning(LOCATION,"Bad value %i, defined as %s particle number (max) in ship '%s'.\nValue should be a non-negative integer.\n", temp, id_string, sip->name);
1893  } else {
1894  pe->n_high = temp;
1895  if (pe->n_high == 0) {
1896  // notification for disabling the particles
1897  mprintf(("Particle effect for %s disabled on ship '%s'.\n", id_string, sip->name));
1898  }
1899  }
1900  }
1901  if(optional_string("+Min particles:"))
1902  {
1903  stuff_int(&temp);
1904  if (temp < 0) {
1905  Warning(LOCATION,"Bad value %i, defined as %s particle number (min) in ship '%s'.\nValue should be a non-negative integer.\n", temp, id_string, sip->name);
1906  } else {
1907  pe->n_low = temp;
1908  }
1909  }
1910  if (pe->n_low > pe->n_high)
1911  pe->n_low = pe->n_high;
1912 
1913  if(optional_string("+Max Radius:"))
1914  {
1915  stuff_float(&tempf);
1916  if (tempf <= 0.0f) {
1917  Warning(LOCATION,"Bad value %f, defined as %s particle radius (max) in ship '%s'.\nValue should be a positive float.\n", tempf, id_string, sip->name);
1918  } else {
1919  pe->max_rad = tempf;
1920  }
1921  }
1922  if(optional_string("+Min Radius:"))
1923  {
1924  stuff_float(&tempf);
1925  if (tempf < 0.0f) {
1926  Warning(LOCATION,"Bad value %f, defined as %s particle radius (min) in ship '%s'.\nValue should be a non-negative float.\n", tempf, id_string, sip->name);
1927  } else {
1928  pe->min_rad = tempf;
1929  }
1930  }
1931  if (pe->min_rad > pe->max_rad)
1932  pe->min_rad = pe->max_rad;
1933 
1934  if(optional_string("+Max Lifetime:"))
1935  {
1936  stuff_float(&tempf);
1937  if (tempf <= 0.0f) {
1938  Warning(LOCATION,"Bad value %f, defined as %s particle lifetime (max) in ship '%s'.\nValue should be a positive float.\n", tempf, id_string, sip->name);
1939  } else {
1940  pe->max_life = tempf;
1941  }
1942  }
1943  if(optional_string("+Min Lifetime:"))
1944  {
1945  stuff_float(&tempf);
1946  if (tempf < 0.0f) {
1947  Warning(LOCATION,"Bad value %f, defined as %s particle lifetime (min) in ship '%s'.\nValue should be a non-negative float.\n", tempf, id_string, sip->name);
1948  } else {
1949  pe->min_life = tempf;
1950  }
1951  }
1952  if (pe->min_life > pe->max_life)
1953  pe->min_life = pe->max_life;
1954 
1955  if(optional_string("+Max Velocity:"))
1956  {
1957  stuff_float(&tempf);
1958  if (tempf < 0.0f) {
1959  Warning(LOCATION,"Bad value %f, defined as %s particle velocity (max) in ship '%s'.\nValue should be a non-negative float.\n", tempf, id_string, sip->name);
1960  } else {
1961  pe->max_vel = tempf;
1962  }
1963  }
1964  if(optional_string("+Min Velocity:"))
1965  {
1966  stuff_float(&tempf);
1967  if (tempf < 0.0f) {
1968  Warning(LOCATION,"Bad value %f, defined as %s particle velocity (min) in ship '%s'.\nValue should be a non-negative float.\n", tempf, id_string, sip->name);
1969  } else {
1970  pe->min_vel = tempf;
1971  }
1972  }
1973  if (pe->min_vel > pe->max_vel)
1974  pe->min_vel = pe->max_vel;
1975 
1976  if(optional_string("+Normal Variance:"))
1977  {
1978  stuff_float(&tempf);
1979  if ((tempf >= 0.0f) && (tempf <= 2.0f)) {
1980  pe->variance = tempf;
1981  } else {
1982  Warning(LOCATION,"Bad value %f, defined as %s particle normal variance in ship '%s'.\nValue should be a float from 0.0 to 2.0.\n", tempf, id_string, sip->name);
1983  }
1984  }
1985 }
1986 
1987 void parse_allowed_weapons(ship_info *sip, const bool is_primary, const bool is_dogfight, const bool first_time)
1988 {
1989  int i, num_allowed;
1990  int allowed_weapons[MAX_WEAPON_TYPES];
1991  const int max_banks = (is_primary ? MAX_SHIP_PRIMARY_BANKS : MAX_SHIP_SECONDARY_BANKS);
1992  const int weapon_type = (is_dogfight ? DOGFIGHT_WEAPON : REGULAR_WEAPON);
1993  const int offset = (is_primary ? 0 : MAX_SHIP_PRIMARY_BANKS);
1994  const char *allowed_banks_str = is_primary ? (is_dogfight ? "$Allowed Dogfight PBanks:" : "$Allowed PBanks:")
1995  : (is_dogfight ? "$Allowed Dogfight SBanks:" : "$Allowed SBanks:");
1996  const char *bank_type_str = is_primary ? "primary" : "secondary";
1997 
1998  // Goober5000 - fixed Bobboau's implementation of restricted banks
1999  int bank;
2000 
2001  // Set the weapons filter used in weapons loadout (for primary weapons)
2002  if (optional_string(allowed_banks_str))
2003  {
2004  // MageKing17 - We need to make modular tables replace bank restrictions by default, instead of adding to them.
2005  if (!first_time && !(optional_string("+noreplace"))) { // Only makes sense for modular tables.
2006  // clear allowed weapons so the modular table can define new ones
2007  for (bank = 0; bank < max_banks; bank++) {
2008  for (i = 0; i < Num_weapon_types; i++) {
2009  sip->allowed_bank_restricted_weapons[offset+bank][i] &= ~weapon_type;
2010  }
2011  sip->restricted_loadout_flag[offset+bank] &= ~weapon_type;
2012  }
2013  }
2014 
2015  bank = -1;
2016 
2017  while (check_for_string("("))
2018  {
2019  bank++;
2020 
2021  // make sure we don't specify more than we have banks for
2022  if (bank >= max_banks)
2023  {
2024  Warning(LOCATION, "%s bank-specific loadout for %s exceeds permissible number of %s banks. Ignoring the rest...", allowed_banks_str, sip->name, bank_type_str);
2025  bank--;
2026  break;
2027  }
2028 
2029  num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
2030 
2031  // actually say which weapons are allowed
2032  for ( i = 0; i < num_allowed; i++ )
2033  {
2034  if ( allowed_weapons[i] >= 0 ) // MK, Bug fix, 9/6/99. Used to be "allowed_weapons" not "allowed_weapons[i]".
2035  {
2036  sip->allowed_bank_restricted_weapons[offset+bank][allowed_weapons[i]] |= weapon_type;
2037  }
2038  }
2039  }
2040 
2041  // set flags if need be
2042  if (bank > 0) // meaning there was a restricted bank table entry
2043  {
2044  for (i=0; i<=bank; i++)
2045  {
2046  sip->restricted_loadout_flag[offset+i] |= weapon_type;
2047  }
2048  }
2049  }
2050 }
2051 
2056 void parse_weapon_bank(ship_info *sip, bool is_primary, int *num_banks, int *bank_default_weapons, int *bank_capacities)
2057 {
2058  Assert(sip != NULL);
2059  Assert(bank_default_weapons != NULL);
2060  Assert(bank_capacities != NULL);
2061  const int max_banks = is_primary ? MAX_SHIP_PRIMARY_BANKS : MAX_SHIP_SECONDARY_BANKS;
2062  const char *default_banks_str = is_primary ? "$Default PBanks:" : "$Default SBanks:";
2063  const char *bank_capacities_str = is_primary ? "$PBank Capacity:" : "$SBank Capacity:";
2064 
2065  // we initialize to the previous parse, which presumably worked
2066  int num_bank_capacities = num_banks != NULL ? *num_banks : 0;
2067 
2068  if (optional_string(default_banks_str))
2069  {
2070  // get weapon list
2071  if (num_banks != NULL)
2072  *num_banks = stuff_int_list(bank_default_weapons, max_banks, WEAPON_LIST_TYPE);
2073  else
2074  stuff_int_list(bank_default_weapons, max_banks, WEAPON_LIST_TYPE);
2075  }
2076 
2077  if (optional_string(bank_capacities_str))
2078  {
2079  // get capacity list
2080  num_bank_capacities = stuff_int_list(bank_capacities, max_banks, RAW_INTEGER_TYPE);
2081  }
2082 
2083  // num_banks can be null if we're parsing weapons for a turret
2084  if ((num_banks != NULL) && (*num_banks != num_bank_capacities))
2085  {
2086  // okay for a ship to have 0 primary capacities, since it won't be ammo-enabled
2087  if (is_primary && num_bank_capacities != 0)
2088  {
2089  Warning(LOCATION, "Ship class '%s' has %d primary banks, but %d primary capacities... fix this!!", sip->name, *num_banks, num_bank_capacities);
2090  }
2091 
2092  // secondaries have no excuse!
2093  if (!is_primary)
2094  {
2095  Warning(LOCATION, "Ship class '%s' has %d secondary banks, but %d secondary capacities... fix this!!", sip->name, *num_banks, num_bank_capacities);
2096  }
2097  }
2098 }
2099 
2104 {
2105  int bii_index = -1;
2106  size_t icon;
2107  char regular_temp[MAX_FILENAME_LEN];
2108  char fade_temp[MAX_FILENAME_LEN];
2109  char highlight_temp[MAX_FILENAME_LEN];
2110 
2111  required_string("+Regular:");
2112  stuff_string(regular_temp, F_NAME, MAX_FILENAME_LEN);
2113  required_string("+Fade:");
2114  stuff_string(fade_temp, F_NAME, MAX_FILENAME_LEN);
2115  required_string("+Highlight:");
2116  stuff_string(highlight_temp, F_NAME, MAX_FILENAME_LEN);
2117 
2118  // search among our existing icons
2119  for (icon = 0; icon < Briefing_icon_info.size(); icon++)
2120  {
2121  if ( !stricmp(regular_temp, Briefing_icon_info[icon].regular.filename)
2122  && !stricmp(fade_temp, Briefing_icon_info[icon].fade.filename)
2123  && !stricmp(highlight_temp, Briefing_icon_info[icon].highlight.filename) )
2124  {
2125  bii_index = (int) icon;
2126  break;
2127  }
2128  }
2129 
2130  // icon not found: create new one
2131  if (bii_index < 0)
2132  {
2133  briefing_icon_info bii;
2134  generic_anim_init(&bii.regular, regular_temp);
2135  hud_anim_init(&bii.fade, 0, 0, fade_temp);
2136  hud_anim_init(&bii.highlight, 0, 0, highlight_temp);
2137 
2138  bii_index = (int) Briefing_icon_info.size();
2139  Briefing_icon_info.push_back(bii);
2140  }
2141 
2142  return bii_index;
2143 }
2144 
2148 int parse_ship_values(ship_info* sip, const bool is_template, const bool first_time, const bool replace)
2149 {
2150  char buf[SHIP_MULTITEXT_LENGTH];
2151  char* info_type_name;
2152  char* type_name;
2153  int i, j;
2154  int rtn = 0;
2155  char name_tmp[NAME_LENGTH];
2156 
2157  if ( ! is_template ) {
2158  info_type_name = "Ship Class";
2159  type_name = "$Name";
2160  }
2161  else {
2162  info_type_name = "Ship Template";
2163  type_name = "$Template";
2164  }
2165 
2166  if(optional_string("$Alt name:"))
2168 
2169  if(optional_string("$Short name:"))
2171  else if (first_time)
2172  {
2173  char *srcpos, *srcend, *destpos;
2174  srcpos = sip->name;
2175  destpos = sip->short_name;
2176  srcend = srcpos + strlen(sip->name);
2177  while(srcpos <= srcend)
2178  {
2179  if(*srcpos != ' ')
2180  *destpos++ = *srcpos++;
2181  else
2182  srcpos++;
2183  }
2184  }
2185  diag_printf ("Ship short name -- %s\n", sip->short_name);
2186 
2187  if (optional_string("$Species:")) {
2188  char temp[NAME_LENGTH];
2190  int i_species = 0;
2191 
2192  bool found = false;
2193  for (SCP_vector<species_info>::iterator sii = Species_info.begin(); sii != Species_info.end(); ++sii, ++i_species) {
2194  if (!stricmp(temp, sii->species_name)) {
2195  sip->species = i_species;
2196  found = true;
2197  break;
2198  }
2199  }
2200 
2201  if (!found) {
2202  Error(LOCATION, "Invalid Species %s defined in table entry for ship %s.\n", temp, sip->name);
2203  }
2204  }
2205 
2206  diag_printf ("Ship species -- %s\n", Species_info[sip->species].species_name);
2207 
2208  if (optional_string("+Type:")) {
2210  }
2211 
2212  if (optional_string("+Maneuverability:")) {
2214  }
2215 
2216  if (optional_string("+Armor:")) {
2218  }
2219 
2220  if (optional_string("+Manufacturer:")) {
2222  }
2223 
2224  if (optional_string("+Description:")) {
2225  stuff_malloc_string(&sip->desc, F_MULTITEXT, NULL);
2226  }
2227 
2228  if (optional_string("+Tech Title:")) {
2230  }
2231 
2232  if (optional_string("+Tech Description:")) {
2234  }
2235 
2236  if (optional_string("+Length:")) {
2238  }
2239 
2240  if (optional_string("+Gun Mounts:")) {
2242  }
2243 
2244  if (optional_string("+Missile Banks:")) {
2246  }
2247 
2248  // Ship fadein effect, used when no ani is specified or ship_select_3d is active
2249  sip->selection_effect = Default_ship_select_effect; //By default, use the FS2 effect
2250  if(optional_string("$Selection Effect:")) {
2251  char effect[NAME_LENGTH];
2252  stuff_string(effect, F_NAME, NAME_LENGTH);
2253  if (!stricmp(effect, "FS2"))
2254  sip->selection_effect = 2;
2255  else if (!stricmp(effect, "FS1"))
2256  sip->selection_effect = 1;
2257  else if (!stricmp(effect, "off"))
2258  sip->selection_effect = 0;
2259  }
2260 
2261  if(optional_string( "$Cockpit POF file:" ))
2262  {
2263  char temp[MAX_FILENAME_LEN];
2265 
2266  // assume we're using this file name
2267  bool valid = true;
2268 
2269  // Goober5000 - if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
2270  if (replace)
2271  if (sip->cockpit_pof_file[0] != '\0')
2272  if (!cf_exists_full(temp, CF_TYPE_MODELS))
2273  valid = false;
2274 
2275  if (valid)
2276  strcpy_s(sip->cockpit_pof_file, temp);
2277  else
2278  WarningEx(LOCATION, "Ship %s\nCockpit POF file \"%s\" invalid!", sip->name, temp);
2279  }
2280  if(optional_string( "+Cockpit offset:" ))
2281  {
2282  stuff_vec3d(&sip->cockpit_offset);
2283  }
2284  while(optional_string( "$Cockpit Display:" ))
2285  {
2286  cockpit_display_info display;
2287 
2288  display.bg_filename[0] = 0;
2289  display.fg_filename[0] = 0;
2290  display.filename[0] = 0;
2291  display.name[0] = 0;
2292  display.offset[0] = 0;
2293  display.offset[1] = 0;
2294 
2295  required_string("+Texture:");
2297 
2298  if ( optional_string("+Offsets:") ) {
2299  stuff_int_list(display.offset, 2);
2300  }
2301 
2302  required_string("+Size:");
2303  stuff_int_list(display.size, 2);
2304 
2305  if ( optional_string("+Background:") ) {
2307  }
2308  if ( optional_string("+Foreground:") ) {
2310  }
2311 
2312  required_string("+Display Name:");
2314 
2315  if ( display.offset[0] < 0 || display.offset[1] < 0 ) {
2316  Warning(LOCATION, "Negative display offsets given for cockpit display on %s, skipping entry", sip->name);
2317  continue;
2318  }
2319 
2320  if( display.size[0] <= 0 || display.size[1] <= 0 ) {
2321  Warning(LOCATION, "Negative or zero display size given for cockpit display on %s, skipping entry", sip->name);
2322  continue;
2323  }
2324 
2325  sip->displays.push_back(display);
2326  }
2327 
2328  if(optional_string( "$POF file:" ))
2329  {
2330  char temp[MAX_FILENAME_LEN];
2332 
2333  // assume we're using this file name
2334  bool valid = true;
2335 
2336  // Goober5000 - if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
2337  if (replace)
2338  if (sip->pof_file[0] != '\0')
2339  if (!cf_exists_full(temp, CF_TYPE_MODELS))
2340  valid = false;
2341 
2342  if (valid)
2343  strcpy_s(sip->pof_file, temp);
2344  else
2345  WarningEx(LOCATION, "Ship %s\nPOF file \"%s\" invalid!", sip->name, temp);
2346  }
2347 
2348  // ship class texture replacement - Goober5000
2349  // don't clear the vector because we could be parsing a TBM
2350  if (optional_string("$Texture Replace:"))
2351  {
2352  texture_replace tr;
2353  char *p;
2354 
2355  while (optional_string("+old:"))
2356  {
2357  strcpy_s(tr.ship_name, sip->name);
2358  tr.new_texture_id = -1;
2359 
2361  required_string("+new:");
2363 
2364  // get rid of extensions
2365  p = strchr(tr.old_texture, '.');
2366  if (p)
2367  {
2368  mprintf(("Extraneous extension found on replacement texture %s!\n", tr.old_texture));
2369  *p = 0;
2370  }
2371  p = strchr(tr.new_texture, '.');
2372  if (p)
2373  {
2374  mprintf(("Extraneous extension found on replacement texture %s!\n", tr.new_texture));
2375  *p = 0;
2376  }
2377 
2378  // add it if we aren't over the limit
2379  if (sip->replacement_textures.size() < MAX_MODEL_TEXTURES)
2380  sip->replacement_textures.push_back(tr);
2381  else
2382  mprintf(("Too many replacement textures specified for ship '%s'!\n", sip->name));
2383  }
2384  }
2385 
2386  // optional hud targeting model
2387  if(optional_string( "$POF target file:"))
2388  {
2389  char temp[MAX_FILENAME_LEN];
2391 
2392  // assume we're using this file name
2393  bool valid = true;
2394 
2395  // Goober5000 - if this is a modular table, and we're replacing an existing file name, and the file doesn't exist, don't replace it
2396  if (replace)
2397  if (sip->pof_file_hud[0] != '\0')
2398  if (!cf_exists_full(temp, CF_TYPE_MODELS))
2399  valid = false;
2400 
2401  if (valid)
2402  strcpy_s(sip->pof_file_hud, temp);
2403  else
2404  WarningEx(LOCATION, "Ship \"%s\" POF target file \"%s\" invalid!", sip->name, temp);
2405  }
2406 
2407  // optional hud target LOD if not using special hud model
2408  if (optional_string( "$POF target LOD:" )) {
2409  stuff_int(&sip->hud_target_lod);
2410  }
2411 
2412  if(optional_string("$Detail distance:")) {
2414  }
2415 
2416  if(optional_string("$Collision LOD:")) {
2417  stuff_int(&sip->collision_lod);
2418 
2419  // Cap to sane values, just in case
2420  sip->collision_lod = MAX(0, MIN(sip->collision_lod, sip->num_detail_levels));
2421  }
2422 
2423  // check for optional pixel colors
2424  while(optional_string("$ND:")){
2425  ubyte nr, ng, nb;
2426  stuff_ubyte(&nr);
2427  stuff_ubyte(&ng);
2428  stuff_ubyte(&nb);
2429 
2431  sip->nondark_colors[sip->num_nondark_colors][0] = nr;
2432  sip->nondark_colors[sip->num_nondark_colors][1] = ng;
2433  sip->nondark_colors[sip->num_nondark_colors++][2] = nb;
2434  }
2435  }
2436 
2437  if (optional_string("$Enable Team Colors:")) {
2439  sip->default_team_name = "None";
2440  }
2441 
2442  if (optional_string("$Default Team:")) {
2443  char temp[NAME_LENGTH];
2445  SCP_string name = temp;
2446  if (!stricmp(temp, "none")) {
2447  sip->uses_team_colors = true;
2448  } else {
2449  if (Team_Colors.find(name) != Team_Colors.end()) {
2450  sip->default_team_name = name;
2451  sip->uses_team_colors = true;
2452  } else {
2453  Warning(LOCATION, "Team name %s is invalid. Teams must be defined in colors.tbl.\n", temp);
2454  }
2455  }
2456  }
2457 
2458  if(optional_string("$Show damage:"))
2459  {
2460  int bogus_bool;
2461  stuff_boolean(&bogus_bool);
2462  }
2463 
2464  if(optional_string("$Damage Lightning Type:"))
2465  {
2467  j = lightningtype_match(buf);
2468  if(j >= 0) {
2469  sip->damage_lightning_type = j;
2470  } else {
2471  Warning(LOCATION, "Invalid lightning type '%s' specified for ship '%s'", buf, sip->name);
2473  }
2474  }
2475 
2476  if(optional_string("$Impact:"))
2477  {
2478  if(optional_string("+Damage Type:"))
2479  {
2482  }
2483  }
2484 
2485  //HACK -
2486  //This should really be reworked so that all particle fields
2487  //are settable, but erg, just not happening right now -C
2488  if(optional_string("$Impact Spew:"))
2489  {
2490  parse_ship_particle_effect(sip, &sip->impact_spew, "impact spew");
2491  }
2492  if(optional_string("$Damage Spew:"))
2493  {
2494  parse_ship_particle_effect(sip, &sip->damage_spew, "damage spew");
2495  }
2496 
2497  if(optional_string("$Collision Physics:"))
2498  {
2499  if(optional_string("+Bounce:")) {
2501  }
2502  if(optional_string("+Both Small Bounce:")) {
2504  }
2505  if(optional_string("+Friction:")) {
2507  }
2508  if(optional_string("+Rotation Factor:")) {
2510  }
2511  if(optional_string("+Landing Max Forward Vel:")) {
2513  }
2514  if(optional_string("+Landing Min Forward Vel:")) {
2516  }
2517  if(optional_string("+Landing Max Descent Vel:")) {
2519  sip->collision_physics.landing_min_y *= -1;
2520  }
2521  if(optional_string("+Landing Max Horizontal Vel:")) {
2523  }
2524  if(optional_string("+Landing Max Angle:")) {
2525  float degrees;
2526  stuff_float(&degrees);
2527  sip->collision_physics.landing_max_angle = cosf(fl_radians(90.0f - degrees));
2528  }
2529  if(optional_string("+Landing Min Angle:")) {
2530  float degrees;
2531  stuff_float(&degrees);
2532  sip->collision_physics.landing_min_angle = cosf(fl_radians(90.0f - degrees));
2533  }
2534  if(optional_string("+Landing Max Rotate Angle:")) {
2535  float degrees;
2536  stuff_float(&degrees);
2537  sip->collision_physics.landing_max_rot_angle = cosf(fl_radians(90.0f - degrees));
2538  }
2539  if(optional_string("+Reorient Max Forward Vel:")) {
2541  }
2542  if(optional_string("+Reorient Min Forward Vel:")) {
2544  }
2545  if(optional_string("+Reorient Max Descent Vel:")) {
2547  sip->collision_physics.reorient_min_y *= -1;
2548  }
2549  if(optional_string("+Reorient Max Horizontal Vel:")) {
2551  }
2552  if(optional_string("+Reorient Max Angle:")) {
2553  float degrees;
2554  stuff_float(&degrees);
2555  sip->collision_physics.reorient_max_angle = cosf(fl_radians(90.0f - degrees));
2556  }
2557  if(optional_string("+Reorient Min Angle:")) {
2558  float degrees;
2559  stuff_float(&degrees);
2560  sip->collision_physics.reorient_min_angle = cosf(fl_radians(90.0f - degrees));
2561  }
2562  if(optional_string("+Reorient Max Rotate Angle:")) {
2563  float degrees;
2564  stuff_float(&degrees);
2565  sip->collision_physics.reorient_max_rot_angle = cosf(fl_radians(90.0f - degrees));
2566  }
2567  if(optional_string("+Reorient Speed Mult:")) {
2569  }
2570  if(optional_string("+Landing Rest Angle:")) {
2571  float degrees;
2572  stuff_float(&degrees);
2573  sip->collision_physics.landing_rest_angle = cosf(fl_radians(90.0f - degrees));
2574  }
2575  parse_sound("+Landing Sound:", &sip->collision_physics.landing_sound_idx, sip->name);
2576  }
2577 
2578 
2579  if(optional_string("$Debris:"))
2580  {
2581  if(optional_string("+Min Lifetime:")) {
2583  if(sip->debris_min_lifetime < 0.0f)
2584  Warning(LOCATION, "Debris min lifetime on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2585  }
2586  if(optional_string("+Max Lifetime:")) {
2588  if(sip->debris_max_lifetime < 0.0f)
2589  Warning(LOCATION, "Debris max lifetime on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2590  }
2591  if(optional_string("+Min Speed:")) {
2593  if(sip->debris_min_speed < 0.0f)
2594  Warning(LOCATION, "Debris min speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2595  }
2596  if(optional_string("+Max Speed:")) {
2598  if(sip->debris_max_speed < 0.0f)
2599  Warning(LOCATION, "Debris max speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2600  }
2601  if(optional_string("+Min Rotation speed:")) {
2603  if(sip->debris_min_rotspeed < 0.0f)
2604  Warning(LOCATION, "Debris min speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2605  }
2606  if(optional_string("+Max Rotation speed:")) {
2608  if(sip->debris_max_rotspeed < 0.0f)
2609  Warning(LOCATION, "Debris max speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2610  }
2611  if(optional_string("+Damage Type:")) {
2614  }
2615  if(optional_string("+Min Hitpoints:")) {
2617  if(sip->debris_min_hitpoints < 0.0f)
2618  Warning(LOCATION, "Debris min hitpoints on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2619  }
2620  if(optional_string("+Max Hitpoints:")) {
2622  if(sip->debris_max_hitpoints < 0.0f)
2623  Warning(LOCATION, "Debris max hitpoints on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2624  }
2625  if(optional_string("+Damage Multiplier:")) {
2627  if(sip->debris_damage_mult < 0.0f)
2628  Warning(LOCATION, "Debris damage multiplier on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
2629  }
2630  if(optional_string("+Lightning Arc Percent:")) {
2632  if(sip->debris_arc_percent < 0.0f || sip->debris_arc_percent > 100.0f) {
2633  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);
2634  sip->debris_arc_percent = 50.0;
2635  }
2636  //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0
2637  sip->debris_arc_percent /= 100.0;
2638  }
2639 
2640  }
2641  //WMC - sanity checking
2642  if(sip->debris_min_speed > sip->debris_max_speed && sip->debris_max_speed >= 0.0f) {
2643  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);
2644  sip->debris_min_speed = sip->debris_max_speed;
2645  }
2646  if(sip->debris_min_rotspeed > sip->debris_max_rotspeed && sip->debris_max_rotspeed >= 0.0f) {
2647  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);
2649  }
2650  if(sip->debris_min_lifetime > sip->debris_max_lifetime && sip->debris_max_lifetime >= 0.0f) {
2651  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);
2653  }
2654  if(sip->debris_min_hitpoints > sip->debris_max_hitpoints && sip->debris_max_hitpoints >= 0.0f) {
2655  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);
2657  }
2658 
2659  if(optional_string("$Density:"))
2660  stuff_float( &(sip->density) );
2661  diag_printf ("Ship density -- %7.3f\n", sip->density);
2662 
2663  if(optional_string("$Damp:"))
2664  stuff_float( &(sip->damp) );
2665  diag_printf ("Ship damp -- %7.3f\n", sip->damp);
2666 
2667  if(optional_string("$Rotdamp:"))
2668  stuff_float( &(sip->rotdamp) );
2669  diag_printf ("Ship rotdamp -- %7.3f\n", sip->rotdamp);
2670 
2671  if(optional_string("$Banking Constant:"))
2672  stuff_float( &(sip->delta_bank_const) );
2673  diag_printf ("%s '%s' delta_bank_const -- %7.3f\n", info_type_name, sip->name, sip->delta_bank_const);
2674 
2675  if(optional_string("$Max Velocity:"))
2676  {
2677  stuff_vec3d(&sip->max_vel);
2678  sip->max_accel = sip->max_vel.xyz.z;
2679  }
2680 
2681  // calculate the max speed from max_velocity
2682  sip->max_speed = sip->max_vel.xyz.z;
2683 
2684  if(optional_string("$Rotation Time:"))
2685  {
2686  stuff_vec3d(&sip->rotation_time);
2687 
2688  // div/0 safety check.
2689  if ((sip->rotation_time.xyz.x == 0) || (sip->rotation_time.xyz.y == 0) || (sip->rotation_time.xyz.z == 0))
2690  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);
2691 
2692  sip->srotation_time = (sip->rotation_time.xyz.x + sip->rotation_time.xyz.y)/2.0f;
2693 
2694  sip->max_rotvel.xyz.x = (2 * PI) / sip->rotation_time.xyz.x;
2695  sip->max_rotvel.xyz.y = (2 * PI) / sip->rotation_time.xyz.y;
2696  sip->max_rotvel.xyz.z = (2 * PI) / sip->rotation_time.xyz.z;
2697  }
2698 
2699  // get the backwards velocity;
2700  if(optional_string("$Rear Velocity:"))
2701  {
2702  stuff_float(&sip->max_rear_vel);
2703  sip->min_speed = -sip->max_rear_vel;
2704  }
2705 
2706  // get the accelerations
2707  if(optional_string("$Forward accel:"))
2708  stuff_float(&sip->forward_accel );
2709 
2710  if(optional_string("$Forward decel:"))
2711  stuff_float(&sip->forward_decel );
2712 
2713  if(optional_string("$Slide accel:"))
2714  stuff_float(&sip->slide_accel );
2715 
2716  if(optional_string("$Slide decel:"))
2717  stuff_float(&sip->slide_decel );
2718 
2719  if(optional_string("$Glide:"))
2720  {
2721  stuff_boolean(&sip->can_glide);
2722  }
2723 
2724  if(sip->can_glide == true)
2725  {
2726  if(optional_string("+Dynamic Glide Cap:"))
2728  if(optional_string("+Max Glide Speed:"))
2729  stuff_float(&sip->glide_cap );
2730  if(optional_string("+Glide Accel Mult:"))
2732  }
2733 
2734  if(optional_string("$Use Newtonian Dampening:")) {
2735  sip->newtonian_damp_override = true;
2737  }
2738 
2739  if(optional_string("$Autoaim FOV:"))
2740  {
2741  float fov_temp;
2742  stuff_float(&fov_temp);
2743 
2744  // Make sure it is a reasonable value
2745  if (fov_temp < 0.0f)
2746  fov_temp = 0.0f;
2747 
2748  if (fov_temp > 180.0f)
2749  fov_temp = 180.0f;
2750 
2752  sip->autoaim_fov = fov_temp * PI / 180.0f;
2753 
2754  if(optional_string("+Converging Autoaim"))
2756 
2757  if(optional_string("+Minimum Distance:"))
2759  }
2760 
2761  if(optional_string("$Convergence:"))
2762  {
2763  if(optional_string("+Automatic"))
2764  {
2766  if(optional_string("+Minimum Distance:"))
2768  }
2769  if(optional_string("+Standard"))
2770  {
2772  if(required_string("+Distance:"))
2774  }
2775  if(optional_string("+Offset:")) {
2777 
2778  if (IS_VEC_NULL(&sip->convergence_offset))
2780  else
2782  }
2783  }
2784 
2785  if(optional_string("$Warpin type:"))
2786  {
2788  j = warptype_match(buf);
2789  if(j >= 0) {
2790  sip->warpin_type = j;
2791  } else {
2792  Warning(LOCATION, "Invalid warpin type '%s' specified for %s '%s'", buf, info_type_name, sip->name);
2793  sip->warpin_type = WT_DEFAULT;
2794  }
2795  }
2796 
2797  parse_sound("$Warpin Start Sound:", &sip->warpin_snd_start, sip->name);
2798  parse_sound("$Warpin End Sound:", &sip->warpin_snd_end, sip->name);
2799 
2800  if(optional_string("$Warpin speed:"))
2801  {
2802  stuff_float(&sip->warpin_speed);
2803  }
2804 
2805  if(optional_string("$Warpin time:"))
2806  {
2807  float t_time;
2808  stuff_float(&t_time);
2809  sip->warpin_time = fl2i(t_time*1000.0f);
2810  if(sip->warpin_time <= 0) {
2811  Warning(LOCATION, "Warp-in time specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
2812  }
2813  }
2814 
2815  if(optional_string("$Warpin decel exp:"))
2816  {
2818  if (sip->warpin_decel_exp < 0.0f) {
2819  Warning(LOCATION, "Warp-in deceleration exponent specified as less than 0 on %s '%s'; value ignored", info_type_name, sip->name);
2820  sip->warpin_decel_exp = 1.0f;
2821  }
2822  }
2823 
2824  if(optional_string("$Warpin radius:"))
2825  {
2826  stuff_float(&sip->warpin_radius);
2827  if(sip->warpin_radius <= 0.0f) {
2828  Warning(LOCATION, "Warp-in radius specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
2829  }
2830  }
2831 
2832  if(optional_string("$Warpin animation:"))
2833  {
2835  }
2836 
2837  if(optional_string("$Warpout type:"))
2838  {
2840  j = warptype_match(buf);
2841  if(j >= 0) {
2842  sip->warpout_type = j;
2843  } else {
2844  Warning(LOCATION, "Invalid warpout type '%s' specified for %s '%s'", buf, info_type_name, sip->name);
2845  sip->warpout_type = WT_DEFAULT;
2846  }
2847  }
2848 
2849  parse_sound("$Warpout Start Sound:", &sip->warpout_snd_start, sip->name);
2850  parse_sound("$Warpout End Sound:", &sip->warpout_snd_end, sip->name);
2851 
2852  if(optional_string("$Warpout engage time:"))
2853  {
2854  float t_time;
2855  stuff_float(&t_time);
2856  if (t_time >= 0)
2857  sip->warpout_engage_time = fl2i(t_time*1000.0f);
2858  else
2859  Warning(LOCATION, "Warp-out engage time specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
2860  } else {
2861  sip->warpout_engage_time = -1;
2862  }
2863 
2864  if(optional_string("$Warpout speed:"))
2865  {
2866  stuff_float(&sip->warpout_speed);
2867  }
2868 
2869  if(optional_string("$Warpout time:"))
2870  {
2871  float t_time;
2872  stuff_float(&t_time);
2873  sip->warpout_time = fl2i(t_time*1000.0f);
2874  if(sip->warpout_time <= 0) {
2875  Warning(LOCATION, "Warp-out time specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
2876  }
2877  }
2878 
2879  if(optional_string("$Warpout accel exp:"))
2880  {
2882  if (sip->warpout_accel_exp < 0.0f) {
2883  Warning(LOCATION, "Warp-out acceleration exponent specified as less than 0 on %s '%s'; value ignored", info_type_name, sip->name);
2884  sip->warpout_accel_exp = 1.0f;
2885  }
2886  }
2887 
2888  if(optional_string("$Warpout radius:"))
2889  {
2890  stuff_float(&sip->warpout_radius);
2891  if(sip->warpout_radius <= 0.0f) {
2892  Warning(LOCATION, "Warp-out radius specified as 0 or less on %s '%s'; value ignored", info_type_name, sip->name);
2893  }
2894  }
2895 
2896  if(optional_string("$Warpout animation:"))
2897  {
2899  }
2900 
2901 
2902  if(optional_string("$Player warpout speed:"))
2903  {
2905  if(sip->warpout_player_speed == 0.0f) {
2906  Warning(LOCATION, "Player warp-out speed cannot be 0; value ignored.");
2907  }
2908  }
2909 
2910  // get ship explosion info
2911  shockwave_create_info *sci = &sip->shockwave;
2912  if(optional_string("$Expl inner rad:")){
2913  stuff_float(&sci->inner_rad);
2914  }
2915 
2916  if(optional_string("$Expl outer rad:")){
2917  stuff_float(&sci->outer_rad);
2918  }
2919 
2920  if(optional_string("$Expl damage:")){
2921  stuff_float(&sci->damage);
2922  }
2923 
2924  if(optional_string("$Expl blast:")){
2925  stuff_float(&sci->blast);
2926  }
2927 
2928  if(optional_string("$Expl Propagates:")){
2930  }
2931 
2932  if(optional_string("$Propagating Expl Radius Multiplier:")){
2934  if(sip->prop_exp_rad_mult <= 0) {
2935  // on invalid value return to default setting
2936  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);
2937  sip->prop_exp_rad_mult = 1.0f;
2938  }
2939  }
2940 
2941  if(optional_string("$Expl Visual Rad:")){
2943  }
2944 
2945  if(optional_string("$Base Death-Roll Time:")){
2947  if (sip->death_roll_base_time < 2)
2948  sip->death_roll_base_time = 2;
2949  }
2950 
2951  if(optional_string("$Death-Roll Explosion Radius Mult:")){
2953  if (sip->death_roll_r_mult < 0)
2954  sip->death_roll_r_mult = 0;
2955  }
2956 
2957  if(optional_string("$Death-Roll Explosion Intensity Mult:")){
2959  if (sip->death_roll_time_mult <= 0)
2960  sip->death_roll_time_mult = 1.0f;
2961  }
2962 
2963  if(optional_string("$Death FX Explosion Radius Mult:")){
2965  if (sip->death_fx_r_mult < 0)
2966  sip->death_fx_r_mult = 0;
2967  }
2968 
2969  if(optional_string("$Death FX Explosion Count:")){
2970  stuff_int(&sip->death_fx_count);
2971  if (sip->death_fx_count < 0)
2972  sip->death_fx_count = 0;
2973  }
2974 
2975  if(optional_string("$Ship Splitting Particles:"))
2976  {
2977  parse_ship_particle_effect(sip, &sip->split_particles, "ship split spew");
2978  }
2979 
2980  if(optional_string("$Ship Death Particles:"))
2981  {
2982  parse_ship_particle_effect(sip, &sip->regular_end_particles, "normal death spew");
2983  }
2984 
2985  if(optional_string("$Alternate Death Particles:"))
2986  {
2987  parse_ship_particle_effect(sip, &sip->knossos_end_particles, "knossos death spew");
2988  }
2989 
2990  if(optional_string("$Vaporize Percent Chance:")){
2992  if (sip->vaporize_chance < 0.0f || sip->vaporize_chance > 100.0f) {
2993  sip->vaporize_chance = 0.0f;
2994  Warning(LOCATION, "$Vaporize Percent Chance should be between 0 and 100.0 (read %f). Setting to 0.", sip->vaporize_chance);
2995  }
2996  //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0
2997  sip->vaporize_chance /= 100.0;
2998  }
2999 
3000  if(optional_string("$Shockwave Damage Type:")) {
3004  }
3005 
3006  if(optional_string("$Shockwave Speed:")){
3007  stuff_float( &sci->speed );
3008  }
3009 
3010  if(optional_string("$Shockwave Count:")){
3011  stuff_int(&sip->shockwave_count);
3012  }
3013 
3014  if(optional_string("$Shockwave model:")){
3016  }
3017 
3018  if(optional_string("$Shockwave name:")) {
3020  }
3021 
3022  if(optional_string("$Explosion Animations:")){
3023  int temp[MAX_FIREBALL_TYPES];
3024  int parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
3025  sip->explosion_bitmap_anims.clear();
3026  sip->explosion_bitmap_anims.insert(sip->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
3027  }
3028 
3029  if (optional_string("$Weapon Model Draw Distance:")) {
3031  }
3032 
3033  // Set the weapons filter used in weapons loadout (for primary weapons)
3034  parse_allowed_weapons(sip, true, false, first_time);
3035  parse_allowed_weapons(sip, true, true, first_time);
3036 
3037  // Get primary bank weapons
3039 
3040  if(optional_string("$Show Primary Models:"))
3041  {
3044  }
3045 
3046  // Set the weapons filter used in weapons loadout (for secondary weapons)
3047  parse_allowed_weapons(sip, false, false, first_time);
3048  parse_allowed_weapons(sip, false, true, first_time);
3049 
3050  // Get secondary bank weapons
3052 
3053  if(optional_string("$Show Secondary Models:"))
3054  {
3057  }
3058 
3059  if (optional_string("$Ship Recoil Modifier:")){
3061  }
3062 
3063  if(optional_string("$Shields:")) {
3065 
3066  if(optional_string("+Auto Spread:")) {
3068  }
3069  if(optional_string("+Minimum Weapon Span:")) {
3071  }
3072  if(optional_string("+Allow Bypass:")) {
3074  }
3075  if(optional_string("+Spread From LOD:")) {
3076  int temp;
3077  stuff_int(&temp);
3078 
3079  if (temp > sip->num_detail_levels)
3080  Warning(LOCATION, "+Spread From LOD for %s was %i whereas ship only has %i detail levels, ignoring...", sip->name, temp, sip->num_detail_levels);
3081  else
3083  }
3084  }
3085 
3086  if(optional_string("$Model Point Shield Controls:")) {
3087  SCP_vector<SCP_string> ctrl_strings;
3088  int num_strings = stuff_string_list(ctrl_strings);
3089 
3090  // Init all to -1 in case some aren't supplied...
3095 
3096  for (i = 0; i < num_strings; i++) {
3097  const char *str = ctrl_strings[i].c_str();
3098 
3099  if (!stricmp(str, "front"))
3101  else if (!stricmp(str, "rear"))
3103  else if (!stricmp(str, "left"))
3105  else if (!stricmp(str, "right"))
3107  else if (!stricmp(str, "none"))
3108  ;
3109  else
3110  Warning(LOCATION, "Unrecognized value \"%s\" passed to $Model Point Shield Controls, ignoring...", str);
3111  }
3112  }
3113 
3114  // optional shield color
3115  if(optional_string("$Shield Color:")){
3116  stuff_ubyte(&sip->shield_color[0]);
3117  stuff_ubyte(&sip->shield_color[1]);
3118  stuff_ubyte(&sip->shield_color[2]);
3119  }
3120 
3121  if(optional_string("$Shield Impact Explosion:")) {
3122  char fname[MAX_NAME_LEN];
3123  stuff_string(fname, F_NAME, NAME_LENGTH);
3124 
3125  if ( VALID_FNAME(fname) )
3127  }
3128 
3129  if(optional_string("$Max Shield Recharge:")){
3131  CLAMP(sip->max_shield_recharge, 0.0f, 1.0f);
3132  }
3133 
3134  // The next five fields are used for the ETS
3135  if (optional_string("$Power Output:"))
3136  stuff_float(&sip->power_output);
3137 
3138  // Goober5000
3139  if (optional_string("$Shield Regeneration Rate:"))
3141  else if (first_time)
3142  sip->max_shield_regen_per_second = 0.02f;
3143 
3144  // Support ship hull shield rate - if allowed
3145  if(optional_string("$Support Shield Repair Rate:"))
3146  {
3148  sip->sup_shield_repair_rate *= 0.01f;
3149  CLAMP(sip->sup_shield_repair_rate, 0.0f, 1.0f);
3150  }
3151 
3152  // Goober5000
3153  if (optional_string("$Weapon Regeneration Rate:"))
3155  else if (first_time)
3156  sip->max_weapon_regen_per_second = 0.04f;
3157 
3158  if (optional_string("$Max Oclk Speed:") || optional_string("$Max Overclock Speed:"))
3160  else if (first_time)
3161  sip->max_overclocked_speed = sip->max_vel.xyz.z * 1.5f;
3162 
3163  if (optional_string("$Max Weapon Eng:") || optional_string("$Max Weapon Energy:"))
3165 
3166  if(optional_string("$Hitpoints:"))
3167  {
3169  if (sip->max_hull_strength < 0.0f)
3170  {
3171  Warning(LOCATION, "Max hull strength on %s '%s' cannot be less than 0. Defaulting to 100.\n", info_type_name, sip->name);
3172  sip->max_hull_strength = 100.0f;
3173  }
3174  }
3175 
3176  //Hull rep rate
3177 
3178  if(optional_string("$Hull Repair Rate:"))
3179  {
3181  sip->hull_repair_rate *= 0.01f;
3182 
3183  //Sanity checking
3184  if(sip->hull_repair_rate > 1.0f)
3185  sip->hull_repair_rate = 1.0f;
3186  else if(sip->hull_repair_rate < -1.0f)
3187  sip->hull_repair_rate = -1.0f;
3188  }
3189 
3190  // Support ship hull repair rate - if allowed
3191  if(optional_string("$Support Hull Repair Rate:"))
3192  {
3194  sip->sup_hull_repair_rate *= 0.01f;
3195  CLAMP(sip->sup_hull_repair_rate, 0.0f, 1.0f);
3196  }
3197 
3198  //Subsys rep rate
3199  if(optional_string("$Subsystem Repair Rate:"))
3200  {
3202  sip->subsys_repair_rate *= 0.01f;
3203 
3204  //Sanity checking
3205  if(sip->subsys_repair_rate > 1.0f)
3206  sip->subsys_repair_rate = 1.0f;
3207  else if(sip->subsys_repair_rate < -1.0f)
3208  sip->subsys_repair_rate = -1.0f;
3209  }
3210 
3211  // Support ship hull repair rate
3212  if(optional_string("$Support Subsystem Repair Rate:"))
3213  {
3215  sip->sup_subsys_repair_rate *= 0.01f;
3216  CLAMP(sip->sup_subsys_repair_rate, 0.0f, 1.0f);
3217  }
3218 
3219  if(optional_string("$Armor Type:"))
3220  {
3222  sip->armor_type_idx = armor_type_get_idx(buf);
3223 
3224  if(sip->armor_type_idx == -1)
3225  Warning(LOCATION,"Invalid armor name %s specified for hull in %s '%s'", buf, info_type_name, sip->name);
3226  }
3227 
3228  if(optional_string("$Shield Armor Type:"))
3229  {
3232 
3233  if(sip->shield_armor_type_idx == -1)
3234  Warning(LOCATION,"Invalid armor name %s specified for shield in %s '%s'", buf, info_type_name, sip->name);
3235  }
3236 
3237  if (optional_string("$Flags:"))
3238  {
3239  // we'll assume the list will contain no more than 20 distinct tokens
3240  char ship_strings[20][NAME_LENGTH];
3241  int num_strings = stuff_string_list(ship_strings, 20);
3242 
3243  int ship_type_index = -1;
3244 
3245  if (!optional_string("+noreplace")) {
3246  // clear flags since we might have a modular table
3247  // clear only those which are actually set in the flags
3248  sip->flags = (sip->flags & SIF_MASK);
3249  sip->flags2 = (sip->flags2 & SIF2_MASK);
3250  }
3251 
3252  for (i = 0; i < num_strings; i++)
3253  {
3254  // get ship type from ship flags
3255  char *ship_type = ship_strings[i];
3256  bool flag_found = false;
3257 
3258  // Goober5000 - in retail FreeSpace, some ship classes were specified differently
3259  // in ships.tbl and the ship type array; this patches those differences so that
3260  // the ship type lookup will work properly
3261  if (!stricmp(ship_type, "sentrygun"))
3262  ship_type = "sentry gun";
3263  else if (!stricmp(ship_type, "escapepod"))
3264  ship_type = "escape pod";
3265  else if (!stricmp(ship_type, "repair_rearm"))
3266  ship_type = "support";
3267  else if (!stricmp(ship_type, "supercap"))
3268  ship_type = "super cap";
3269  else if (!stricmp(ship_type, "knossos"))
3270  ship_type = "knossos device";
3271 
3272  // look it up in the object types table
3273  ship_type_index = ship_type_name_lookup(ship_type);
3274 
3275  // set ship class type
3276  if ((ship_type_index >= 0) && (sip->class_type < 0))
3277  sip->class_type = ship_type_index;
3278 
3279  // check various ship flags
3280  for (int idx = 0; idx < Num_ship_flags; idx++) {
3281  if ( !stricmp(Ship_flags[idx].name, ship_strings[i]) ) {
3282  flag_found = true;
3283 
3284  if (Ship_flags[idx].var == 255)
3285  Warning(LOCATION, "Use of '%s' flag for %s '%s' - this flag is no longer needed.", Ship_flags[idx].name, info_type_name, sip->name);
3286  else if (Ship_flags[idx].var == 0)
3287  sip->flags |= Ship_flags[idx].def;
3288  else if (Ship_flags[idx].var == 1)
3289  sip->flags2 |= Ship_flags[idx].def;
3290  }
3291  }
3292 
3293  if ( !flag_found && (ship_type_index < 0) )
3294  Warning(LOCATION, "Bogus string in ship flags: %s\n", ship_strings[i]);
3295  }
3296 
3297  // set original status of tech database flags - Goober5000
3298  if (sip->flags & SIF_IN_TECH_DATABASE)
3300  if (sip->flags & SIF_IN_TECH_DATABASE_M)
3302  }
3303 
3304  // Goober5000 - ensure number of banks checks out
3306  {
3307  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);
3308  }
3309 
3310  memset(sip->allowed_weapons, 0, sizeof(int) * MAX_WEAPON_TYPES);
3311 
3312  // copy to regular allowed_weapons array
3313  for (i = 0; i < MAX_SHIP_WEAPONS; i++)
3314  {
3315  for (j = 0; j < Num_weapon_types; j++)
3316  {
3318  sip->allowed_weapons[j] |= REGULAR_WEAPON;
3319 
3321  sip->allowed_weapons[j] |= DOGFIGHT_WEAPON;
3322  }
3323  }
3324 
3325  sip->flags &= ~SIF_BALLISTIC_PRIMARIES;
3326 
3327  //Set ship ballistic flag if necessary
3328  for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++)
3329  {
3330  for (j = 0; j < Num_weapon_types; j++)
3331  {
3333  {
3335  break;
3336  }
3337  }
3338  }
3339 
3340  find_and_stuff_optional("$AI Class:", &sip->ai_class, F_NAME, Ai_class_names, Num_ai_classes, "AI class names");
3341 
3342  // Get Afterburner information
3343  // Be aware that if $Afterburner is not 1, the other Afterburner fields are not read in
3344  int has_afterburner = 0;
3345 
3346  if(optional_string("$Afterburner:"))
3347  stuff_boolean(&has_afterburner);
3348 
3349  if ( has_afterburner == 1 )
3350  {
3351  sip->flags |= SIF_AFTERBURNER;
3352 
3353  if(optional_string("+Aburn Max Vel:")) {
3355  }
3356 
3357  if(optional_string("+Aburn For accel:")) {
3359  }
3360 
3361  // SparK: added reverse burner capability
3362  if(optional_string("+Aburn Max Reverse Vel:")) {
3364  }
3365  if(optional_string("+Aburn Rev accel:")) {
3367  }
3368 
3369  if(optional_string("+Aburn Fuel:")) {
3371  }
3372 
3373  if(optional_string("+Aburn Burn Rate:")) {
3375  }
3376 
3377  if(optional_string("+Aburn Rec Rate:")) {
3379  }
3380 
3381  if (!(sip->afterburner_fuel_capacity) ) {
3382  Warning(LOCATION, "%s '%s' has an afterburner but has no afterburner fuel. Setting fuel to 1", info_type_name, sip->name);
3383  sip->afterburner_fuel_capacity = 1.0f;
3384  }
3385  }
3386 
3387  if ( optional_string("$Trails:") ) {
3388  bool trails_warning = true;
3389 
3390  if (optional_string("+Bitmap:") ) {
3391  trails_warning = false;
3394  }
3395 
3396  if ( optional_string("+Width:") ) {
3397  trails_warning = false;
3399  }
3400 
3401  if ( optional_string("+Alpha:") ) {
3402  trails_warning = false;
3404  }
3405 
3406  if ( optional_string("+Life:") ) {
3407  trails_warning = false;
3409  }
3410 
3411  if ( optional_string("+Faded Out Sections:") ) {
3412  trails_warning = false;
3414  }
3415 
3416  if (trails_warning)
3417  Warning(LOCATION, "%s %s entry has $Trails field specified, but no properties given.", info_type_name, sip->name);
3418  }
3419 
3420  if (optional_string("$Countermeasure type:")) {
3422  int res = weapon_info_lookup(buf);
3423  if (res < 0) {
3424  Warning(LOCATION, "Could not find weapon type '%s' to use as countermeasure on %s '%s'", buf, info_type_name, sip->name);
3425  } else if (Weapon_info[res].wi_flags & WIF_BEAM) {
3426  Warning(LOCATION, "Attempt made to set a beam weapon as a countermeasure on %s '%s'", info_type_name, sip->name);
3427  } else {
3428  sip->cmeasure_type = res;
3429  }
3430  } else if (Species_info[sip->species].cmeasure_index >= 0) {
3431  sip->cmeasure_type = Species_info[sip->species].cmeasure_index;
3432  }
3433 
3434  if(optional_string("$Countermeasures:"))
3435  stuff_int(&sip->cmeasure_max);
3436 
3437  if(optional_string("$Scan time:"))
3438  stuff_int(&sip->scan_time);
3439 
3440  //Parse the engine sound
3441  parse_sound("$EngineSnd:", &sip->engine_snd, sip->name);
3442 
3443  if(optional_string("$Minimum Engine Volume:"))
3444  stuff_float(&sip->min_engine_vol);
3445 
3446  //Parse optional sound to be used for beginning of a glide
3447  parse_sound("$GlideStartSnd:", &sip->glide_start_snd, sip->name);
3448 
3449  //Parse optional sound to be used for end of a glide
3450  parse_sound("$GlideEndSnd:", &sip->glide_end_snd, sip->name);
3451 
3452  parse_ship_sounds(sip);
3453 
3454  if(optional_string("$Closeup_pos:"))
3455  {
3456  stuff_vec3d(&sip->closeup_pos);
3457  }
3458  else if (first_time && strlen(sip->pof_file))
3459  {
3460  //Calculate from the model file. This is inefficient, but whatever
3461  int model_idx = model_load(sip->pof_file, 0, NULL);
3462  polymodel *pm = model_get(model_idx);
3463 
3464  //Go through, find best
3465  sip->closeup_pos.xyz.z = fabsf(pm->maxs.xyz.z);
3466 
3467  float temp = fabsf(pm->mins.xyz.z);
3468  if(temp > sip->closeup_pos.xyz.z)
3469  sip->closeup_pos.xyz.z = temp;
3470 
3471  //Now multiply by 2
3472  sip->closeup_pos.xyz.z *= -2.0f;
3473 
3474  //We're done with the model.
3475  model_unload(model_idx);
3476  }
3477 
3478  if (optional_string("$Closeup_zoom:")) {
3479  stuff_float(&sip->closeup_zoom);
3480 
3481  if (sip->closeup_zoom <= 0.0f) {
3482  mprintf(("Warning! Ship '%s' has a $Closeup_zoom value that is less than or equal to 0 (%f). Setting to default value.\n", sip->name, sip->closeup_zoom));
3483  sip->closeup_zoom = 0.5f;
3484  }
3485  }
3486 
3487  if(optional_string("$Topdown offset:")) {
3488  sip->topdown_offset_def = true;
3489  stuff_vec3d(&sip->topdown_offset);
3490  }
3491 
3492  if (optional_string("$Shield_icon:")) {
3493  stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
3494  hud_shield_assign_info(sip, name_tmp);
3495  }
3496 
3497  // read in filename for icon that is used in ship selection
3498  if ( optional_string("$Ship_icon:") ) {
3500  }
3501 
3502  if ( optional_string("$Model Icon Direction:") ) {
3503  char str[NAME_LENGTH];
3505 
3506  angles model_icon_angles = {0.0f,0.0f,0.0f};
3507 
3508  if (!stricmp(str, "top")) {
3509  model_icon_angles.p = -PI_2;
3510  } else if (!stricmp(str, "bottom")) {
3511  model_icon_angles.p = -PI_2;
3512  model_icon_angles.b = 2 * PI_2;
3513  } else if (!stricmp(str, "front")) {
3514  model_icon_angles.h = 2 * PI_2;
3515  } else if (!stricmp(str, "back")) {
3516  model_icon_angles.h = 4 * PI_2;
3517  } else if (!stricmp(str, "left")) {
3518  model_icon_angles.h = -PI_2;
3519  } else if (!stricmp(str, "right")) {
3520  model_icon_angles.h = PI_2;
3521  } else {
3522  Warning(LOCATION, "Unrecognized value \"%s\" passed to $Model Icon Direction, ignoring...", str);
3523  }
3524 
3525  sip->model_icon_angles = model_icon_angles;
3526  }
3527 
3528  // read in filename for animation that is used in ship selection
3529  if ( optional_string("$Ship_anim:") ) {
3531  }
3532 
3533  // read in filename for animation that is used in ship selection
3534  if ( optional_string("$Ship_overhead:") ) {
3536  }
3537 
3538  // read in briefing stuff
3539  if ( optional_string("$Briefing icon:") )
3541  if ( optional_string("$Briefing icon with cargo:") )
3543  if ( optional_string("$Briefing wing icon:") )
3545  if ( optional_string("$Briefing wing icon with cargo:") )
3547 
3548  // check for inconsistencies
3549  if ((sip->bii_index_wing_with_cargo >= 0) && (sip->bii_index_wing < 0 || sip->bii_index_ship_with_cargo < 0))
3550  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);
3551  if ((sip->bii_index_wing_with_cargo < 0) && (sip->bii_index_wing >= 0) && (sip->bii_index_ship_with_cargo >= 0))
3552  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);
3553 
3554  if ( optional_string("$Score:") ){
3555  stuff_int( &sip->score );
3556  }
3557 
3558  if (first_time)
3559  {
3560  species_info *species = &Species_info[sip->species];
3561 
3562  sip->thruster_flame_info = species->thruster_info.flames;
3563  sip->thruster_glow_info = species->thruster_info.glow;
3567  }
3568 
3569  if ( optional_string("$Thruster Normal Flame:") ) {
3570  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3571 
3572  if ( VALID_FNAME(name_tmp) )
3573  generic_anim_init( &sip->thruster_flame_info.normal, name_tmp );
3574  }
3575 
3576  if ( optional_string("$Thruster Afterburner Flame:") ) {
3577  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3578 
3579  if ( VALID_FNAME(name_tmp) )
3580  generic_anim_init( &sip->thruster_flame_info.afterburn, name_tmp );
3581  }
3582 
3583  if ( optional_string("$Thruster Bitmap 1:") ) {
3584  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3585 
3586  if ( VALID_FNAME(name_tmp) ) {
3587  strcpy_s(sip->thruster_glow_info.normal.filename, name_tmp);
3589  }
3590  }
3591 
3592  if ( optional_string("$Thruster Bitmap 1a:") ) {
3593  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3594 
3595  if ( VALID_FNAME(name_tmp) ) {
3598  }
3599  }
3600 
3601  if ( optional_string("$Thruster01 Radius factor:") ) {
3603  }
3604 
3605  if ( optional_string("$Thruster Bitmap 2:") ) {
3606  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3607 
3608  if ( VALID_FNAME(name_tmp) )
3610  }
3611 
3612  if ( optional_string("$Thruster Bitmap 2a:") ) {
3613  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3614 
3615  if ( VALID_FNAME(name_tmp) )
3617  }
3618 
3619  if ( optional_string("$Thruster02 Radius factor:") ) {
3621  }
3622 
3623  if ( optional_string("$Thruster01 Length factor:") ) {
3625  Warning(LOCATION, "Deprecated spelling: \"$Thruster01 Length factor:\". Use \"$Thruster02 Length factor:\" instead.");
3626  }
3627 
3628  if ( optional_string("$Thruster02 Length factor:") ) {
3630  }
3631 
3632  if ( optional_string("$Thruster Bitmap 3:") ) {
3633  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3634 
3635  if ( VALID_FNAME(name_tmp) )
3637  }
3638 
3639  if ( optional_string("$Thruster Bitmap 3a:") ) {
3640  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3641 
3642  if ( VALID_FNAME(name_tmp) )
3644  }
3645 
3646  if ( optional_string("$Thruster03 Radius factor:") ) {
3648  }
3649 
3650  // Valathil - Custom Thruster Distortion
3651  if ( optional_string("$Thruster Bitmap Distortion:") ) {
3652  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3653 
3654  if ( VALID_FNAME(name_tmp) )
3656  }
3657 
3658  if ( optional_string("$Thruster Bitmap Distortion a:") ) {
3659  stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3660 
3661  if ( VALID_FNAME(name_tmp) )
3663  }
3664 
3665  if ( optional_string("$Thruster Distortion Radius factor:") ) {
3667  }
3668 
3669  if ( optional_string("$Thruster Distortion Length factor:") ) {
3671  }
3672 
3673  if ( optional_string("$Thruster Distortion:") ) {
3675  }
3676 
3677  if ( optional_string("$Thruster Glow Noise Mult:") ) {
3679  }
3680 
3681  while ( optional_string("$Thruster Particles:") ) {
3682  bool afterburner = false;
3683  thruster_particles tpart;
3684 
3685  if ( optional_string("$Thruster Particle Bitmap:") )
3686  afterburner = false;
3687  else if ( optional_string("$Afterburner Particle Bitmap:") )
3688  afterburner = true;
3689  else
3690  Error( LOCATION, "formatting error in the thruster's particle section for %s '%s'\n", info_type_name, sip->name );
3691 
3692  generic_anim_init(&tpart.thruster_bitmap, NULL);
3694 
3695  required_string("$Min Radius:");
3696  stuff_float(&tpart.min_rad);
3697 
3698  required_string("$Max Radius:");
3699  stuff_float(&tpart.max_rad);
3700 
3701  required_string("$Min created:");
3702  stuff_int(&tpart.n_low);
3703 
3704  required_string("$Max created:");
3705  stuff_int(&tpart.n_high);
3706 
3707  required_string("$Variance:");
3708  stuff_float(&tpart.variance);
3709 
3710  if (afterburner) {
3711  sip->afterburner_thruster_particles.push_back( tpart );
3712  } else {
3713  sip->normal_thruster_particles.push_back( tpart );
3714  }
3715  }
3716 
3717  // if the ship is a stealth ship
3718  if ( optional_string("$Stealth:") ) {
3719  sip->flags |= SIF_STEALTH;
3720  }
3721 
3722  else if ( optional_string("$Stealth") ) {
3723  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);
3724  sip->flags |= SIF_STEALTH;
3725  }
3726 
3727  if ( optional_string("$max decals:") ){
3728  int bogus;
3729  stuff_int(&bogus);
3730  WarningEx(LOCATION, "The decal system has been deactivated in FSO builds. Entries will be discarded.\n");
3731  mprintf(("WARNING: The decal system has been deactivated in FSO builds. Entries will be discarded.\n"));
3732  //Do nothing, left in for compatibility.
3733  }
3734 
3735  // parse contrail info
3736  while ( optional_string("$Trail:") ) {
3737  // setting '+ClearAll' resets the trails
3738  if ( optional_string("+ClearAll")) {
3739  memset(&sip->ct_info, 0, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
3740  sip->ct_count = 0;
3741  }
3742 
3743  // this means you've reached the max # of contrails for a ship
3744  if (sip->ct_count >= MAX_SHIP_CONTRAILS) {
3745  Warning(LOCATION, "%s '%s' has more contrails than the max of %d", info_type_name, sip->name, MAX_SHIP_CONTRAILS);
3746  break;
3747  }
3748 
3749  trail_info *ci = &sip->ct_info[sip->ct_count++];
3750 
3751  required_string("+Offset:");
3752  stuff_vec3d(&ci->pt);
3753 
3754  required_string("+Start Width:");
3755  stuff_float(&ci->w_start);
3756 
3757  required_string("+End Width:");
3758  stuff_float(&ci->w_end);
3759 
3760  required_string("+Start Alpha:");
3761  stuff_float(&ci->a_start);
3762 
3763  required_string("+End Alpha:");
3764  stuff_float(&ci->a_end);
3765 
3766  required_string("+Max Life:");
3767  stuff_float(&ci->max_life);
3768 
3769  required_string("+Spew Time:");
3770  stuff_int(&ci->stamp);
3771 
3772  required_string("+Bitmap:");
3773  stuff_string(name_tmp, F_NAME, NAME_LENGTH);
3774  generic_bitmap_init(&ci->texture, name_tmp);
3776 
3777  if (optional_string("+Faded Out Sections:") ) {
3779  }
3780  }
3781 
3782  man_thruster *mtp = NULL;
3783  man_thruster manwich;
3784  while(optional_string("$Thruster:"))
3785  {
3786  int idx = -1;
3787  if(optional_string("+index:")) {
3788  stuff_int(&idx);
3789  }
3790 
3791  if(idx >= 0 && idx < sip->num_maneuvering) {
3792  mtp = &sip->maneuvering[idx];
3793  } else if(idx < 0) {
3794  if(sip->num_maneuvering < MAX_MAN_THRUSTERS) {
3795  mtp = &sip->maneuvering[sip->num_maneuvering++];
3796  } else {
3797  Warning(LOCATION, "Too many maneuvering thrusters on %s '%s'; maximum is %d", info_type_name, sip->name, MAX_MAN_THRUSTERS);
3798  }
3799  } else {
3800  mtp = &manwich;
3801  Warning(LOCATION, "Invalid index (%d) specified for maneuvering thruster on %s '%s'", idx, info_type_name, sip->name);
3802  }
3803 
3804  if(optional_string("+Used for:")) {
3805  parse_string_flag_list(&mtp->use_flags, Man_types, Num_man_types);
3806  }
3807 
3808  if(optional_string("+Position:")) {
3809  stuff_float_list(mtp->pos.a1d, 3);
3810  }
3811 
3812  if(optional_string("+Normal:")) {
3813  stuff_float_list(mtp->norm.a1d, 3);
3814  }
3815 
3816  if(optional_string("+Texture:"))
3817  {
3818  stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
3819  int tex_fps=0, tex_nframes=0, tex_id=-1;;
3820  tex_id = bm_load_animation(name_tmp, &tex_nframes, &tex_fps, NULL, 1);
3821  if(tex_id < 0)
3822  tex_id = bm_load(name_tmp);
3823  if(tex_id >= 0)
3824  {
3825  if(mtp->tex_id >= 0) {
3826  bm_unload(mtp->tex_id);
3827  }
3828 
3829  mtp->tex_id = tex_id;
3830  mtp->tex_fps = tex_fps;
3831  mtp->tex_nframes = tex_nframes;
3832  }
3833  }
3834 
3835  if(optional_string("+Radius:")) {
3836  stuff_float(&mtp->radius);
3837  }
3838 
3839  if(optional_string("+Length:")) {
3840  stuff_float(&mtp->length);
3841  }
3842 
3843  parse_sound("+StartSnd:", &mtp->start_snd, sip->name);
3844  parse_sound("+LoopSnd:", &mtp->loop_snd, sip->name);
3845  parse_sound("+StopSnd:", &mtp->stop_snd, sip->name);
3846  }
3847 
3848  if (optional_string("$Glowpoint overrides:")) {
3849  SCP_vector<SCP_string> tokens;
3850  tokens.clear();
3851  stuff_string_list(tokens);
3852  for(SCP_vector<SCP_string>::iterator token = tokens.begin(); token != tokens.end(); ++token) {
3853  SCP_string name, banks;
3854  size_t seppos;
3855  seppos = token->find_first_of(':');
3856  if(seppos == SCP_string::npos) {
3857  Warning(LOCATION, "Couldn't find ':' seperator in Glowpoint override for ship %s ignoring token", sip->name);
3858  continue;
3859  }
3860  name = token->substr(0, seppos);
3861  banks = token->substr(seppos+1);
3863  if(gpo == glowpoint_bank_overrides.end()){
3864  Warning(LOCATION, "Couldn't find preset %s in glowpoints.tbl when parsing ship: %s", name.data(), sip->name);
3865  continue;
3866  }
3867  if(banks == "*") {
3868  sip->glowpoint_bank_override_map[-1] = (void*)(&(*gpo));
3869  continue;
3870  }
3871  SCP_string banktoken;
3872  int start = -1;
3873  int end = -1;
3874  do {
3875  end = banks.find_first_of(',', ++start);
3876  banktoken = banks.substr(start, end);
3877  start = end;
3878 
3879  size_t fromtopos;
3880  fromtopos = banktoken.find_first_of('-');
3881  if(fromtopos != SCP_string::npos) {
3882  SCP_string from, to;
3883  int ifrom, ito;
3884  from = banktoken.substr(0, fromtopos);
3885  to = banktoken.substr(fromtopos+1);
3886  ifrom = atoi(from.data()) - 1;
3887  ito = atoi(to.data()) - 1;
3888  for(int bank = ifrom; bank <= ito; ++bank) {
3889  sip->glowpoint_bank_override_map[bank] = (void*)(&(*gpo));
3890  }
3891  } else {
3892  int bank = atoi(banktoken.data()) - 1;
3893  sip->glowpoint_bank_override_map[bank] = (void*)(&(*gpo));
3894  }
3895  } while(start!=-1);
3896  }
3897  }
3898 
3899  if (optional_string("$Radar Image 2D:"))
3900  {
3901  stuff_string(name_tmp, F_NAME, NAME_LENGTH);
3902  sip->radar_image_2d_idx = bm_load(name_tmp);
3903 
3904  if ( optional_string("$Radar Color Image 2D:") ) {
3905  stuff_string(name_tmp, F_NAME, NAME_LENGTH);
3906  sip->radar_color_image_2d_idx = bm_load(name_tmp);
3907  }
3908 
3909  if (optional_string("$Radar Image Size:"))
3910  stuff_int(&sip->radar_image_size);
3911 
3912  if (optional_string("$3D Radar Blip Size Multiplier:"))
3914  }
3915 
3916  // Alternate - per ship class - IFF colors
3917  while((optional_string("$Ship IFF Colors:")) || (optional_string("$Ship IFF Colours:")))
3918  {
3919  char iff_1[NAME_LENGTH];
3920  char iff_2[NAME_LENGTH];
3921  int iff_color_data[3];
3922  int iff_data[2];
3923 
3924  // Get the iff strings and get the iff indexes
3925  required_string("+Seen By:");
3926  stuff_string(iff_1, F_NAME, NAME_LENGTH);
3927 
3928  required_string("+When IFF Is:");
3929  stuff_string(iff_2, F_NAME, NAME_LENGTH);
3930  iff_data[0] = iff_lookup(iff_1);
3931  iff_data[1] = iff_lookup(iff_2);
3932 
3933  if (iff_data[0] == -1)
3934  WarningEx(LOCATION, "%s '%s'\nIFF colour seen by \"%s\" invalid!", info_type_name, sip->name, iff_1);
3935 
3936  if (iff_data[1] == -1)
3937  WarningEx(LOCATION, "%s '%s'\nIFF colour when IFF is \"%s\" invalid!", info_type_name, sip->name, iff_2);
3938 
3939  // Set the color
3940  required_string("+As Color:");
3941  stuff_int_list(iff_color_data, 3, RAW_INTEGER_TYPE);
3942  sip->ship_iff_info[iff_data[0]][iff_data[1]] = iff_init_color(iff_color_data[0],iff_color_data[1],iff_color_data[2]);
3943  }
3944 
3945  if (optional_string("$Target Priority Groups:") ) {
3946  SCP_vector<SCP_string> target_group_strings;
3947  int num_strings = stuff_string_list(target_group_strings);
3948  int num_groups = Ai_tp_list.size();
3949  bool override_strings = false;
3950 
3951  if (optional_string("+Override")) {
3952  override_strings = true;
3953  }
3954 
3955  for(j = 0; j < num_strings; j++) {
3956  for(i = 0; i < num_groups; i++) {
3957  if ( !stricmp(target_group_strings[j].c_str(), Ai_tp_list[i].name) ) {
3958  //so now the string from the list above as well as the ai priority group name match
3959  //clear it if override has been set
3960  if (override_strings) {
3961  Ai_tp_list[i].ship_class.clear();
3962  override_strings = false;
3963  }
3964  for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
3965  //find the index number of the current ship info type
3966  if (it->name == sip->name) {
3967  Ai_tp_list[i].ship_class.push_back(std::distance(Ship_info.cbegin(), it));
3968  break;
3969  }
3970  }
3971  // found something, try next string
3972  break;
3973  }
3974  }
3975  if (i == num_groups) {
3976  Warning(LOCATION,"Unidentified priority group '%s' set for %s '%s'\n", target_group_strings[j].c_str(), info_type_name, sip->name);
3977  }
3978  }
3979  }
3980 
3981  if (optional_string("$EMP Resistance Modifier:")) {
3983  }
3984 
3985  if (optional_string("$Piercing Damage Draw Limit:")) {
3986  float tempf;
3987  stuff_float(&tempf);
3988  sip->piercing_damage_draw_limit = tempf / 100.0f;
3989  }
3990 
3991  while(optional_string("$Path Metadata:"))
3992  {
3993  char path_name[64];
3994  stuff_string(path_name, F_NAME, sizeof(path_name));
3995 
3996  path_metadata metadata;
3997  init_path_metadata(metadata);
3998 
3999  //Get +departure rvec and store on the path_metadata object
4000  if (optional_string("+departure rvec:"))
4001  {
4002  stuff_vec3d(&metadata.departure_rvec);
4003  }
4004 
4005  if (optional_string("+arrive speed multiplier:"))
4006  {
4007  stuff_float(&metadata.arrive_speed_mult);
4008  }
4009  if (optional_string("+depart speed multiplier:"))
4010  {
4011  stuff_float(&metadata.depart_speed_mult);
4012  }
4013 
4014  //Add the new path_metadata to sip->pathMetadata keyed by path name
4015  SCP_string pathName(path_name);
4016  sip->pathMetadata[pathName] = metadata;
4017  }
4018 
4019  int n_subsystems = 0;
4020  int cont_flag = 1;
4021  model_subsystem subsystems[MAX_MODEL_SUBSYSTEMS]; // see model.h for max_model_subsystems
4022  for (i=0; i<MAX_MODEL_SUBSYSTEMS; i++) {
4023  subsystems[i].stepped_rotation = NULL;
4024  }
4025 
4026  float hull_percentage_of_hits = 100.0f;
4027  //If the ship already has subsystem entries (ie this is a modular table)
4028  //make sure hull_percentage_of_hits is set properly
4029  for(i=0; i < sip->n_subsystems; i++) {
4030  hull_percentage_of_hits -= sip->subsystems[i].max_subsys_strength / sip->max_hull_strength;
4031  }
4032 
4033  while (cont_flag) {
4034  int r = required_string_one_of(3, "#End", "$Subsystem:", type_name);
4035  switch (r) {
4036  case 0:
4037  cont_flag = 0;
4038  break;
4039  case 1:
4040  {
4041  float turning_rate;
4042  float percentage_of_hits;
4043  bool turret_has_base_fov = false;
4044  model_subsystem *sp = NULL; // to append on the ships list of subsystems
4045 
4046  int sfo_return;
4047  required_string("$Subsystem:");
4048  stuff_string(name_tmp, F_NAME, sizeof(name_tmp), ",");
4049  Mp++;
4050  for(i = 0;i < sip->n_subsystems; i++)
4051  {
4052  if(!subsystem_stricmp(sip->subsystems[i].subobj_name, name_tmp))
4053  sp = &sip->subsystems[i];
4054  }
4055 
4056  if(sp == NULL)
4057  {
4058  if( sip->n_subsystems + n_subsystems >= MAX_MODEL_SUBSYSTEMS )
4059  {
4060  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);
4061  break;
4062  }
4063  sp = &subsystems[n_subsystems++]; // subsystems a local -- when done, we will malloc and copy
4064  strcpy_s(sp->subobj_name, name_tmp);
4065 
4066  //Init blank values
4067  sp->max_subsys_strength = 0.0f;
4068  sp->turret_turning_rate = 0.0f;
4069  sp->weapon_rotation_pbank = -1;
4070 
4071  memset(sp->alt_sub_name, 0, sizeof(sp->alt_sub_name) );
4072  memset(sp->alt_dmg_sub_name, 0, sizeof(sp->alt_dmg_sub_name) );
4073 
4074  for (i=0; i<MAX_SHIP_PRIMARY_BANKS; i++) {
4075  sp->primary_banks[i] = -1;
4076  sp->primary_bank_capacity[i] = 0;
4077  }
4078 
4079  for (i=0; i<MAX_SHIP_SECONDARY_BANKS; i++) {
4080  sp->secondary_banks[i] = -1;
4081  sp->secondary_bank_capacity[i] = 0;
4082  }
4083 
4084  sp->engine_wash_pointer = NULL;
4085 
4086  sp->alive_snd = -1;
4087  sp->dead_snd = -1;
4088  sp->rotation_snd = -1;
4089  sp->turret_gun_rotation_snd = -1;
4090  sp->turret_gun_rotation_snd_mult = 1.0f;
4091  sp->turret_base_rotation_snd = -1;
4092  sp->turret_base_rotation_snd_mult = 1.0f;
4093 
4094  sp->flags = 0;
4095  sp->flags2 = 0;
4096 
4097  sp->n_triggers = 0;
4098  sp->triggers = NULL;
4099 
4100  sp->model_num = -1; // init value for later sanity checking!!
4101  sp->armor_type_idx = -1;
4102  sp->path_num = -1;
4103  sp->turret_max_fov = 1.0f;
4104 
4105  sp->turret_reset_delay = 2000;
4106 
4107  sp->num_target_priorities = 0;
4108  for (i = 0; i < 32; i++) {
4109  sp->target_priority[i] = -1;
4110  }
4111  sp->optimum_range = 0.0f;
4112  sp->favor_current_facing = 0.0f;
4113 
4114  sp->turret_rof_scaler = 1.0f;
4115 
4116  sp->turret_max_bomb_ownage = -1;
4117  sp->turret_max_target_ownage = -1;
4118  }
4119  sfo_return = stuff_float_optional(&percentage_of_hits);
4120  if(sfo_return==2)
4121  {
4122  hull_percentage_of_hits -= percentage_of_hits;
4123  sp->max_subsys_strength = sip->max_hull_strength * (percentage_of_hits / 100.0f);
4124  sp->type = SUBSYSTEM_UNKNOWN;
4125  }
4126  if(sfo_return > 0)
4127  {
4128  if(stuff_float_optional(&turning_rate)==2)
4129  {
4130  // specified as how long to turn 360 degrees in ships.tbl
4131  if ( turning_rate > 0.0f ){
4132  sp->turret_turning_rate = PI2 / turning_rate;
4133  } else {
4134  sp->turret_turning_rate = 0.0f;
4135  }
4136  }
4137  else
4138  {
4139  Error(LOCATION, "Malformed $Subsystem entry '%s' in %s '%s'.\n\n"
4140  "Specify a turning rate or remove the trailing comma.",
4141  sp->subobj_name, info_type_name, sip->name);
4142  }
4143  }
4144 
4145  if(optional_string("$Alt Subsystem Name:")) {
4147  strcpy_s(sp->alt_sub_name, buf);
4148  }
4149 
4150  if(optional_string("$Alt Damage Popup Subsystem Name:")) {
4152  strcpy_s(sp->alt_dmg_sub_name, buf);
4153  }
4154 
4155  if(optional_string("$Armor Type:")) {
4158 
4159  if (sp->armor_type_idx == -1)
4160  WarningEx(LOCATION, "%s '%s', subsystem %s\nInvalid armor type %s!", info_type_name, sip->name, sp->subobj_name, buf);
4161  }
4162 
4163  // Get primary bank weapons
4164  parse_weapon_bank(sip, true, NULL, sp->primary_banks, sp->primary_bank_capacity);
4165 
4166  // Get secondary bank weapons
4167  parse_weapon_bank(sip, false, NULL, sp->secondary_banks, sp->secondary_bank_capacity);
4168 
4169  // Get optional engine wake info
4170  if (optional_string("$Engine Wash:")) {
4171  stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
4172  // get and set index
4174 
4175  if(sp->engine_wash_pointer == NULL)
4176  WarningEx(LOCATION,"Invalid engine wash name %s specified for subsystem %s in %s '%s'", name_tmp, sp->subobj_name, info_type_name, sip->name);
4177  }
4178 
4179  parse_sound("$AliveSnd:", &sp->alive_snd, sp->subobj_name);
4180  parse_sound("$DeadSnd:", &sp->dead_snd, sp->subobj_name);
4181  parse_sound("$RotationSnd:", &sp->rotation_snd, sp->subobj_name);
4182  parse_sound("$Turret Base RotationSnd:", &sp->turret_base_rotation_snd, sp->subobj_name);
4183  parse_sound("$Turret Gun RotationSnd:", &sp->turret_gun_rotation_snd, sp->subobj_name);
4184 
4185  if (optional_string("$Turret BaseSnd Volume:"))
4187 
4188  if (optional_string("$Turret GunSnd Volume:"))
4190 
4191  // Get any AWACS info
4192  sp->awacs_intensity = 0.0f;
4193  if(optional_string("$AWACS:")){
4194  sfo_return = stuff_float_optional(&sp->awacs_intensity);
4195  if(sfo_return > 0)
4197  sip->flags |= SIF_HAS_AWACS;
4198  }
4199 
4200  if(optional_string("$Maximum Barrel Elevation:")){
4201  int value;
4202  stuff_int(&value);
4203  CAP(value, 0, 90);
4204  float angle = fl_radians(90 - value);
4205  sp->turret_max_fov = cosf(angle);
4206  }
4207 
4208  if(optional_string("$Turret Base FOV:")) {
4209  int value;
4210  stuff_int(&value);
4211  CAP(value, 0, 359);
4212  float angle = fl_radians(value)/2.0f;
4213  sp->turret_y_fov = cosf(angle);
4214  turret_has_base_fov = true;
4215  }
4216 
4217  if (optional_string("$Turret Reset Delay:"))
4219 
4220  if (optional_string("$Turret Optimum Range:"))
4221  stuff_float(&sp->optimum_range);
4222 
4223  if (optional_string("$Turret Direction Preference:")) {
4224  int temp;
4225  stuff_int(&temp);
4226  if (temp == 0) {
4227  sp->favor_current_facing = 0.0f;
4228  } else {
4229  CAP(temp, 1, 100);
4230  sp->favor_current_facing = 1.0f + (((float) (100 - temp)) / 10.0f);
4231  }
4232  }
4233 
4234  if (optional_string("$Target Priority:")) {
4235  SCP_vector <SCP_string> tgt_priorities;
4236  int num_strings = stuff_string_list(tgt_priorities);
4237  sp->num_target_priorities = 0;
4238 
4239  if (num_strings > 32)
4240  num_strings = 32;
4241 
4242  int num_groups = Ai_tp_list.size();
4243 
4244  for(i = 0; i < num_strings; i++) {
4245  for(j = 0; j < num_groups; j++) {
4246  if ( !stricmp(Ai_tp_list[j].name, tgt_priorities[i].c_str())) {
4247  sp->target_priority[i] = j;
4248  sp->num_target_priorities++;
4249  break;
4250  }
4251  }
4252  if (j == num_groups) {
4253  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);
4254  }
4255  }
4256  }
4257 
4258  if (optional_string("$Max Turrets per Bomb:")) {
4260  }
4261 
4262  if (optional_string("$Max Turrets per Target:")) {
4264  }
4265 
4266  if (optional_string("$ROF:")) {
4267 
4268  if (optional_string("+Use firingpoints")) {
4269  sp->turret_rof_scaler = 0;
4270  } else {
4271  if (optional_string("+Multiplier:")) {
4272  float tempf;
4273  stuff_float(&tempf);
4274 
4275  if (tempf < 0) {
4276  mprintf(("RoF multiplier clamped to 0 for subsystem '%s' in %s '%s'.\n", sp->subobj_name, info_type_name, sip->name));
4277  sp->turret_rof_scaler = 0;
4278  } else {
4279  sp->turret_rof_scaler = tempf;
4280  }
4281  } else {
4282  Warning(LOCATION, "RoF multiplier not set for subsystem\n'%s' in %s '%s'.", sp->subobj_name, info_type_name, sip->name);
4283  }
4284  }
4285  }
4286 
4287  if (optional_string("$Flags:")) {
4288  char flag_strings[Num_subsystem_flags][NAME_LENGTH];
4289  int num_strings = stuff_string_list(flag_strings, NUM_SUBSYSTEM_FLAGS);
4290 
4291  if (!optional_string("+noreplace")) {
4292  // clear flags since we might have a modular table
4293  // clear only those which are actually set in the flags
4294  sp->flags = 0;
4295  sp->flags2 = 0;
4296  }
4297 
4298  for (i = 0; i < num_strings; i++)
4299  {
4300 
4301  bool flag_found = false;
4302  // check various subsystem flags
4303  for (int idx = 0; idx < Num_subsystem_flags; idx++) {
4304  if ( !stricmp(Subsystem_flags[idx].name, flag_strings[i]) ) {
4305  flag_found = true;
4306 
4307  if (Subsystem_flags[idx].var == 0)
4308  sp->flags |= Subsystem_flags[idx].def;
4309  else if (Subsystem_flags[idx].var == 1)
4310  sp->flags2 |= Subsystem_flags[idx].def;
4311  }
4312  }
4313 
4314  if ( !flag_found )
4315  Warning(LOCATION, "Bogus string in subsystem flags: %s\n", flag_strings[i]);
4316  }
4317 
4318  //If we've set any subsystem as landable, set a ship-info flag as a shortcut for later
4319  if (sp->flags & MSS_FLAG_ALLOW_LANDING)
4320  sip->flags2 |= SIF2_ALLOW_LANDINGS;
4321  }
4322 
4323  if (turret_has_base_fov)
4325 
4326  if (optional_string("+non-targetable")) {
4327  Warning(LOCATION, "Grammar error in table file. Please change \"+non-targetable\" to \"+untargetable\".");
4329  }
4330 
4331  bool old_flags = false;
4332  if (optional_string("+untargetable")) {
4334  old_flags = true;
4335  }
4336 
4337  if (optional_string("+carry-no-damage")) {
4339  old_flags = true;
4340  }
4341 
4342  if (optional_string("+use-multiple-guns")) {
4344  old_flags = true;
4345  }
4346 
4347  if (optional_string("+fire-down-normals")) {
4349  old_flags = true;
4350  }
4351 
4353  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);
4355  }
4356 
4358  Warning(LOCATION, "\"autorepair if disabled\" flag used with \"don't autorepair if disabled\" flag on a subsystem on %s '%s'.\nWhichever flag would be default behavior anyway for this ship has been removed.\n", info_type_name, sip->name);
4361  } else {
4363  }
4364  }
4365 
4366  if (old_flags) {
4367  mprintf(("Use of deprecated subsystem syntax. Please use the $Flags: field for subsystem flags.\n\n" \
4368  "At least one of the following tags was used on %s '%s', subsystem %s:\n" \
4369  "\t+untargetable\n" \
4370  "\t+carry-no-damage\n" \
4371  "\t+use-multiple-guns\n" \
4372  "\t+fire-down-normals\n", info_type_name, sip->name, sp->subobj_name));
4373  }
4374 
4375  while(optional_string("$animation:"))
4376  {
4377  stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
4378  if(!stricmp(name_tmp, "triggered"))
4379  {
4380  queued_animation *current_trigger;
4381 
4382  sp->triggers = (queued_animation*)vm_realloc(sp->triggers, sizeof(queued_animation) * (sp->n_triggers + 1));
4383  Verify(sp->triggers != NULL);
4384 
4385  //add a new trigger
4386  current_trigger = &sp->triggers[sp->n_triggers];
4387  queued_animation_init(current_trigger);
4388  sp->n_triggers++;
4389 
4390  required_string("$type:");
4391  char atype[NAME_LENGTH];
4392  stuff_string(atype, F_NAME, NAME_LENGTH);
4393  current_trigger->type = model_anim_match_type(atype);
4394 
4395  if(optional_string("+sub_type:")){
4396  stuff_int(&current_trigger->subtype);
4397  }else{
4398  current_trigger->subtype = ANIMATION_SUBTYPE_ALL;
4399  }
4400 
4401  if(optional_string("+sub_name:")) {
4402  stuff_string(current_trigger->sub_name, F_NAME, NAME_LENGTH);
4403  } else {
4404  strcpy_s(current_trigger->sub_name, "<none>");
4405  }
4406 
4407 
4408  if(current_trigger->type == TRIGGER_TYPE_INITIAL){
4409  //the only thing initial animation type needs is the angle,
4410  //so to save space lets just make everything optional in this case
4411 
4412  if(optional_string("+delay:"))
4413  stuff_int(&current_trigger->start);
4414  else
4415  current_trigger->start = 0;
4416 
4417  if ( optional_string("+reverse_delay:") )
4418  stuff_int(&current_trigger->reverse_start);
4419  else
4420  current_trigger->reverse_start = 0;
4421 
4422  if(optional_string("+absolute_angle:")){
4423  current_trigger->absolute = true;
4424  stuff_vec3d(&current_trigger->angle );
4425 
4426  current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
4427  current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
4428  current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
4429  }else{
4430  current_trigger->absolute = false;
4431  if(!optional_string("+relative_angle:"))
4432  required_string("+relative_angle:");
4433 
4434  stuff_vec3d(&current_trigger->angle );
4435 
4436  current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
4437  current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
4438  current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
4439  }
4440 
4441  if(optional_string("+velocity:")){
4442  stuff_vec3d(&current_trigger->vel );
4443  current_trigger->vel.xyz.x = fl_radians(current_trigger->vel.xyz.x);
4444  current_trigger->vel.xyz.y = fl_radians(current_trigger->vel.xyz.y);
4445  current_trigger->vel.xyz.z = fl_radians(current_trigger->vel.xyz.z);
4446  }
4447 
4448  if(optional_string("+acceleration:")){
4449  stuff_vec3d(&current_trigger->accel );
4450  current_trigger->accel.xyz.x = fl_radians(current_trigger->accel.xyz.x);
4451  current_trigger->accel.xyz.y = fl_radians(current_trigger->accel.xyz.y);
4452  current_trigger->accel.xyz.z = fl_radians(current_trigger->accel.xyz.z);
4453  }
4454 
4455  if(optional_string("+time:"))
4456  stuff_int(&current_trigger->end );
4457  else
4458  current_trigger->end = 0;
4459  }else{
4460 
4461  if(optional_string("+delay:"))
4462  stuff_int(&current_trigger->start);
4463  else
4464  current_trigger->start = 0;
4465 
4466  if ( optional_string("+reverse_delay:") )
4467  stuff_int(&current_trigger->reverse_start);
4468  else
4469  current_trigger->reverse_start = -1; //have some code figure this out for us
4470 
4471  if(optional_string("+absolute_angle:")){
4472  current_trigger->absolute = true;
4473  stuff_vec3d(&current_trigger->angle );
4474 
4475  current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
4476  current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
4477  current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
4478  }else{
4479  current_trigger->absolute = false;
4480  required_string("+relative_angle:");
4481  stuff_vec3d(&current_trigger->angle );
4482 
4483  current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
4484  current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
4485  current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
4486  }
4487 
4488  required_string("+velocity:");
4489  stuff_vec3d(&current_trigger->vel );
4490  current_trigger->vel.xyz.x = fl_radians(current_trigger->vel.xyz.x);
4491  current_trigger->vel.xyz.y = fl_radians(current_trigger->vel.xyz.y);
4492  current_trigger->vel.xyz.z = fl_radians(current_trigger->vel.xyz.z);
4493 
4494  if (optional_string("+acceleration:")){
4495  stuff_vec3d(&current_trigger->accel );
4496  current_trigger->accel.xyz.x = fl_radians(current_trigger->accel.xyz.x);
4497  current_trigger->accel.xyz.y = fl_radians(current_trigger->accel.xyz.y);
4498  current_trigger->accel.xyz.z = fl_radians(current_trigger->accel.xyz.z);
4499  } else {
4500  current_trigger->accel.xyz.x = 0.0f;
4501  current_trigger->accel.xyz.y = 0.0f;
4502  current_trigger->accel.xyz.z = 0.0f;
4503  }
4504 
4505  if(optional_string("+time:"))
4506  stuff_int(&current_trigger->end );
4507  else
4508  current_trigger->end = 0;
4509 
4510  if(optional_string("$Sound:")){
4511  parse_sound("+Start:", &current_trigger->start_sound, sip->name);
4512 
4513  parse_sound("+Loop:", &current_trigger->loop_sound, sip->name);
4514 
4515  parse_sound("+End:", &current_trigger->end_sound, sip->name);
4516 
4517  required_string("+Radius:");
4518  stuff_float(&current_trigger->snd_rad );
4519  }else{
4520  current_trigger->start_sound = -1;
4521  current_trigger->loop_sound = -1;
4522  current_trigger->end_sound = -1;
4523  current_trigger->snd_rad = 0;
4524  }
4525  }
4526 
4527  //make sure that the amount of time it takes to accelerate up and down doesn't make it go farther than the angle
4528  queued_animation_correct(current_trigger);
4529  }
4530  else if(!stricmp(name_tmp, "linked"))
4531  {
4532  mprintf(("TODO: set up linked animation\n"));
4533  }
4534  }
4535 
4536  }
4537  break;
4538  case 2:
4539  cont_flag = 0;
4540  break;
4541  case -1: // Possible return value if -noparseerrors is used
4542  break;
4543  default:
4544  Assertion(false, "This should never happen.\n"); // Impossible return value from required_string_one_of.
4545  }
4546  }
4547 
4548  // must be > 0//no it doesn't :P -Bobboau
4549  // yes it does! - Goober5000
4550  // (we don't want a div-0 error)
4551  if (hull_percentage_of_hits <= 0.0f )
4552  {
4553  //Warning(LOCATION, "The subsystems defined for the %s can take more (or the same) combined damage than the ship itself. Adjust the tables so that the percentages add up to less than 100", sip->name);
4554  }
4555  // when done reading subsystems, malloc and copy the subsystem data to the ship info structure
4556  int orig_n_subsystems = sip->n_subsystems;
4557  if ( n_subsystems > 0 ) {
4558  if(sip->n_subsystems < 1) {
4559  sip->n_subsystems = n_subsystems;
4561  } else {
4562  sip->n_subsystems += n_subsystems;
4564  }
4565  Assert( sip->subsystems != NULL );
4566 
4567  for ( i = 0; i < n_subsystems; i++ ){
4568  sip->subsystems[orig_n_subsystems+i] = subsystems[i];
4569  }
4570  }
4571 
4573 
4574  return rtn; //0 for success
4575 }
4576 
4578 {
4579  for(int i = 0; i < Num_engine_wash_types; i++)
4580  {
4581  if(!stricmp(engine_wash_name, Engine_wash_info[i].name))
4582  {
4583  return &Engine_wash_info[i];
4584  }
4585  }
4586 
4587  //Didn't find anything.
4588  return NULL;
4589 }
4590 
4592 {
4593  char name_buf[NAME_LENGTH];
4594  bool nocreate = false;
4595  ship_type_info stp_buf, *stp = NULL;
4596 
4597  required_string("$Name:");
4598  stuff_string(name_buf, F_NAME, NAME_LENGTH);
4599 
4600  if(optional_string("+nocreate")) {
4601  nocreate = true;
4602  }
4603 
4604  int idx = ship_type_name_lookup(name_buf);
4605  if (idx >= 0)
4606  {
4607  stp = &Ship_types[idx];
4608  }
4609  else
4610  {
4611  stp = &stp_buf;
4612  strcpy_s(stp->name, name_buf);
4613  }
4614 
4615  char *ship_type = NULL;
4616  if (!stricmp(stp->name, "sentrygun")) {
4617  ship_type = "sentry gun";
4618  } else if (!stricmp(stp->name, "escapepod")) {
4619  ship_type = "escape pod";
4620  } else if (!stricmp(stp->name, "repair_rearm")) {
4621  ship_type = "support";
4622  } else if (!stricmp(stp->name, "supercap")) {
4623  ship_type = "super cap";
4624  } else if (!stricmp(stp->name, "knossos")) {
4625  ship_type = "knossos device";
4626  }
4627 
4628  if (ship_type != NULL) {
4629  Warning(LOCATION, "Bad ship type name in objecttypes.tbl\n\nUsed ship type is redirected to another ship type.\nReplace \"%s\" with \"%s\"\nin objecttypes.tbl to fix this.\n", stp->name, ship_type);
4630  }
4631 
4632  //Okay, now we should have the values to parse
4633  //But they aren't here!! :O
4634  //Now they are!! Whee fogging!!
4635 
4636  //AI turret targeting priority setup
4637  if (optional_string("$Target Priority Groups:") ) {
4638  SCP_vector <SCP_string> target_group_strings;
4639  int num_strings = stuff_string_list(target_group_strings);
4640  int num_groups = Ai_tp_list.size();
4641  int i, j;
4642  bool override_strings = false;
4643 
4644  if (optional_string("+Override")) {
4645  override_strings = true;
4646  }
4647 
4648  for(j = 0; j < num_strings; j++) {
4649  for(i = 0; i < num_groups; i++) {
4650  if ( !stricmp(target_group_strings[j].c_str(), Ai_tp_list[i].name) ) {
4651  //so now the string from the list above as well as the ai priority group name match
4652  //clear it if override has been set
4653  if (override_strings) {
4654  Ai_tp_list[i].ship_type.clear();
4655  override_strings = false;
4656  }
4657  //find the index number of the current ship info type
4658  Ai_tp_list[i].ship_type.push_back(ship_type_name_lookup(name_buf));
4659  break;
4660  }
4661  }
4662  if (i == num_groups) {
4663  Warning(LOCATION,"Unidentified priority group '%s' set for objecttype '%s'\n", target_group_strings[j].c_str(), stp->name);
4664  }
4665  }
4666  }
4667 
4668  if(optional_string("$Counts for Alone:")) {
4670  }
4671 
4672  if(optional_string("$Praise Destruction:")) {
4674  }
4675 
4676  if(optional_string("$On Hotkey list:")) {
4678  }
4679 
4680  if(optional_string("$Target as Threat:")) {
4682  }
4683