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 "globalincs/def_files.h"
16 #include "globalincs/alphacolors.h"
17 #include "ship/ship.h"
18 #include "object/object.h"
19 #include "weapon/weapon.h"
20 #include "radar/radar.h"
21 #include "render/3d.h"
22 #include "fireball/fireballs.h"
23 #include "hud/hud.h"
24 #include "io/timer.h"
25 #include "mission/missionlog.h"
26 #include "io/joy_ff.h"
27 #include "playerman/player.h"
28 #include "parse/parselo.h"
29 #include "freespace2/freespace.h"
30 #include "globalincs/linklist.h"
31 #include "hud/hudets.h"
32 #include "hud/hudshield.h"
33 #include "hud/hudmessage.h"
34 #include "ai/aigoals.h"
35 #include "gamesnd/gamesnd.h"
36 #include "gamesnd/eventmusic.h"
37 #include "ship/shipfx.h"
38 #include "gamesequence/gamesequence.h"
39 #include "object/objectshield.h"
40 #include "object/objectsnd.h"
41 #include "object/waypoint.h"
42 #include "cmeasure/cmeasure.h"
43 #include "ship/afterburner.h"
44 #include "weapon/shockwave.h"
45 #include "hud/hudsquadmsg.h"
46 #include "weapon/swarm.h"
47 #include "ship/subsysdamage.h"
48 #include "mission/missionmessage.h"
49 #include "lighting/lighting.h"
50 #include "particle/particle.h"
51 #include "ship/shiphit.h"
52 #include "asteroid/asteroid.h"
53 #include "hud/hudtargetbox.h"
54 #include "hud/hudwingmanstatus.h"
55 #include "jumpnode/jumpnode.h"
56 #include "missionui/redalert.h"
57 #include "weapon/corkscrew.h"
58 #include "weapon/emp.h"
59 #include "localization/localize.h"
60 #include "nebula/neb.h"
61 #include "ship/shipcontrails.h"
62 #include "weapon/beam.h"
63 #include "math/staticrand.h"
64 #include "math/fvi.h"
65 #include "missionui/missionshipchoice.h"
66 #include "hud/hudartillery.h"
67 #include "species_defs/species_defs.h"
68 #include "weapon/flak.h"								//phreak addded 11/05/02 for flak primaries
69 #include "mission/missioncampaign.h"
70 #include "radar/radarsetup.h"
71 #include "object/objectdock.h"
72 #include "object/deadobjectdock.h"
73 #include "iff_defs/iff_defs.h"
74 #include "network/multiutil.h"
75 #include "network/multimsgs.h"
76 #include "autopilot/autopilot.h"
77 #include "cmdline/cmdline.h"
78 #include "object/objcollide.h"
79 #include "parse/scripting.h"
80 #include "graphics/gropenglshader.h"
81 #include "model/model.h"
82 #include "mod_table/mod_table.h"
83 
84 
85 #define NUM_SHIP_SUBSYSTEM_SETS			20		// number of subobject sets to use (because of the fact that it's a linked list,
86 												//     we can't easily go fully dynamic)
87 
88 #define NUM_SHIP_SUBSYSTEMS_PER_SET		200 	// Reduced from 1000 to 400 by MK on 4/1/98.  DTP; bumped from 700 to 2100
89 												// Reduced to 200 by taylor on 3/13/07  --  it's managed in dynamically allocated sets now
90 												//    Highest I saw was 164 in sm2-03a which Sandeep says has a lot of ships.
91 												//    JAS: sm3-01 needs 460.   You cannot know this number until *all* ships
92 												//    have warped in.   So I put code in the paging code which knows all ships
93 												//    that will warp in.
94 
95 static int Num_ship_subsystems = 0;
96 static int Num_ship_subsystems_allocated = 0;
97 
98 static ship_subsys *Ship_subsystems[NUM_SHIP_SUBSYSTEM_SETS] = { NULL };
99 ship_subsys ship_subsys_free_list;
100 
101 extern bool splodeing;
102 extern float splode_level;
103 extern int splodeingtexture;
104 
105 extern int Cmdline_nohtl;
106 
107 extern void fs2netd_add_table_validation(const char *tblname);
108 
109 #define SHIP_REPAIR_SUBSYSTEM_RATE	0.01f
110 
111 int	Ai_render_debug_flag=0;
112 #ifndef NDEBUG
113 int	Ship_sphere_check = 0;
114 int	Ship_auto_repair = 1;		// flag to indicate auto-repair of subsystem should occur
115 extern void render_path_points(object *objp);
116 #endif
117 
118 int	Num_wings = 0;
119 int	Num_reinforcements = 0;
120 ship	Ships[MAX_SHIPS];
121 
122 ship	*Player_ship;
123 int		*Player_cockpit_textures;
124 SCP_vector<cockpit_display> Player_displays;
125 
126 wing	Wings[MAX_WINGS];
127 int	ships_inited = 0;
128 int armor_inited = 0;
129 
130 int	Starting_wings[MAX_STARTING_WINGS];  // wings player starts a mission with (-1 = none)
131 
132 // Goober5000
133 int Squadron_wings[MAX_SQUADRON_WINGS];
134 int TVT_wings[MAX_TVT_WINGS];
135 
136 // Goober5000
137 char Starting_wing_names[MAX_STARTING_WINGS][NAME_LENGTH];
138 char Squadron_wing_names[MAX_SQUADRON_WINGS][NAME_LENGTH];
139 char TVT_wing_names[MAX_TVT_WINGS][NAME_LENGTH];
140 
141 SCP_vector<engine_wash_info> Engine_wash_info;
142 engine_wash_info *get_engine_wash_pointer(char* engine_wash_name);
143 
144 void ship_reset_disabled_physics(object *objp, int ship_class);
145 
146 // information for ships which have exited the game
147 SCP_vector<exited_ship> Ships_exited;
148 
149 int	Num_engine_wash_types;
150 int	Num_ship_classes;
151 int	Num_ship_subobj_types;
152 int	Num_ship_subobjects;
153 int	Player_ship_class;	// needs to be player specific, move to player structure
154 
155 #define		SHIP_OBJ_USED	(1<<0)				// flag used in ship_obj struct
156 #define		MAX_SHIP_OBJS	MAX_SHIPS			// max number of ships tracked in ship list
157 ship_obj		Ship_objs[MAX_SHIP_OBJS];		// array used to store ship object indexes
158 ship_obj		Ship_obj_list;							// head of linked list of ship_obj structs
159 
160 ship_info		Ship_info[MAX_SHIP_CLASSES];
161 reinforcements	Reinforcements[MAX_REINFORCEMENTS];
162 SCP_vector<ship_info> Ship_templates;
163 
164 static char **tspecies_names = NULL;
165 
166 SCP_vector<ship_type_info> Ship_types;
167 
168 SCP_vector<ArmorType> Armor_types;
169 SCP_vector<DamageTypeStruct>	Damage_types;
170 
171 flag_def_list Armor_flags[] = {
172 	{ "ignore subsystem armor",		SAF_IGNORE_SS_ARMOR,	0 }
173 };
174 
175 const int Num_armor_flags = sizeof(Armor_flags)/sizeof(flag_def_list);
176 
177 flag_def_list Man_types[] = {
178 	{ "Bank right",		MT_BANK_RIGHT,	0 },
179 	{ "Bank left",		MT_BANK_LEFT,	0 },
180 	{ "Pitch up",		MT_PITCH_UP,	0 },
181 	{ "Pitch down",		MT_PITCH_DOWN,	0 },
182 	{ "Roll right",		MT_ROLL_RIGHT,	0 },
183 	{ "Roll left",		MT_ROLL_LEFT,	0 },
184 	{ "Slide right",	MT_SLIDE_RIGHT,	0 },
185 	{ "Slide left",		MT_SLIDE_LEFT,	0 },
186 	{ "Slide up",		MT_SLIDE_UP,	0 },
187 	{ "Slide down",		MT_SLIDE_DOWN,	0 },
188 	{ "Forward",		MT_FORWARD,		0 },
189 	{ "Reverse",		MT_REVERSE,		0 }
190 };
191 
192 const int Num_man_types = sizeof(Man_types)/sizeof(flag_def_list);
193 
194 // Goober5000 - I figured we should keep this separate
195 // from Comm_orders, considering how I redid it :p
196 // (and also because we may want to change either
197 // the order text or the flag text in the future)
198 flag_def_list Player_orders[] = {
199 	// common stuff
200 	{ "attack ship",		ATTACK_TARGET_ITEM,		0 },
201 	{ "disable ship",		DISABLE_TARGET_ITEM,	0 },
202 	{ "disarm ship",		DISARM_TARGET_ITEM,		0 },
203 	{ "disable subsys",		DISABLE_SUBSYSTEM_ITEM,	0 },
204 	{ "guard ship",			PROTECT_TARGET_ITEM,	0 },
205 	{ "ignore ship",		IGNORE_TARGET_ITEM,		0 },
206 	{ "form on wing",		FORMATION_ITEM,			0 },
207 	{ "cover me",			COVER_ME_ITEM,			0 },
208 	{ "attack any",			ENGAGE_ENEMY_ITEM,		0 },
209 
210 	// transports mostly
211 	{ "dock",				CAPTURE_TARGET_ITEM,	0 },
212 
213 	// support ships
214 	{ "rearm me",			REARM_REPAIR_ME_ITEM,	0 },
215 	{ "abort rearm",		ABORT_REARM_REPAIR_ITEM,	0 },
216 
217 	// all ships
218 	{ "depart",				DEPART_ITEM,			0 },
219 
220 	// extra stuff for support
221 	{ "stay near me",		STAY_NEAR_ME_ITEM,		0 },
222 	{ "stay near ship",		STAY_NEAR_TARGET_ITEM,	0 },
223 	{ "keep safe dist",		KEEP_SAFE_DIST_ITEM,	0 }
224 };
225 
226 const int Num_player_orders = sizeof(Player_orders)/sizeof(flag_def_list);
227 
228 // Use the last parameter here to tell the parser whether to stuff the flag into flags or flags2
229 flag_def_list Subsystem_flags[] = {
230 	{ "untargetable",			MSS_FLAG_UNTARGETABLE,		0 },
231 	{ "carry no damage",		MSS_FLAG_CARRY_NO_DAMAGE,	0 },
232 	{ "use multiple guns",		MSS_FLAG_USE_MULTIPLE_GUNS,	0 },
233 	{ "fire down normals",		MSS_FLAG_FIRE_ON_NORMAL,	0 },
234 	{ "check hull",				MSS_FLAG_TURRET_HULL_CHECK,	0 },
235 	{ "fixed firingpoints",		MSS_FLAG_TURRET_FIXED_FP,	0 },
236 	{ "salvo mode",				MSS_FLAG_TURRET_SALVO,		0 },
237 	{ "no subsystem targeting",	MSS_FLAG_NO_SS_TARGETING,	0 },
238 	{ "fire on target",			MSS_FLAG_FIRE_ON_TARGET,	0 },
239 	{ "reset when idle",		MSS_FLAG_TURRET_RESET_IDLE,	0 },
240 	{ "carry shockwave",		MSS_FLAG_CARRY_SHOCKWAVE,	0 },
241 	{ "allow landing",			MSS_FLAG_ALLOW_LANDING,		0 },
242 	{ "target requires fov",	MSS_FLAG_FOV_REQUIRED,		0 },
243 	{ "fov edge checks",		MSS_FLAG_FOV_EDGE_CHECK,	0 },
244 	{ "no replace",				MSS_FLAG_NO_REPLACE,		0 },
245 	{ "no live debris",			MSS_FLAG_NO_LIVE_DEBRIS,	0 },
246 	{ "ignore if dead",			MSS_FLAG_IGNORE_IF_DEAD,	0 },
247 	{ "allow vanishing",		MSS_FLAG_ALLOW_VANISHING,	0 },
248 	{ "damage as hull",			MSS_FLAG_DAMAGE_AS_HULL,	0 },
249 	{ "starts locked",          MSS_FLAG_TURRET_LOCKED,     0 },
250 	{ "no aggregate",			MSS_FLAG_NO_AGGREGATE,		0 },
251 	{ "wait for animation",     MSS_FLAG_TURRET_ANIM_WAIT,  0 },
252 	{ "play fire sound for player", MSS_FLAG2_PLAYER_TURRET_SOUND, 1},
253 	{ "only target if can fire",    MSS_FLAG2_TURRET_ONLY_TARGET_IF_CAN_FIRE, 1},
254 	{ "no disappear",			MSS_FLAG2_NO_DISAPPEAR, 1},
255 	{ "collide submodel",		MSS_FLAG2_COLLIDE_SUBMODEL, 1},
256 	{ "allow destroyed rotation",	MSS_FLAG2_DESTROYED_ROTATION, 1}
257 };
258 
259 const int Num_subsystem_flags = sizeof(Subsystem_flags)/sizeof(flag_def_list);
260 
261 
262 // NOTE: a var of:
263 //         "0"    means that it's a SIF_* flag
264 //         "1"    means that it's a SIF2_* flag
265 //         "255"  means that the option is obsolete and a warning should be generated
266 flag_def_list Ship_flags[] = {
267 	{ "no_collide",					SIF_NO_COLLIDE,				0 },
268 	{ "player_ship",				SIF_PLAYER_SHIP,			0 },
269 	{ "default_player_ship",		SIF_DEFAULT_PLAYER_SHIP,	0 },
270 	{ "repair_rearm",				SIF_SUPPORT,				0 },
271 	{ "cargo",						SIF_CARGO,					0 },
272 	{ "fighter",					SIF_FIGHTER,				0 },
273 	{ "bomber",						SIF_BOMBER,					0 },
274 	{ "transport",					SIF_TRANSPORT,				0 },
275 	{ "freighter",					SIF_FREIGHTER,				0 },
276 	{ "capital",					SIF_CAPITAL,				0 },
277 	{ "supercap",					SIF_SUPERCAP,				0 },
278 	{ "drydock",					SIF_DRYDOCK,				0 },
279 	{ "cruiser",					SIF_CRUISER,				0 },
280 	{ "navbuoy",					SIF_NAVBUOY,				0 },
281 	{ "sentrygun",					SIF_SENTRYGUN,				0 },
282 	{ "escapepod",					SIF_ESCAPEPOD,				0 },
283 	{ "stealth",					SIF_STEALTH,				0 },
284 	{ "no type",					SIF_NO_SHIP_TYPE,			0 },
285 	{ "ship copy",					SIF_SHIP_COPY,				0 },
286 	{ "in tech database",			SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M,	0 },
287 	{ "in tech database multi",		SIF_IN_TECH_DATABASE_M,		0 },
288 	{ "dont collide invisible",		SIF_SHIP_CLASS_DONT_COLLIDE_INVIS,	0 },
289 	{ "big damage",					SIF_BIG_DAMAGE,				0 },
290 	{ "corvette",					SIF_CORVETTE,				0 },
291 	{ "gas miner",					SIF_GAS_MINER,				0 },
292 	{ "awacs",						SIF_AWACS,					0 },
293 	{ "knossos",					SIF_KNOSSOS_DEVICE,			0 },
294 	{ "no_fred",					SIF_NO_FRED,				0 },
295 	{ "flash",						SIF2_FLASH,					1 },
296 	{ "surface shields",			SIF2_SURFACE_SHIELDS,		1 },
297 	{ "show ship",					SIF2_SHOW_SHIP_MODEL,		1 },
298 	{ "generate icon",				SIF2_GENERATE_HUD_ICON,		1 },
299 	{ "no weapon damage scaling",	SIF2_DISABLE_WEAPON_DAMAGE_SCALING,	1 },
300 	{ "gun convergence",			SIF2_GUN_CONVERGENCE,		1 },
301 	{ "no thruster geometry noise", SIF2_NO_THRUSTER_GEO_NOISE,	1 },
302 	{ "intrinsic no shields",		SIF2_INTRINSIC_NO_SHIELDS,	1 },
303 	{ "dynamic primary linking",    SIF2_DYN_PRIMARY_LINKING,	1 },
304 	{ "no primary linking",			SIF2_NO_PRIMARY_LINKING,	1 },
305 	{ "no pain flash",				SIF2_NO_PAIN_FLASH,			1 },
306 	{ "no ets",						SIF2_NO_ETS,				1 },
307 	{ "no lighting",				SIF2_NO_LIGHTING,			1 },
308 	{ "auto spread shields",		SIF2_AUTO_SPREAD_SHIELDS,	1 },
309 	{ "model point shields",		SIF2_MODEL_POINT_SHIELDS,	1 },
310 
311 	// to keep things clean, obsolete options go last
312 	{ "ballistic primaries",		-1,		255 }
313 };
314 
315 const int Num_ship_flags = sizeof(Ship_flags) / sizeof(flag_def_list);
316 
317 /*
318 ++Here be dragons.. err.. begins the section for the ai targeting revision
319 ++  First flag_def_list (& its size) for object types (ship/asteroid/weapon)
320 ++  List of reasonable object flags (from object.h)
321 ++  List of potentially useful ship class flags
322 ++  List of potentially useful weapon class flags
323 */
324 flag_def_list ai_tgt_objects[] = {
325 	{ "ship",		OBJ_SHIP,		0 },
326 	{ "asteroid",	OBJ_ASTEROID,	0 },
327 	{ "weapon",		OBJ_WEAPON,		0 }
328 };
329 
330 const int num_ai_tgt_objects = sizeof(ai_tgt_objects) / sizeof(flag_def_list);
331 
332 flag_def_list ai_tgt_obj_flags[] = {
333 	{ "no shields",			OF_NO_SHIELDS,			0 },
334 	{ "targetable as bomb",	OF_TARGETABLE_AS_BOMB,	0 }
335 };
336 
337 const int num_ai_tgt_obj_flags = sizeof(ai_tgt_obj_flags) / sizeof(flag_def_list);
338 
339 flag_def_list ai_tgt_ship_flags[] = {
340 	{ "afterburners",	SIF_AFTERBURNER,	0 },
341 	{ "big damage",		SIF_BIG_DAMAGE,		0 },
342 	{ "has awacs",		SIF_HAS_AWACS,		0 }
343 };
344 
345 const int num_ai_tgt_ship_flags = sizeof(ai_tgt_ship_flags) / sizeof(flag_def_list);
346 
347 flag_def_list ai_tgt_weapon_flags[] = {
348 	{ "bomb",				WIF_BOMB,				0 },
349 	{ "huge damage",		WIF_HUGE,				0 },
350 	{ "supercap damage",	WIF_SUPERCAP,			0 },
351 	{ "bomber+",			WIF_BOMBER_PLUS,		0 },
352 	{ "electronics",		WIF_ELECTRONICS,		0 },
353 	{ "puncture",			WIF_PUNCTURE,			0 },
354 	{ "emp",				WIF_EMP,				0 },
355 	{ "heat seeking",		WIF_HOMING_HEAT,		0 },
356 	{ "aspect seeking",		WIF_HOMING_ASPECT,		0 },
357 	{ "engine seeking",		WIF_HOMING_JAVELIN,		0 },
358 	{ "pierce shields",		WIF2_PIERCE_SHIELDS,	1 },
359 	{ "local ssm",			WIF2_LOCAL_SSM,			1 },
360 	{ "capital+",			WIF2_CAPITAL_PLUS,		1 }
361 };
362 
363 //	Constant for flag,				Name of flag,				In flags or flags2
364 //  When adding new flags remember to bump MAX_SHIP_FLAG_NAMES in ship.h
365 ship_flag_name Ship_flag_names[] = {
366 	{SF_VAPORIZE,					"vaporize",						1,	},
367 	{SF_WARP_BROKEN,				"break-warp",					1,	},
368 	{SF_WARP_NEVER,					"never-warp",					1,	},
369 	{SF_SCANNABLE,					"scannable",					1,	},
370 	{SF_CARGO_REVEALED,				"cargo-known",					1,	},
371 	{SF_HIDDEN_FROM_SENSORS,		"hidden-from-sensors",			1,	},
372 	{SF2_STEALTH,					"stealth",						2,	},
373 	{SF2_FRIENDLY_STEALTH_INVIS,	"friendly-stealth-invisible",	2,	},
374 	{SF2_HIDE_SHIP_NAME,			"hide-ship-name",				2,	},
375 	{SF2_AFTERBURNER_LOCKED,		"afterburners-locked",			2,	},
376 	{SF2_PRIMARIES_LOCKED,			"primaries-locked",				2,	},
377 	{SF2_SECONDARIES_LOCKED,		"secondaries-locked",			2,	},
378 	{SF2_NO_SUBSPACE_DRIVE,			"no-subspace-drive",			2,	},
379 	{SF2_DONT_COLLIDE_INVIS,		"don't-collide-invisible",		2,	},
380 	{SF2_NO_ETS,					"no-ets",						2,	},
381 	{SF2_TOGGLE_SUBSYSTEM_SCANNING,	"toggle-subsystem-scanning",	2,	},
382 };
383 
384 const int num_ai_tgt_weapon_flags = sizeof(ai_tgt_weapon_flags) / sizeof(flag_def_list);
385 
386 SCP_vector <ai_target_priority> Ai_tp_list;
387 
388 static int Laser_energy_out_snd_timer;	// timer so we play out of laser sound effect periodically
389 static int Missile_out_snd_timer;	// timer so we play out of laser sound effect periodically
390 
391 SCP_vector<ship_counts>	Ship_type_counts;
392 
393 // I don't want to do an AI cargo check every frame, so I made a global timer to limit check to
394 // every SHIP_CARGO_CHECK_INTERVAL ms.  Didn't want to make a timer in each ship struct.  Ensure
395 // inited to 1 at mission start.
396 static int Ship_cargo_check_timer;
397 
398 static int Thrust_anim_inited = 0;
399 
400 bool warning_too_many_ship_classes = false;
401 
402 int ship_get_subobj_model_num(ship_info* sip, char* subobj_name);
403 
404 SCP_vector<ship_effect> Ship_effects;
405 
406 /**
407  * Set the ship_obj struct fields to default values
408  */
ship_obj_list_reset_slot(int index)409 void ship_obj_list_reset_slot(int index)
410 {
411 	Ship_objs[index].flags = 0;
412 	Ship_objs[index].next = NULL;
413 	Ship_objs[index].prev = (ship_obj*)-1;
414 }
415 
416 /**
417  * If the given ship is in my squadron wings
418  */
ship_in_my_squadron(ship * shipp)419 int ship_in_my_squadron(ship *shipp)
420 {
421 	int i;
422 
423 	for (i=0; i<MAX_STARTING_WINGS; i++)
424 	{
425 		if (shipp->wingnum == Starting_wings[i])
426 			return 1;
427 	}
428 
429 	for (i=0; i<MAX_TVT_WINGS; i++)
430 	{
431 		if (shipp->wingnum == TVT_wings[i])
432 			return 1;
433 	}
434 
435 	// not in
436 	return 0;
437 }
438 
439 /**
440  * Initialise Ship_obj_list
441  */
ship_obj_list_init()442 void ship_obj_list_init()
443 {
444 	int i;
445 
446 	list_init(&Ship_obj_list);
447 	for ( i = 0; i < MAX_SHIP_OBJS; i++ ) {
448 		ship_obj_list_reset_slot(i);
449 	}
450 }
451 
452 /**
453  * Function to add a node to the Ship_obj_list.  Only
454  * called from ::ship_create()
455  */
ship_obj_list_add(int objnum)456 int ship_obj_list_add(int objnum)
457 {
458 	int i;
459 
460 	for ( i = 0; i < MAX_SHIP_OBJS; i++ ) {
461 		if ( !(Ship_objs[i].flags & SHIP_OBJ_USED) )
462 			break;
463 	}
464 	if ( i == MAX_SHIP_OBJS ) {
465 		Error(LOCATION, "Fatal Error: Ran out of ship object nodes\n");
466 		return -1;
467 	}
468 
469 	Ship_objs[i].flags = 0;
470 	Ship_objs[i].objnum = objnum;
471 	list_append(&Ship_obj_list, &Ship_objs[i]);
472 	Ship_objs[i].flags |= SHIP_OBJ_USED;
473 
474 	return i;
475 }
476 
477 /**
478  * Function to remove a node from the Ship_obj_list.  Only
479  * called from ::ship_delete()
480  */
ship_obj_list_remove(int index)481 void ship_obj_list_remove(int index)
482 {
483 	Assert(index >= 0 && index < MAX_SHIP_OBJS);
484 	list_remove( Ship_obj_list, &Ship_objs[index]);
485 	ship_obj_list_reset_slot(index);
486 }
487 
488 /**
489  * Called from the save/restore code to re-create the Ship_obj_list
490  */
ship_obj_list_rebuild()491 void ship_obj_list_rebuild()
492 {
493 	object *objp;
494 
495 	ship_obj_list_init();
496 
497 	for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
498 		if ( objp->type == OBJ_SHIP ) {
499 			Ships[objp->instance].ship_list_index = ship_obj_list_add(OBJ_INDEX(objp));
500 		}
501 	}
502 }
503 
get_ship_obj_ptr_from_index(int index)504 ship_obj *get_ship_obj_ptr_from_index(int index)
505 {
506 	Assert(index >= 0 && index < MAX_SHIP_OBJS);
507 	return &Ship_objs[index];
508 }
509 
510 /**
511  * Return number of ships in the game.
512  */
ship_get_num_ships()513 int ship_get_num_ships()
514 {
515 	int count;
516 	ship_obj *so;
517 
518 	count = 0;
519 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )
520 		count++;
521 
522 	return count;
523 }
524 
engine_wash_info_init(engine_wash_info * ewi)525 void engine_wash_info_init(engine_wash_info *ewi)
526 {
527 	ewi->name[0] = '\0';
528 	ewi->angle = PI / 10.0f;
529 	ewi->radius_mult = 1.0f;
530 	ewi->length = 500.0f;
531 	ewi->intensity = 1.0f;
532 }
533 
534 /**
535  * Parse an engine wash info record
536  */
parse_engine_wash(bool replace)537 void parse_engine_wash(bool replace)
538 {
539 	engine_wash_info ewt;
540 	engine_wash_info_init(&ewt);
541 
542 	engine_wash_info *ewp;
543 	bool create_if_not_found  = true;
544 
545 	// name of engine wash info
546 	required_string("$Name:");
547 	stuff_string(ewt.name, F_NAME, NAME_LENGTH);
548 
549 	if(optional_string("+nocreate")) {
550 		if(!replace) {
551 			Warning(LOCATION, "+nocreate flag used for engine wash in non-modular table");
552 		}
553 		create_if_not_found = false;
554 	}
555 
556 	//Does this engine wash exist already?
557 	//If so, load this new info into it
558 	//Otherwise, increment Num_engine_wash_types
559 	ewp = get_engine_wash_pointer(ewt.name);
560 	if(ewp != NULL)
561 	{
562 		if(replace)
563 		{
564 			nprintf(("Warning", "More than one version of engine wash %s exists; using newer version.", ewt.name));
565 		}
566 		else
567 		{
568 			Error(LOCATION, "Error:  Engine wash %s already exists.  All engine wash names must be unique.", ewt.name);
569 		}
570 	}
571 	else
572 	{
573 		//Don't create engine wash if it has +nocreate and is in a modular table.
574 		if(!create_if_not_found && replace)
575 		{
576 			if ( !skip_to_start_of_string_either("$Name:", "#End")) {
577 				Int3();
578 			}
579 			return;
580 		}
581 		Engine_wash_info.push_back(ewt);
582 		ewp = &Engine_wash_info[Num_engine_wash_types++];
583 	}
584 
585 
586 	// half angle of cone of wash from thruster
587 	if(optional_string("$Angle:"))
588 	{
589 		stuff_float(&ewp->angle);
590 		ewp->angle *= (PI / 180.0f);
591 	}
592 
593 	// radius multiplier for hemisphere around thruster pt
594 	if(optional_string("$Radius Mult:")) {
595 		stuff_float(&ewp->radius_mult);
596 	}
597 
598 	// length of cone
599 	if(optional_string("$Length:")) {
600 		stuff_float(&ewp->length);
601 	}
602 
603 	// intensity inside hemisphere (or at 0 distance from frustated cone)
604 	if(optional_string("$Intensity:")) {
605 		stuff_float(&ewp->intensity);
606 	}
607 }
608 
609 char *Warp_types[] = {
610 	"Default",
611 	"Knossos",
612 	"Babylon5",
613 	"Galactica",
614 	"Homeworld",
615 	"Hyperspace",
616 };
617 
618 int Num_warp_types = sizeof(Warp_types)/sizeof(char*);
619 
warptype_match(char * p)620 int warptype_match(char *p)
621 {
622 	int i;
623 	for(i = 0; i < Num_warp_types; i++)
624 	{
625 		if(!stricmp(Warp_types[i], p))
626 			return i;
627 	}
628 
629 	return -1;
630 }
631 
632 char *Lightning_types[] = {
633 	"None",
634 	"Default",
635 };
636 
637 int Num_lightning_types = sizeof(Lightning_types)/sizeof(char*);
638 
lightningtype_match(char * p)639 int lightningtype_match(char *p)
640 {
641 	int i;
642 	for(i = 0; i < Num_lightning_types; i++)
643 	{
644 		if(!stricmp(Lightning_types[i], p))
645 			return i;
646 	}
647 
648 	return -1;
649 }
650 
651 // Kazan -- Volition had this set to 1500, Set it to 4K for WC Saga
652 #define SHIP_MULTITEXT_LENGTH 4096
653 #define DEFAULT_DELTA_BANK_CONST	0.5f
654 
655 
656 /**
657  * Writes default info to a ship entry
658  *
659  * Result: Perfectly valid ship_info entry, just with no name
660  * Called from parse_ship so that modular tables are cumulative,
661  * rather than simply replacing the previous entry
662  */
init_ship_entry(ship_info * sip)663 void init_ship_entry(ship_info *sip)
664 {
665 	int i,j;
666 
667 	sip->name[0] = '\0';
668 	sip->alt_name[0] = '\0';
669 	sprintf(sip->short_name, "ShipClass%d", (int) (sip - Ship_info));
670 	sip->species = 0;
671 	sip->class_type = -1;
672 
673 	sip->type_str = sip->maneuverability_str = sip->armor_str = sip->manufacturer_str = NULL;
674 	sip->desc = sip->tech_desc = NULL;
675 	sip->tech_title[0] = 0;
676 
677 	sip->ship_length = NULL;
678 	sip->gun_mounts = NULL;
679 	sip->missile_banks = NULL;
680 
681 	sip->cockpit_pof_file[0] = '\0';
682 	vm_vec_zero(&sip->cockpit_offset);
683 	sip->pof_file[0] = '\0';
684 	sip->pof_file_hud[0] = '\0';
685 	sip->num_detail_levels = 1;
686 	sip->detail_distance[0] = 0;
687 	sip->cockpit_model_num = -1;
688 	sip->model_num = -1;
689 	sip->model_num_hud = -1;
690 	sip->hud_target_lod = -1;
691 
692 	sip->density = 1.0f;
693 	sip->damp = 0.0f;
694 	sip->rotdamp = 0.0f;
695 	sip->delta_bank_const = DEFAULT_DELTA_BANK_CONST;
696 	vm_vec_zero(&sip->max_vel);
697 	vm_vec_zero(&sip->max_rotvel);
698 	vm_vec_zero(&sip->rotation_time);
699 	sip->srotation_time = 0.0f;
700 	sip->max_rear_vel = 0.0f;
701 	sip->forward_accel = 0.0f;
702 	sip->forward_decel = 0.0f;
703 	sip->slide_accel = 0.0f;
704 	sip->slide_decel = 0.0f;
705 
706 	sip->warpin_anim[0] = '\0';
707 	sip->warpin_radius = 0.0f;
708 	sip->warpin_snd_start = -1;
709 	sip->warpin_snd_end = -1;
710 	sip->warpin_speed = 0.0f;
711 	sip->warpin_time = 0;
712 	sip->warpin_decel_exp = 1;
713 	sip->warpin_type = WT_DEFAULT;
714 
715 	sip->warpout_anim[0] = '\0';
716 	sip->warpout_radius = 0.0f;
717 	sip->warpout_snd_start = -1;
718 	sip->warpout_snd_end = -1;
719 	sip->warpout_engage_time = -1;
720 	sip->warpout_speed = 0.0f;
721 	sip->warpout_time = 0;
722 	sip->warpout_accel_exp = 1;
723 	sip->warpout_type = WT_DEFAULT;
724 
725 	sip->warpout_player_speed = 0.0f;
726 
727 	sip->flags = SIF_DEFAULT_VALUE;
728 	sip->flags2 = SIF2_DEFAULT_VALUE;
729 	sip->ai_class = 0;
730 	sip->max_speed = 0.0f;
731 	sip->min_speed = 0.0f;
732 	sip->max_accel = 0.0f;
733 
734 	sip->collision_damage_type_idx = -1;
735 	// Retail default collision physics and default landing parameters
736 	memset(&sip->collision_physics, 0, sizeof(ship_collision_physics));
737 	sip->collision_physics.both_small_bounce = 5.0;
738 	sip->collision_physics.bounce = 5.0;
739 	sip->collision_physics.friction = COLLISION_FRICTION_FACTOR;
740 	sip->collision_physics.rotation_factor = COLLISION_ROTATION_FACTOR;
741 	sip->collision_physics.reorient_mult = 1.0f;
742 	sip->collision_physics.landing_sound_idx = -1;
743 
744 	shockwave_create_info_init(&sip->shockwave);
745 	sip->explosion_propagates = 0;
746 	sip->big_exp_visual_rad = -1.0f;
747 	sip->prop_exp_rad_mult = 1.0f;
748 	sip->death_roll_r_mult = 1.0f;
749 	sip->death_fx_r_mult = 1.0f;
750 	sip->death_roll_time_mult = 1.0f;
751 	sip->death_roll_base_time = 3000;
752 	sip->death_fx_count = 6;
753 	sip->shockwave_count = 1;
754 	sip->explosion_bitmap_anims.clear();
755 	sip->vaporize_chance = 0;
756 
757 	// default values from shipfx.cpp
758 	sip->impact_spew.n_high = 30;
759 	sip->impact_spew.n_low = 25;
760 	sip->impact_spew.max_rad = 0.5f;
761 	sip->impact_spew.min_rad = 0.2f;
762 	sip->impact_spew.max_life = 0.55f;
763 	sip->impact_spew.min_life = 0.05f;
764 	sip->impact_spew.max_vel = 12.0f;
765 	sip->impact_spew.min_vel = 2.0f;
766 	sip->impact_spew.variance = 1.0f;
767 
768 	// default values from shipfx.cpp
769 	sip->damage_spew.n_high = 1;						// 1 is used here to trigger retail behaviour
770 	sip->damage_spew.n_low = 0;
771 	sip->damage_spew.max_rad = 1.3f;
772 	sip->damage_spew.min_rad = 0.7f;
773 	sip->damage_spew.max_life = 0.0f;
774 	sip->damage_spew.min_life = 0.0f;
775 	sip->damage_spew.max_vel = 12.0f;
776 	sip->damage_spew.min_vel = 3.0f;
777 	sip->damage_spew.variance = 0.0f;
778 
779 	sip->split_particles.n_high = 80;
780 	sip->split_particles.n_low = 40;
781 	sip->split_particles.max_rad = 0.0f;
782 	sip->split_particles.min_rad = 0.0f;
783 	sip->split_particles.max_life = 0.0f;
784 	sip->split_particles.min_life = 0.0f;
785 	sip->split_particles.max_vel = 0.0f;
786 	sip->split_particles.min_vel = 0.0f;
787 	sip->split_particles.variance = 2.0f;
788 
789 	sip->knossos_end_particles.n_high = 30;
790 	sip->knossos_end_particles.n_low = 15;
791 	sip->knossos_end_particles.max_rad = 100.0f;
792 	sip->knossos_end_particles.min_rad = 30.0f;
793 	sip->knossos_end_particles.max_life = 12.0f;
794 	sip->knossos_end_particles.min_life = 2.0f;
795 	sip->knossos_end_particles.max_vel = 350.0f;
796 	sip->knossos_end_particles.min_vel = 50.0f;
797 	sip->knossos_end_particles.variance = 2.0f;
798 
799 	sip->regular_end_particles.n_high = 100;
800 	sip->regular_end_particles.n_low = 50;
801 	sip->regular_end_particles.max_rad = 1.5f;
802 	sip->regular_end_particles.min_rad = 0.1f;
803 	sip->regular_end_particles.max_life = 4.0f;
804 	sip->regular_end_particles.min_life = 0.5f;
805 	sip->regular_end_particles.max_vel = 20.0f;
806 	sip->regular_end_particles.min_vel = 0.0f;
807 	sip->regular_end_particles.variance = 2.0f;
808 
809 	sip->debris_min_lifetime = -1.0f;
810 	sip->debris_max_lifetime = -1.0f;
811 	sip->debris_min_speed = -1.0f;
812 	sip->debris_max_speed = -1.0f;
813 	sip->debris_min_rotspeed = -1.0f;
814 	sip->debris_max_rotspeed = -1.0f;
815 	sip->debris_damage_type_idx = -1;
816 	sip->debris_min_hitpoints = -1.0f;
817 	sip->debris_max_hitpoints = -1.0f;
818 	sip->debris_damage_mult = 1.0f;
819 	sip->debris_arc_percent = 0.5f;
820 
821 	sip->n_subsystems = 0;
822 	sip->subsystems = NULL;
823 
824 	sip->power_output = 0.0f;
825 	sip->max_overclocked_speed = 0.0f;
826 	sip->max_weapon_reserve = 0.0f;
827 	sip->max_shield_regen_per_second = 0.0f;
828 	sip->max_weapon_regen_per_second = 0.0f;
829 
830 	vm_vec_zero(&sip->afterburner_max_vel);
831 	sip->afterburner_forward_accel = 0.0f;
832 	sip->afterburner_fuel_capacity = 0.0f;
833 	sip->afterburner_burn_rate = 0.0f;
834 	sip->afterburner_recover_rate = 0.0f;
835 	sip->afterburner_max_reverse_vel = 0.0f;
836 	sip->afterburner_reverse_accel = 0.0f;
837 
838 	sip->cmeasure_type = Default_cmeasure_index;
839 	sip->cmeasure_max = 0;
840 
841 	sip->num_primary_banks = 0;
842 	for ( i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++ )
843 	{
844 		sip->primary_bank_weapons[i] = -1;
845 		sip->draw_primary_models[i] = false;
846 		sip->primary_bank_ammo_capacity[i] = 0;
847 	}
848 
849 	sip->num_secondary_banks = 0;
850 	for ( i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++ )
851 	{
852 		sip->secondary_bank_weapons[i] = -1;
853 		sip->draw_secondary_models[i] = false;
854 		sip->secondary_bank_ammo_capacity[i] = 0;
855 	}
856 
857 	sip->weapon_model_draw_distance = 200.0f;
858 
859 	sip->max_hull_strength = 100.0f;
860 	sip->max_shield_strength = 0.0f;
861 
862 	sip->auto_shield_spread = 0.0f;
863 	sip->auto_shield_spread_bypass = false;
864 	sip->auto_shield_spread_from_lod = -1;
865 
866 	for (i = 0; i < 4; i++)
867 	{
868 		sip->shield_point_augment_ctrls[i] = -1;
869 	}
870 
871 	sip->hull_repair_rate = 0.0f;
872 	//-2 represents not set, in which case the default is used for the ship (if it is small)
873 	sip->subsys_repair_rate = -2.0f;
874 
875 	sip->sup_hull_repair_rate = 0.15f;
876 	sip->sup_shield_repair_rate = 0.20f;
877 	sip->sup_subsys_repair_rate = 0.15f;
878 
879 	vm_vec_zero(&sip->closeup_pos);
880 	sip->closeup_zoom = 0.5f;
881 
882 	memset(sip->allowed_weapons, 0, sizeof(int) * MAX_WEAPON_TYPES);
883 
884 	memset(sip->restricted_loadout_flag, 0, sizeof(int) * MAX_SHIP_WEAPONS);
885 	memset(sip->allowed_bank_restricted_weapons, 0, sizeof(int) * MAX_SHIP_WEAPONS * MAX_WEAPON_TYPES);
886 
887 	sip->shield_icon_index = 255;		// stored as ubyte
888 	sip->icon_filename[0] = '\0';
889 	sip->anim_filename[0] = '\0';
890 	sip->overhead_filename[0] = '\0';
891 
892 	sip->selection_effect = Default_ship_select_effect;
893 
894 	sip->bii_index_ship = -1;
895 	sip->bii_index_ship_with_cargo = -1;
896 	sip->bii_index_wing = -1;
897 	sip->bii_index_wing_with_cargo = -1;
898 
899 	sip->score = 0;
900 
901 	sip->scan_time = 2000;
902 
903 	memset(&sip->ct_info, 0, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
904 	sip->ct_count = 0;
905 
906 	sip->num_nondark_colors = 0;
907 	memset(sip->nondark_colors, 0, sizeof(ubyte) * MAX_NONDARK_COLORS * 3);
908 
909 	sip->shield_color[0] = 255;
910 	sip->shield_color[1] = 255;
911 	sip->shield_color[2] = 255;
912 
913 	// Team colors
914 	sip->uses_team_colors = false;
915 	sip->default_team_name = "";
916 
917 	generic_bitmap_init(&sip->afterburner_trail, NULL);
918 	sip->afterburner_trail_width_factor = 1.0f;
919 	sip->afterburner_trail_alpha_factor = 1.0f;
920 	sip->afterburner_trail_life = 5.0f;
921 	sip->afterburner_trail_faded_out_sections = 0;
922 
923 	sip->normal_thruster_particles.clear();
924 	sip->afterburner_thruster_particles.clear();
925 
926 	// Bobboau's thruster stuff
927 	generic_anim_init( &sip->thruster_flame_info.normal );
928 	generic_anim_init( &sip->thruster_flame_info.afterburn );
929 	generic_anim_init( &sip->thruster_glow_info.normal );
930 	generic_anim_init( &sip->thruster_glow_info.afterburn );
931 	generic_bitmap_init( &sip->thruster_secondary_glow_info.normal );
932 	generic_bitmap_init( &sip->thruster_secondary_glow_info.afterburn );
933 	generic_bitmap_init( &sip->thruster_tertiary_glow_info.normal );
934 	generic_bitmap_init( &sip->thruster_tertiary_glow_info.afterburn );
935 	generic_bitmap_init( &sip->thruster_distortion_info.normal );
936 	generic_bitmap_init( &sip->thruster_distortion_info.afterburn );
937 
938 	// Bobboau's thruster stuff
939 	sip->thruster01_glow_rad_factor = 1.0f;
940 	sip->thruster02_glow_rad_factor = 1.0f;
941 	sip->thruster03_glow_rad_factor = 1.0f;
942 	sip->thruster02_glow_len_factor = 1.0f;
943 	sip->thruster_dist_rad_factor = 2.0f;
944 	sip->thruster_dist_len_factor = 2.0f;
945 
946 	sip->draw_distortion = true;
947 
948 	sip->splodeing_texture = -1;
949 	strcpy_s(sip->splodeing_texture_name, "boom");
950 
951 	sip->armor_type_idx = -1;
952 	sip->shield_armor_type_idx = -1;
953 
954 	sip->can_glide = false;
955 	sip->glide_cap = 0.0f;
956 	sip->glide_dynamic_cap = false;
957 	sip->glide_accel_mult = 0.0f;
958 	sip->use_newtonian_damp = false;
959 	sip->newtonian_damp_override = false;
960 
961 	sip->autoaim_fov = 0.0f;
962 
963 	sip->topdown_offset_def = false;
964 	vm_vec_zero(&sip->topdown_offset);
965 
966 	sip->engine_snd = -1;
967 	sip->glide_start_snd = -1;
968 	sip->glide_end_snd = -1;
969 
970 	sip->ship_sounds.clear();
971 
972 	sip->num_maneuvering = 0;
973 	memset(sip->maneuvering, 0, MAX_MAN_THRUSTERS * sizeof(man_thruster));
974 	for (i = 0; i < MAX_MAN_THRUSTERS; i++)
975 	{
976 		sip->maneuvering[i].start_snd = -1;
977 		sip->maneuvering[i].loop_snd = -1;
978 		sip->maneuvering[i].stop_snd = -1;
979 		sip->maneuvering[i].tex_id = -1;
980 	}
981 
982 	sip->radar_image_2d_idx = -1;
983 	sip->radar_color_image_2d_idx = -1;
984 	sip->radar_image_size = -1;
985 	sip->radar_projection_size_mult = 1.0f;
986 
987 	for (i=0;i<MAX_IFFS;i++)
988 	{
989 		for (j=0;j<MAX_IFFS;j++)
990 			sip->ship_iff_info[i][j] = -1;
991 	}
992 
993 	sip->aiming_flags = 0;
994 	sip->minimum_convergence_distance = 0.0f;
995 	sip->convergence_distance = 100.0f;
996 	vm_vec_zero(&sip->convergence_offset);
997 
998 	sip->emp_resistance_mod = 0.0f;
999 
1000 	sip->piercing_damage_draw_limit = 0.10f;
1001 
1002 	sip->damage_lightning_type = SLT_DEFAULT;
1003 
1004 	sip->hud_gauges.clear();
1005 	sip->hud_enabled = false;
1006 	sip->hud_retail = false;
1007 
1008 	sip->displays.clear();
1009 
1010 	sip->pathMetadata.clear();
1011 }
1012 
1013 /**
1014  * Parse the information for a specific ship type.
1015  */
parse_ship(const char * filename,bool replace)1016 int parse_ship(const char *filename, bool replace)
1017 {
1018 	char buf[SHIP_MULTITEXT_LENGTH];
1019 	ship_info *sip;
1020 	bool create_if_not_found  = true;
1021 	int rtn = 0;
1022 
1023 	required_string("$Name:");
1024 	stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
1025 
1026 	if(optional_string("+nocreate")) {
1027 		if(!replace) {
1028 			Warning(LOCATION, "+nocreate flag used for ship in non-modular table");
1029 		}
1030 		create_if_not_found = false;
1031 	}
1032 
1033 #ifdef NDEBUG
1034 	if (get_pointer_to_first_hash_symbol(buf) && Fred_running)
1035 		rtn = 1;
1036 #endif
1037 
1038 	//Remove @ symbol
1039 	//these used to be used to denote weapons that would
1040 	//only be parsed in demo builds
1041 	if ( buf[0] == '@' ) {
1042 		backspace(buf);
1043 	}
1044 
1045 	diag_printf ("Ship name -- %s\n", buf);
1046 	//Check if ship exists already
1047 	int ship_id;
1048 	bool first_time = false;
1049 	ship_id = ship_info_lookup( buf );
1050 
1051 	if(ship_id != -1)
1052 	{
1053 		sip = &Ship_info[ship_id];
1054 		if(!replace)
1055 		{
1056 			Warning(LOCATION, "Error:  Ship name %s already exists in %s.  All ship class names must be unique.", sip->name, filename);
1057 			if ( !skip_to_start_of_string_either("$Name:", "#End")) {
1058 				Int3();
1059 			}
1060 			return -1;
1061 		}
1062 	}
1063 	else
1064 	{
1065 		//Don't create ship if it has +nocreate and is in a modular table.
1066 		if(!create_if_not_found && replace)
1067 		{
1068 			if ( !skip_to_start_of_string_either("$Name:", "#End")) {
1069 				Int3();
1070 			}
1071 
1072 			return -1;
1073 		}
1074 
1075 		//Check if there are too many ship classes
1076 		if(Num_ship_classes >= MAX_SHIP_CLASSES) {
1077 			if (!warning_too_many_ship_classes) {
1078 				Warning(LOCATION, "Too many ship classes before '%s'; maximum is %d, so only the first %d will be used\nPlease check also the debug log as it may contain other ship classes which are over the limit", buf, MAX_SHIP_CLASSES, Num_ship_classes);
1079 				warning_too_many_ship_classes = true;
1080 			} else {
1081 				mprintf(("Warning: Too many ship classes before '%s'\n", buf));
1082 			}
1083 
1084 			skip_to_start_of_string_either("$Name:", "#End");
1085 			return -1;
1086 		}
1087 
1088 		//Init vars
1089 		sip = &Ship_info[Num_ship_classes];
1090 		first_time = true;
1091 		init_ship_entry(sip);
1092 
1093 		strcpy_s(sip->name, buf);
1094 		Num_ship_classes++;
1095 	}
1096 
1097 	// Use a template for this ship.
1098 	if( optional_string( "+Use Template:" ) ) {
1099 		// Should never resolve to true, but just in case...
1100 		if( !create_if_not_found ) {
1101 			Warning(LOCATION, "Both '+nocreate' and '+Use Template:' were specified for ship class '%s', ignoring '+Use Template:'", buf);
1102 		}
1103 		else {
1104 			char template_name[SHIP_MULTITEXT_LENGTH];
1105 			stuff_string(template_name, F_NAME, SHIP_MULTITEXT_LENGTH);
1106 			int template_id = ship_template_lookup( template_name);
1107 			if ( template_id != -1 ) {
1108 				first_time = false;
1109 
1110 				// TODO: whoever implemented ship templates should implement this method
1111 				// sip->assign(&Ship_templates[template_id]);
1112 				Warning(LOCATION, "Ship templates have been broken since they were added, and are not currently supported.");
1113 
1114 				strcpy_s(sip->name, buf);
1115 			}
1116 			else {
1117 				Warning(LOCATION, "Unable to find ship template '%s' requested by ship class '%s', ignoring template request...", template_name, buf);
1118 			}
1119 		}
1120 	}
1121 
1122 	rtn = parse_ship_values(sip, false, first_time, replace);
1123 
1124 	return rtn;	//0 for success
1125 }
1126 
1127 /**
1128  * Parse the information for a specific ship type template.
1129  */
parse_ship_template()1130 int parse_ship_template()
1131 {
1132 	char buf[SHIP_MULTITEXT_LENGTH];
1133 	ship_info new_template;
1134 	ship_info *sip = &new_template;
1135 	int rtn = 0;
1136 
1137 	required_string("$Template:");
1138 	stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
1139 
1140 	if( optional_string("+nocreate") ) {
1141 		Warning(LOCATION, "+nocreate flag used on ship template. Ship templates can not be modified. Ignoring +nocreate.");
1142 	}
1143 
1144 	diag_printf ("Ship template name -- %s\n", buf);
1145 	//Check if the template exists already
1146 	int template_id;
1147 	template_id = ship_template_lookup( buf );
1148 
1149 	if( template_id != -1 ) {
1150 		sip = &Ship_templates[template_id];
1151 		Warning(LOCATION, "Error:  Ship template %s already exists. All ship template names must be unique.", sip->name);
1152 		if ( !skip_to_start_of_string_either("$Template:", "#End")) {
1153 			Int3();
1154 		}
1155 		return -1;
1156 	}
1157 	else {
1158 
1159 		init_ship_entry(sip);
1160 		strcpy_s(sip->name, buf);
1161 		//Use another template for this template. This allows for template hierarchies. - Turey
1162 		if( optional_string("+Use Template:") ) {
1163 			char template_name[SHIP_MULTITEXT_LENGTH];
1164 			stuff_string(template_name, F_NAME, SHIP_MULTITEXT_LENGTH);
1165 			template_id = ship_template_lookup( template_name);
1166 
1167 			if ( template_id != -1 ) {
1168 				// TODO: whoever implemented ship templates should implement this method
1169 				// sip->assign(&Ship_templates[template_id]);
1170 				Warning(LOCATION, "Ship templates have been broken since they were added, and are not currently supported.");
1171 
1172 				strcpy_s(sip->name, buf);
1173 			}
1174 			else {
1175 				Warning(LOCATION, "Unable to find ship template '%s' requested by ship template '%s', ignoring template request...", template_name, buf);
1176 			}
1177 		}
1178 	}
1179 
1180 	rtn = parse_ship_values( sip, true, true, false );
1181 
1182 	// Now that we're done everything, check to see if the template exists already, and if it doesn't, add it to the vector.
1183 	if ( ship_template_lookup( sip->name ) != -1 ) {
1184 		Warning(LOCATION, "Ship Template '%s' already exists, discarding duplicate...", sip->name);
1185 	}
1186 	else {
1187 		Ship_templates.push_back(*sip);
1188 	}
1189 
1190 	return rtn;
1191 }
1192 
parse_ship_sound(char * name,GameSoundsIndex id,ship_info * sip)1193 void parse_ship_sound(char *name, GameSoundsIndex id, ship_info *sip)
1194 {
1195 	Assert( name != NULL );
1196 
1197 	int temp_index = -1;
1198 
1199 	parse_sound(name, &temp_index, sip->name);
1200 
1201 	if (temp_index >= 0)
1202 		sip->ship_sounds.insert(std::pair<GameSoundsIndex, int>(id, temp_index));
1203 }
1204 
parse_ship_sounds(ship_info * sip)1205 void parse_ship_sounds(ship_info *sip)
1206 {
1207 	parse_ship_sound("$CockpitEngineSnd:",                SND_ENGINE, sip);
1208 	parse_ship_sound("$FullThrottleSnd:",                 SND_FULL_THROTTLE, sip);
1209 	parse_ship_sound("$ZeroThrottleSnd:",                 SND_ZERO_THROTTLE, sip);
1210 	parse_ship_sound("$ThrottleUpSnd:",                   SND_THROTTLE_UP, sip);
1211 	parse_ship_sound("$ThrottleDownSnd:",                 SND_THROTTLE_DOWN, sip);
1212 	parse_ship_sound("$AfterburnerSnd:",                  SND_ABURN_LOOP, sip);
1213 	parse_ship_sound("$AfterburnerEngageSnd:",            SND_ABURN_ENGAGE, sip);
1214 	parse_ship_sound("$AfterburnerFailedSnd:",            SND_ABURN_FAIL, sip);
1215 	parse_ship_sound("$MissileTrackingSnd:",              SND_MISSILE_TRACKING, sip);
1216 	parse_ship_sound("$MissileLockedSnd:",                SND_MISSILE_LOCK, sip);
1217 	parse_ship_sound("$PrimaryCycleSnd:",                 SND_PRIMARY_CYCLE, sip);
1218 	parse_ship_sound("$SecondaryCycleSnd:",               SND_SECONDARY_CYCLE, sip);
1219 	parse_ship_sound("$TargetAcquiredSnd:",               SND_TARGET_ACQUIRE, sip);
1220 	parse_ship_sound("$PrimaryFireFailedSnd:",            SND_OUT_OF_WEAPON_ENERGY, sip);
1221 	parse_ship_sound("$SecondaryFireFailedSnd:",          SND_OUT_OF_MISSLES, sip);
1222 	parse_ship_sound("$HeatSeekerLaunchWarningSnd:",      SND_HEATLOCK_WARN, sip);
1223 	parse_ship_sound("$AspectSeekerLaunchWarningSnd:",    SND_ASPECTLOCK_WARN, sip);
1224 	parse_ship_sound("$MissileLockWarningSnd:",           SND_THREAT_FLASH, sip);
1225 	parse_ship_sound("$HeatSeekerProximityWarningSnd:",   SND_PROXIMITY_WARNING, sip);
1226 	parse_ship_sound("$AspectSeekerProximityWarningSnd:", SND_PROXIMITY_ASPECT_WARNING, sip);
1227 	parse_ship_sound("$MissileEvadedSnd:",                SND_MISSILE_EVADED_POPUP, sip);
1228 	parse_ship_sound("$CargoScanningSnd:",                SND_CARGO_SCAN, sip);
1229 
1230 	// Use SND_SHIP_EXPLODE_1 for custom explosion sounds
1231 	parse_ship_sound("$ExplosionSnd:",                    SND_SHIP_EXPLODE_1, sip);
1232 }
1233 
parse_ship_particle_effect(ship_info * sip,particle_effect * pe,char * id_string)1234 void parse_ship_particle_effect(ship_info* sip, particle_effect* pe, char *id_string)
1235 {
1236 	float tempf;
1237 	int temp;
1238 	if(optional_string("+Max particles:"))
1239 	{
1240 		stuff_int(&temp);
1241 		if (temp < 0) {
1242 			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);
1243 		} else {
1244 			pe->n_high = temp;
1245 			if (pe->n_high == 0) {
1246 				// notification for disabling the particles
1247 				mprintf(("Particle effect for %s disabled on ship '%s'.\n", id_string, sip->name));
1248 			}
1249 		}
1250 	}
1251 	if(optional_string("+Min particles:"))
1252 	{
1253 		stuff_int(&temp);
1254 		if (temp < 0) {
1255 			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);
1256 		} else {
1257 			pe->n_low = temp;
1258 		}
1259 	}
1260 	if (pe->n_low > pe->n_high)
1261 		pe->n_low = pe->n_high;
1262 
1263 	if(optional_string("+Max Radius:"))
1264 	{
1265 		stuff_float(&tempf);
1266 		if (tempf <= 0.0f) {
1267 			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);
1268 		} else {
1269 			pe->max_rad = tempf;
1270 		}
1271 	}
1272 	if(optional_string("+Min Radius:"))
1273 	{
1274 		stuff_float(&tempf);
1275 		if (tempf < 0.0f) {
1276 			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);
1277 		} else {
1278 			pe->min_rad = tempf;
1279 		}
1280 	}
1281 	if (pe->min_rad > pe->max_rad)
1282 		pe->min_rad = pe->max_rad;
1283 
1284 	if(optional_string("+Max Lifetime:"))
1285 	{
1286 		stuff_float(&tempf);
1287 		if (tempf <= 0.0f) {
1288 			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);
1289 		} else {
1290 			pe->max_life = tempf;
1291 		}
1292 	}
1293 	if(optional_string("+Min Lifetime:"))
1294 	{
1295 		stuff_float(&tempf);
1296 		if (tempf < 0.0f) {
1297 			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);
1298 		} else {
1299 			pe->min_life = tempf;
1300 		}
1301 	}
1302 	if (pe->min_life > pe->max_life)
1303 		pe->min_life = pe->max_life;
1304 
1305 	if(optional_string("+Max Velocity:"))
1306 	{
1307 		stuff_float(&tempf);
1308 		if (tempf < 0.0f) {
1309 			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);
1310 		} else {
1311 			pe->max_vel = tempf;
1312 		}
1313 	}
1314 	if(optional_string("+Min Velocity:"))
1315 	{
1316 		stuff_float(&tempf);
1317 		if (tempf < 0.0f) {
1318 			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);
1319 		} else {
1320 			pe->min_vel = tempf;
1321 		}
1322 	}
1323 	if (pe->min_vel > pe->max_vel)
1324 		pe->min_vel = pe->max_vel;
1325 
1326 	if(optional_string("+Normal Variance:"))
1327 	{
1328 		stuff_float(&tempf);
1329 		if ((tempf >= 0.0f) && (tempf <= 2.0f)) {
1330 			pe->variance = tempf;
1331 		} else {
1332 			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);
1333 		}
1334 	}
1335 }
1336 
1337 /**
1338  * Common method for parsing ship/subsystem primary/secondary weapons so that the parser doesn't flip out in the event of a problem.
1339  *
1340  */
parse_weapon_bank(ship_info * sip,bool is_primary,int * num_banks,int * bank_default_weapons,int * bank_capacities)1341 void parse_weapon_bank(ship_info *sip, bool is_primary, int *num_banks, int *bank_default_weapons, int *bank_capacities)
1342 {
1343 	Assert(sip != NULL);
1344 	Assert(bank_default_weapons != NULL);
1345 	Assert(bank_capacities != NULL);
1346 	const int max_banks = is_primary ? MAX_SHIP_PRIMARY_BANKS : MAX_SHIP_SECONDARY_BANKS;
1347 	const char *default_banks_str = is_primary ? "$Default PBanks:" : "$Default SBanks:";
1348 	const char *bank_capacities_str = is_primary ? "$PBank Capacity:" : "$SBank Capacity:";
1349 
1350 	// we initialize to the previous parse, which presumably worked
1351 	int num_bank_capacities = num_banks != NULL ? *num_banks : 0;
1352 
1353 	if (optional_string(default_banks_str))
1354 	{
1355 		// get weapon list
1356 		if (num_banks != NULL)
1357 			*num_banks = stuff_int_list(bank_default_weapons, max_banks, WEAPON_LIST_TYPE);
1358 		else
1359 			stuff_int_list(bank_default_weapons, max_banks, WEAPON_LIST_TYPE);
1360 	}
1361 
1362 	if (optional_string(bank_capacities_str))
1363 	{
1364 		// get capacity list
1365 		num_bank_capacities = stuff_int_list(bank_capacities, max_banks, RAW_INTEGER_TYPE);
1366 	}
1367 
1368 	// num_banks can be null if we're parsing weapons for a turret
1369 	if ((num_banks != NULL) && (*num_banks != num_bank_capacities))
1370 	{
1371 		// okay for a ship to have 0 primary capacities, since it won't be ammo-enabled
1372 		if (is_primary && num_bank_capacities != 0)
1373 		{
1374 			Warning(LOCATION, "Ship class '%s' has %d primary banks, but %d primary capacities... fix this!!", sip->name, *num_banks, num_bank_capacities);
1375 		}
1376 
1377 		// secondaries have no excuse!
1378 		if (!is_primary)
1379 		{
1380 			Warning(LOCATION, "Ship class '%s' has %d secondary banks, but %d secondary capacities... fix this!!", sip->name, *num_banks, num_bank_capacities);
1381 		}
1382 	}
1383 }
1384 
1385 /**
1386  * Common method for parsing briefing icon info.
1387  */
parse_and_add_briefing_icon_info()1388 int parse_and_add_briefing_icon_info()
1389 {
1390 	int bii_index = -1;
1391 	size_t icon;
1392 	char regular_temp[MAX_FILENAME_LEN];
1393 	char fade_temp[MAX_FILENAME_LEN];
1394 	char highlight_temp[MAX_FILENAME_LEN];
1395 
1396 	required_string("+Regular:");
1397 	stuff_string(regular_temp, F_NAME, MAX_FILENAME_LEN);
1398 	required_string("+Fade:");
1399 	stuff_string(fade_temp, F_NAME, MAX_FILENAME_LEN);
1400 	required_string("+Highlight:");
1401 	stuff_string(highlight_temp, F_NAME, MAX_FILENAME_LEN);
1402 
1403 	// search among our existing icons
1404 	for (icon = 0; icon < Briefing_icon_info.size(); icon++)
1405 	{
1406 		if (   !stricmp(regular_temp, Briefing_icon_info[icon].regular.filename)
1407 			&& !stricmp(fade_temp, Briefing_icon_info[icon].fade.filename)
1408 			&& !stricmp(highlight_temp, Briefing_icon_info[icon].highlight.filename) )
1409 		{
1410 			bii_index = (int) icon;
1411 			break;
1412 		}
1413 	}
1414 
1415 	// icon not found: create new one
1416 	if (bii_index < 0)
1417 	{
1418 		briefing_icon_info bii;
1419 		generic_anim_init(&bii.regular, regular_temp);
1420 		hud_anim_init(&bii.fade, 0, 0, fade_temp);
1421 		hud_anim_init(&bii.highlight, 0, 0, highlight_temp);
1422 
1423 		bii_index = (int) Briefing_icon_info.size();
1424 		Briefing_icon_info.push_back(bii);
1425 	}
1426 
1427 	return bii_index;
1428 }
1429 
1430 /**
1431  * Puts values into a ship_info.
1432  */
parse_ship_values(ship_info * sip,bool isTemplate,bool first_time,bool replace)1433 int parse_ship_values(ship_info* sip, bool isTemplate, bool first_time, bool replace)
1434 {
1435 	char buf[SHIP_MULTITEXT_LENGTH];
1436 	char* info_type_name;
1437 	int i, j, num_allowed;
1438 	int allowed_weapons[MAX_WEAPON_TYPES];
1439 	int rtn = 0;
1440 	char name_tmp[NAME_LENGTH];
1441 
1442 	if ( !isTemplate ) {
1443 		info_type_name = "Ship Class";
1444 	}
1445 	else {
1446 		info_type_name = "Ship Template";
1447 	}
1448 
1449 	if(optional_string("$Alt name:"))
1450 		stuff_string(sip->alt_name, F_NAME, NAME_LENGTH);
1451 
1452 	if(optional_string("$Short name:"))
1453 		stuff_string(sip->short_name, F_NAME, NAME_LENGTH);
1454 	else if(first_time)
1455 	{
1456 		char *srcpos, *srcend, *destpos, *destend;
1457 		srcpos = sip->name;
1458 		destpos = sip->short_name;
1459 		srcend = srcpos + strlen(sip->name);
1460 		destend = destpos + sizeof(sip->short_name) - 1;
1461 		while(srcpos < srcend)
1462 		{
1463 			if(*srcpos != ' ')
1464 				*destpos++ = *srcpos++;
1465 			else
1466 				srcpos++;
1467 		}
1468 	}
1469 	diag_printf ("Ship short name -- %s\n", sip->short_name);
1470 
1471 	if (optional_string("$Species:")) {
1472 		char temp[NAME_LENGTH];
1473 		stuff_string(temp, F_NAME, NAME_LENGTH);
1474 		int i_species = 0;
1475 
1476 		bool found = false;
1477 		for (SCP_vector<species_info>::iterator sii = Species_info.begin(); sii != Species_info.end(); ++sii, ++i_species) {
1478 			if (!stricmp(temp, sii->species_name)) {
1479 				sip->species = i_species;
1480 				found = true;
1481 				break;
1482 			}
1483 		}
1484 
1485 		if (!found) {
1486 			Error(LOCATION, "Invalid Species %s defined in table entry for ship %s.\n", temp, sip->name);
1487 		}
1488 	}
1489 
1490 	diag_printf ("Ship species -- %s\n", Species_info[sip->species].species_name);
1491 
1492 	if (optional_string("+Type:")) {
1493 		stuff_malloc_string(&sip->type_str, F_MESSAGE);
1494 	}
1495 
1496 	if (optional_string("+Maneuverability:")) {
1497 		stuff_malloc_string(&sip->maneuverability_str, F_MESSAGE);
1498 	}
1499 
1500 	if (optional_string("+Armor:")) {
1501 		stuff_malloc_string(&sip->armor_str, F_MESSAGE);
1502 	}
1503 
1504 	if (optional_string("+Manufacturer:")) {
1505 		stuff_malloc_string(&sip->manufacturer_str, F_MESSAGE);
1506 	}
1507 
1508 	if (optional_string("+Description:")) {
1509 		stuff_malloc_string(&sip->desc, F_MULTITEXT, NULL);
1510 	}
1511 
1512 	if (optional_string("+Tech Title:")) {
1513 		stuff_string(sip->tech_title, F_NAME, NAME_LENGTH);
1514 	}
1515 
1516 	if (optional_string("+Tech Description:")) {
1517 		stuff_malloc_string(&sip->tech_desc, F_MULTITEXT, NULL);
1518 	}
1519 
1520 	if (optional_string("+Length:")) {
1521 		stuff_malloc_string(&sip->ship_length, F_MESSAGE);
1522 	}
1523 
1524 	if (optional_string("+Gun Mounts:")) {
1525 		stuff_malloc_string(&sip->gun_mounts, F_MESSAGE);
1526 	}
1527 
1528 	if (optional_string("+Missile Banks:")) {
1529 		stuff_malloc_string(&sip->missile_banks, F_MESSAGE);
1530 	}
1531 
1532 	// Ship fadein effect, used when no ani is specified or ship_select_3d is active
1533 	sip->selection_effect = Default_ship_select_effect; //By default, use the FS2 effect
1534 	if(optional_string("$Selection Effect:")) {
1535 		char effect[NAME_LENGTH];
1536 		stuff_string(effect, F_NAME, NAME_LENGTH);
1537 		if (!stricmp(effect, "FS2"))
1538 			sip->selection_effect = 2;
1539 		else if (!stricmp(effect, "FS1"))
1540 			sip->selection_effect = 1;
1541 		else if (!stricmp(effect, "off"))
1542 			sip->selection_effect = 0;
1543 	}
1544 
1545 	if(optional_string( "$Cockpit POF file:" ))
1546 	{
1547 		char temp[MAX_FILENAME_LEN];
1548 		stuff_string(temp, F_NAME, MAX_FILENAME_LEN);
1549 
1550 		// assume we're using this file name
1551 		bool valid = true;
1552 
1553 		// 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
1554 		if (replace)
1555 			if (sip->cockpit_pof_file[0] != '\0')
1556 				if (!cf_exists_full(temp, CF_TYPE_MODELS))
1557 					valid = false;
1558 
1559 		if (valid)
1560 			strcpy_s(sip->cockpit_pof_file, temp);
1561 		else
1562 			WarningEx(LOCATION, "Ship %s\nCockpit POF file \"%s\" invalid!", sip->name, temp);
1563 	}
1564 	if(optional_string( "+Cockpit offset:" ))
1565 	{
1566 		stuff_vec3d(&sip->cockpit_offset);
1567 	}
1568 	while(optional_string( "$Cockpit Display:" ))
1569 	{
1570 		cockpit_display_info display;
1571 
1572 		display.bg_filename[0] = 0;
1573 		display.fg_filename[0] = 0;
1574 		display.filename[0] = 0;
1575 		display.name[0] = 0;
1576 		display.offset[0] = 0;
1577 		display.offset[1] = 0;
1578 
1579 		required_string("+Texture:");
1580 		stuff_string(display.filename, F_NAME, MAX_FILENAME_LEN);
1581 
1582 		if ( optional_string("+Offsets:") ) {
1583 			stuff_int_list(display.offset, 2);
1584 		}
1585 
1586 		required_string("+Size:");
1587 		stuff_int_list(display.size, 2);
1588 
1589 		if ( optional_string("+Background:") ) {
1590 			stuff_string(display.bg_filename, F_NAME, MAX_FILENAME_LEN);
1591 		}
1592 		if ( optional_string("+Foreground:") ) {
1593 			stuff_string(display.fg_filename, F_NAME, MAX_FILENAME_LEN);
1594 		}
1595 
1596 		required_string("+Display Name:");
1597 		stuff_string(display.name, F_NAME, MAX_FILENAME_LEN);
1598 
1599 		if ( display.offset[0] < 0 || display.offset[1] < 0 ) {
1600 			Warning(LOCATION, "Negative display offsets given for cockpit display on %s, skipping entry", sip->name);
1601 			continue;
1602 		}
1603 
1604 		if( display.size[0] <= 0 || display.size[1] <= 0 ) {
1605 			Warning(LOCATION, "Negative or zero display size given for cockpit display on %s, skipping entry", sip->name);
1606 			continue;
1607 		}
1608 
1609 		sip->displays.push_back(display);
1610 	}
1611 
1612 	if(optional_string( "$POF file:" ))
1613 	{
1614 		char temp[MAX_FILENAME_LEN];
1615 		stuff_string(temp, F_NAME, MAX_FILENAME_LEN);
1616 
1617 		// assume we're using this file name
1618 		bool valid = true;
1619 
1620 		// 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
1621 		if (replace)
1622 			if (sip->pof_file[0] != '\0')
1623 				if (!cf_exists_full(temp, CF_TYPE_MODELS))
1624 					valid = false;
1625 
1626 		if (valid)
1627 			strcpy_s(sip->pof_file, temp);
1628 		else
1629 			WarningEx(LOCATION, "Ship %s\nPOF file \"%s\" invalid!", sip->name, temp);
1630 	}
1631 
1632 	// ship class texture replacement - Goober5000 and taylor
1633 	int PLACEHOLDER_num_texture_replacements = 0;
1634 	char PLACEHOLDER_old_texture[MAX_FILENAME_LEN];
1635 	char PLACEHOLDER_new_texture[MAX_FILENAME_LEN];
1636 	int PLACEHOLDER_new_texture_id;
1637 	if (optional_string("$Texture Replace:"))
1638 	{
1639 		char *p;
1640 
1641 		while ((PLACEHOLDER_num_texture_replacements < MAX_REPLACEMENT_TEXTURES) && (optional_string("+old:")))
1642 		{
1643 			stuff_string(PLACEHOLDER_old_texture, F_NAME, MAX_FILENAME_LEN);
1644 			required_string("+new:");
1645 			stuff_string(PLACEHOLDER_new_texture, F_NAME, MAX_FILENAME_LEN);
1646 
1647 			// get rid of extensions
1648 			p = strchr(PLACEHOLDER_old_texture, '.');
1649 			if (p)
1650 			{
1651 				mprintf(("Extraneous extension found on replacement texture %s!\n", PLACEHOLDER_old_texture));
1652 				*p = 0;
1653 			}
1654 			p = strchr(PLACEHOLDER_new_texture, '.');
1655 			if (p)
1656 			{
1657 				mprintf(("Extraneous extension found on replacement texture %s!\n", PLACEHOLDER_new_texture));
1658 				*p = 0;
1659 			}
1660 
1661 			// load the texture
1662 			PLACEHOLDER_new_texture_id = bm_load(PLACEHOLDER_new_texture);
1663 
1664 			if (PLACEHOLDER_new_texture_id < 0)
1665 			{
1666 				mprintf(("Could not load replacement texture %s for ship %s\n", PLACEHOLDER_new_texture, sip->name));
1667 			}
1668 
1669 			// increment
1670 			PLACEHOLDER_num_texture_replacements++;
1671 		}
1672 	}
1673 
1674 	// optional hud targeting model
1675 	if(optional_string( "$POF target file:"))
1676 	{
1677 		char temp[MAX_FILENAME_LEN];
1678 		stuff_string(temp, F_NAME, MAX_FILENAME_LEN);
1679 
1680 		// assume we're using this file name
1681 		bool valid = true;
1682 
1683 		// 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
1684 		if (replace)
1685 			if (sip->pof_file[0] != '\0')
1686 				if (!cf_exists_full(temp, CF_TYPE_MODELS))
1687 					valid = false;
1688 
1689 		if (valid)
1690 			strcpy_s(sip->pof_file_hud, temp);
1691 		else
1692 			WarningEx(LOCATION, "Ship \"%s\" POF target file \"%s\" invalid!", sip->name, temp);
1693 	}
1694 
1695 	// optional hud target LOD if not using special hud model
1696 	if (optional_string( "$POF target LOD:" )) {
1697 		stuff_int(&sip->hud_target_lod);
1698 	}
1699 
1700 	if(optional_string("$Detail distance:")) {
1701 		sip->num_detail_levels = stuff_int_list(sip->detail_distance, MAX_SHIP_DETAIL_LEVELS, RAW_INTEGER_TYPE);
1702 	}
1703 
1704 	// check for optional pixel colors
1705 	while(optional_string("$ND:")){
1706 		ubyte nr, ng, nb;
1707 		stuff_ubyte(&nr);
1708 		stuff_ubyte(&ng);
1709 		stuff_ubyte(&nb);
1710 
1711 		if(sip->num_nondark_colors < MAX_NONDARK_COLORS){
1712 			sip->nondark_colors[sip->num_nondark_colors][0] = nr;
1713 			sip->nondark_colors[sip->num_nondark_colors][1] = ng;
1714 			sip->nondark_colors[sip->num_nondark_colors++][2] = nb;
1715 		}
1716 	}
1717 
1718 	if (optional_string("$Enable Team Colors:")) {
1719 		stuff_boolean(&sip->uses_team_colors);
1720 		sip->default_team_name = "None";
1721 	}
1722 
1723 	if (optional_string("$Default Team:")) {
1724 		char temp[NAME_LENGTH];
1725 		stuff_string(temp, F_NAME, NAME_LENGTH);
1726 		SCP_string name = temp;
1727 		if (name == "None") {
1728 			sip->uses_team_colors = true;
1729 		} else {
1730 			if (Team_Colors.find(name) != Team_Colors.end()) {
1731 				sip->default_team_name = name;
1732 				sip->uses_team_colors = true;
1733 			} else {
1734 				Warning(LOCATION, "Team name %s is invalid. Teams must be defined in colors.tbl.\n", temp);
1735 			}
1736 		}
1737 	}
1738 
1739 	if(optional_string("$Show damage:"))
1740 	{
1741 		int bogus_bool;
1742 		stuff_boolean(&bogus_bool);
1743 	}
1744 
1745 	if(optional_string("$Damage Lightning Type:"))
1746 	{
1747 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
1748 		j = lightningtype_match(buf);
1749 		if(j >= 0) {
1750 			sip->damage_lightning_type = j;
1751 		} else {
1752 			Warning(LOCATION, "Invalid lightning type '%s' specified for ship '%s'", buf, sip->name);
1753 			sip->damage_lightning_type = SLT_DEFAULT;
1754 		}
1755 	}
1756 
1757 	if(optional_string("$Impact:"))
1758 	{
1759 		if(optional_string("+Damage Type:"))
1760 		{
1761 			stuff_string(buf, F_NAME, NAME_LENGTH);
1762 			sip->collision_damage_type_idx = damage_type_add(buf);
1763 		}
1764 	}
1765 
1766 	//HACK -
1767 	//This should really be reworked so that all particle fields
1768 	//are settable, but erg, just not happening right now -C
1769 	if(optional_string("$Impact Spew:"))
1770 	{
1771 		parse_ship_particle_effect(sip, &sip->impact_spew, "impact spew");
1772 	}
1773 	if(optional_string("$Damage Spew:"))
1774 	{
1775 		parse_ship_particle_effect(sip, &sip->damage_spew, "damage spew");
1776 	}
1777 
1778 	if(optional_string("$Collision Physics:"))
1779 	{
1780 		if(optional_string("+Bounce:"))	{
1781 			stuff_float(&sip->collision_physics.bounce);
1782 		}
1783 		if(optional_string("+Both Small Bounce:")) {
1784 			stuff_float(&sip->collision_physics.both_small_bounce);
1785 		}
1786 		if(optional_string("+Friction:")) {
1787 			stuff_float(&sip->collision_physics.friction);
1788 		}
1789 		if(optional_string("+Rotation Factor:")) {
1790 			stuff_float(&sip->collision_physics.friction);
1791 		}
1792 		if(optional_string("+Landing Max Forward Vel:")) {
1793 			stuff_float(&sip->collision_physics.landing_max_z);
1794 		}
1795 		if(optional_string("+Landing Min Forward Vel:")) {
1796 			stuff_float(&sip->collision_physics.landing_min_z);
1797 		}
1798 		if(optional_string("+Landing Max Descent Vel:")) {
1799 			stuff_float(&sip->collision_physics.landing_min_y);
1800 			sip->collision_physics.landing_min_y *= -1;
1801 		}
1802 		if(optional_string("+Landing Max Horizontal Vel:")) {
1803 			stuff_float(&sip->collision_physics.landing_max_x);
1804 		}
1805 		if(optional_string("+Landing Max Angle:")) {
1806 			float degrees;
1807 			stuff_float(&degrees);
1808 			sip->collision_physics.landing_max_angle = cos(ANG_TO_RAD(90 - degrees));
1809 		}
1810 		if(optional_string("+Landing Min Angle:")) {
1811 			float degrees;
1812 			stuff_float(&degrees);
1813 			sip->collision_physics.landing_min_angle = cos(ANG_TO_RAD(90 - degrees));
1814 		}
1815 		if(optional_string("+Landing Max Rotate Angle:")) {
1816 			float degrees;
1817 			stuff_float(&degrees);
1818 			sip->collision_physics.landing_max_rot_angle = cos(ANG_TO_RAD(90 - degrees));
1819 		}
1820 		if(optional_string("+Reorient Max Forward Vel:")) {
1821 			stuff_float(&sip->collision_physics.reorient_max_z);
1822 		}
1823 		if(optional_string("+Reorient Min Forward Vel:")) {
1824 			stuff_float(&sip->collision_physics.reorient_min_z);
1825 		}
1826 		if(optional_string("+Reorient Max Descent Vel:")) {
1827 			stuff_float(&sip->collision_physics.reorient_min_y);
1828 			sip->collision_physics.reorient_min_y *= -1;
1829 		}
1830 		if(optional_string("+Reorient Max Horizontal Vel:")) {
1831 			stuff_float(&sip->collision_physics.reorient_max_x);
1832 		}
1833 		if(optional_string("+Reorient Max Angle:")) {
1834 			float degrees;
1835 			stuff_float(&degrees);
1836 			sip->collision_physics.reorient_max_angle = cos(ANG_TO_RAD(90 - degrees));
1837 		}
1838 		if(optional_string("+Reorient Min Angle:")) {
1839 			float degrees;
1840 			stuff_float(&degrees);
1841 			sip->collision_physics.reorient_min_angle = cos(ANG_TO_RAD(90 - degrees));
1842 		}
1843 		if(optional_string("+Reorient Max Rotate Angle:")) {
1844 			float degrees;
1845 			stuff_float(&degrees);
1846 			sip->collision_physics.reorient_max_rot_angle = cos(ANG_TO_RAD(90 - degrees));
1847 		}
1848 		if(optional_string("+Reorient Speed Mult:")) {
1849 			stuff_float(&sip->collision_physics.reorient_mult);
1850 		}
1851 		if(optional_string("+Landing Rest Angle:")) {
1852 			float degrees;
1853 			stuff_float(&degrees);
1854 			sip->collision_physics.landing_rest_angle = cos(ANG_TO_RAD(90 - degrees));
1855 		}
1856 		parse_sound("+Landing Sound:", &sip->collision_physics.landing_sound_idx, sip->name);
1857 	}
1858 
1859 
1860 	if(optional_string("$Debris:"))
1861 	{
1862 		if(optional_string("+Min Lifetime:"))	{
1863 			stuff_float(&sip->debris_min_lifetime);
1864 			if(sip->debris_min_lifetime < 0.0f)
1865 				Warning(LOCATION, "Debris min lifetime on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1866 		}
1867 		if(optional_string("+Max Lifetime:"))	{
1868 			stuff_float(&sip->debris_max_lifetime);
1869 			if(sip->debris_max_lifetime < 0.0f)
1870 				Warning(LOCATION, "Debris max lifetime on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1871 		}
1872 		if(optional_string("+Min Speed:"))	{
1873 			stuff_float(&sip->debris_min_speed);
1874 			if(sip->debris_min_speed < 0.0f)
1875 				Warning(LOCATION, "Debris min speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1876 		}
1877 		if(optional_string("+Max Speed:"))	{
1878 			stuff_float(&sip->debris_max_speed);
1879 			if(sip->debris_max_speed < 0.0f)
1880 				Warning(LOCATION, "Debris max speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1881 		}
1882 		if(optional_string("+Min Rotation speed:"))	{
1883 			stuff_float(&sip->debris_min_rotspeed);
1884 			if(sip->debris_min_rotspeed < 0.0f)
1885 				Warning(LOCATION, "Debris min speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1886 		}
1887 		if(optional_string("+Max Rotation speed:"))	{
1888 			stuff_float(&sip->debris_max_rotspeed);
1889 			if(sip->debris_max_rotspeed < 0.0f)
1890 				Warning(LOCATION, "Debris max speed on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1891 		}
1892 		if(optional_string("+Damage Type:")) {
1893 			stuff_string(buf, F_NAME, NAME_LENGTH);
1894 			sip->debris_damage_type_idx = damage_type_add(buf);
1895 		}
1896 		if(optional_string("+Min Hitpoints:")) {
1897 			stuff_float(&sip->debris_min_hitpoints);
1898 			if(sip->debris_min_hitpoints < 0.0f)
1899 				Warning(LOCATION, "Debris min hitpoints on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1900 		}
1901 		if(optional_string("+Max Hitpoints:")) {
1902 			stuff_float(&sip->debris_max_hitpoints);
1903 			if(sip->debris_max_hitpoints < 0.0f)
1904 				Warning(LOCATION, "Debris max hitpoints on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1905 		}
1906 		if(optional_string("+Damage Multiplier:")) {
1907 			stuff_float(&sip->debris_damage_mult);
1908 			if(sip->debris_damage_mult < 0.0f)
1909 				Warning(LOCATION, "Debris damage multiplier on %s '%s' is below 0 and will be ignored", info_type_name, sip->name);
1910 		}
1911 		if(optional_string("+Lightning Arc Percent:")) {
1912 			stuff_float(&sip->debris_arc_percent);
1913 			if(sip->debris_arc_percent < 0.0f || sip->debris_arc_percent > 100.0f) {
1914 				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);
1915 				sip->debris_arc_percent = 50.0;
1916 			}
1917 			//Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0
1918 			sip->debris_arc_percent /= 100.0;
1919 		}
1920 
1921 	}
1922 	//WMC - sanity checking
1923 	if(sip->debris_min_speed > sip->debris_max_speed && sip->debris_max_speed >= 0.0f) {
1924 		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);
1925 		sip->debris_min_speed = sip->debris_max_speed;
1926 	}
1927 	if(sip->debris_min_rotspeed > sip->debris_max_rotspeed && sip->debris_max_rotspeed >= 0.0f) {
1928 		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);
1929 		sip->debris_min_rotspeed = sip->debris_max_rotspeed;
1930 	}
1931 	if(sip->debris_min_lifetime > sip->debris_max_lifetime && sip->debris_max_lifetime >= 0.0f) {
1932 		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);
1933 		sip->debris_min_lifetime = sip->debris_max_lifetime;
1934 	}
1935 	if(sip->debris_min_hitpoints > sip->debris_max_hitpoints && sip->debris_max_hitpoints >= 0.0f) {
1936 		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);
1937 		sip->debris_min_hitpoints = sip->debris_max_hitpoints;
1938 	}
1939 
1940 	if(optional_string("$Density:"))
1941 		stuff_float( &(sip->density) );
1942 	diag_printf ("Ship density -- %7.3f\n", sip->density);
1943 
1944 	if(optional_string("$Damp:"))
1945 		stuff_float( &(sip->damp) );
1946 	diag_printf ("Ship damp -- %7.3f\n", sip->damp);
1947 
1948 	if(optional_string("$Rotdamp:"))
1949 		stuff_float( &(sip->rotdamp) );
1950 	diag_printf ("Ship rotdamp -- %7.3f\n", sip->rotdamp);
1951 
1952 	if(optional_string("$Banking Constant:"))
1953 		stuff_float( &(sip->delta_bank_const) );
1954 	diag_printf ("%s '%s' delta_bank_const -- %7.3f\n", info_type_name, sip->name, sip->delta_bank_const);
1955 
1956 	if(optional_string("$Max Velocity:"))
1957 	{
1958 		stuff_vec3d(&sip->max_vel);
1959 		sip->max_accel = sip->max_vel.xyz.z;
1960 	}
1961 
1962 	// calculate the max speed from max_velocity
1963 	sip->max_speed = sip->max_vel.xyz.z;
1964 
1965 	if(optional_string("$Rotation Time:"))
1966 	{
1967 		stuff_vec3d(&sip->rotation_time);
1968 
1969 		// div/0 safety check.
1970 		if ((sip->rotation_time.xyz.x == 0) || (sip->rotation_time.xyz.y == 0) || (sip->rotation_time.xyz.z == 0))
1971 			Warning(LOCATION, "Rotation time must have non-zero values in each of the three variables.\nFix this in ship %s\n", sip->name);
1972 
1973 		sip->srotation_time = (sip->rotation_time.xyz.x + sip->rotation_time.xyz.y)/2.0f;
1974 
1975 		sip->max_rotvel.xyz.x = (2 * PI) / sip->rotation_time.xyz.x;
1976 		sip->max_rotvel.xyz.y = (2 * PI) / sip->rotation_time.xyz.y;
1977 		sip->max_rotvel.xyz.z = (2 * PI) / sip->rotation_time.xyz.z;
1978 	}
1979 
1980 	// get the backwards velocity;
1981 	if(optional_string("$Rear Velocity:"))
1982 	{
1983 		stuff_float(&sip->max_rear_vel);
1984 		sip->min_speed = -sip->max_rear_vel;
1985 	}
1986 
1987 	// get the accelerations
1988 	if(optional_string("$Forward accel:"))
1989 		stuff_float(&sip->forward_accel );
1990 
1991 	if(optional_string("$Forward decel:"))
1992 		stuff_float(&sip->forward_decel );
1993 
1994 	if(optional_string("$Slide accel:"))
1995 		stuff_float(&sip->slide_accel );
1996 
1997 	if(optional_string("$Slide decel:"))
1998 		stuff_float(&sip->slide_decel );
1999 
2000 	if(optional_string("$Glide:"))
2001 	{
2002 		stuff_boolean(&sip->can_glide);
2003 	}
2004 
2005 	if(sip->can_glide == true)
2006 	{
2007 		if(optional_string("+Dynamic Glide Cap:"))
2008 			stuff_boolean(&sip->glide_dynamic_cap);
2009 		if(optional_string("+Max Glide Speed:"))
2010 			stuff_float(&sip->glide_cap );
2011 		if(optional_string("+Glide Accel Mult:"))
2012 			stuff_float(&sip->glide_accel_mult);
2013 	}
2014 
2015 	if(optional_string("$Use Newtonian Dampening:")) {
2016 			sip->newtonian_damp_override = true;
2017 			stuff_boolean(&sip->use_newtonian_damp);
2018 	}
2019 
2020 	if(optional_string("$Autoaim FOV:"))
2021 	{
2022 		float fov_temp;
2023 		stuff_float(&fov_temp);
2024 
2025 		// Make sure it is a reasonable value
2026 		if (fov_temp < 0.0f)
2027 			fov_temp = 0.0f;
2028 
2029 		if (fov_temp > 180.0f)
2030 			fov_temp = 180.0f;
2031 
2032 		sip->aiming_flags |= AIM_FLAG_AUTOAIM;
2033 		sip->autoaim_fov = fov_temp * PI / 180.0f;
2034 
2035 		if(optional_string("+Converging Autoaim"))
2036 			sip->aiming_flags |= AIM_FLAG_AUTOAIM_CONVERGENCE;
2037 
2038 		if(optional_string("+Minimum Distance:"))
2039 			stuff_float(&sip->minimum_convergence_distance);
2040 	}
2041 
2042 	if(optional_string("$Convergence:"))
2043 	{
2044 		if(optional_string("+Automatic"))
2045 		{
2046 			sip->aiming_flags |= AIM_FLAG_AUTO_CONVERGENCE;
2047 			if(optional_string("+Minimum Distance:"))
2048 				stuff_float(&sip->minimum_convergence_distance);
2049 		}
2050 		if(optional_string("+Standard"))
2051 		{
2052 			sip->aiming_flags |= AIM_FLAG_STD_CONVERGENCE;
2053 			if(required_string("+Distance:"))
2054 				stuff_float(&sip->convergence_distance);
2055 		}
2056 		if(optional_string("+Offset:")) {
2057 			stuff_vec3d(&sip->convergence_offset);
2058 
2059 			if (IS_VEC_NULL(&sip->convergence_offset))
2060 				sip->aiming_flags &= ~AIM_FLAG_CONVERGENCE_OFFSET;
2061 			else
2062 				sip->aiming_flags |= AIM_FLAG_CONVERGENCE_OFFSET;
2063 		}
2064 	}
2065 
2066 	if(optional_string("$Warpin type:"))
2067 	{
2068 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
2069 		j = warptype_match(buf);
2070 		if(j >= 0) {
2071 			sip->warpin_type = j;
2072 		} else {
2073 			Warning(LOCATION, "Invalid warpin type '%s' specified for ship '%s'", buf, sip->name);
2074 			sip->warpin_type = WT_DEFAULT;
2075 		}
2076 	}
2077 
2078 	parse_sound("$Warpin Start Sound:", &sip->warpin_snd_start, sip->name);
2079 	parse_sound("$Warpin End Sound:", &sip->warpin_snd_end, sip->name);
2080 
2081 	if(optional_string("$Warpin speed:"))
2082 	{
2083 		stuff_float(&sip->warpin_speed);
2084 	}
2085 
2086 	if(optional_string("$Warpin time:"))
2087 	{
2088 		float t_time;
2089 		stuff_float(&t_time);
2090 		sip->warpin_time = fl2i(t_time*1000.0f);
2091 		if(sip->warpin_time <= 0) {
2092 			Warning(LOCATION, "Warp-in time specified as 0 or less on ship '%s'; value ignored", sip->name);
2093 		}
2094 	}
2095 
2096 	if(optional_string("$Warpin decel exp:"))
2097 	{
2098 		stuff_float(&sip->warpin_decel_exp);
2099 		if (sip->warpin_decel_exp < 0.0f) {
2100 			Warning(LOCATION, "Warp-in deceleration exponent specified as less than 0 on ship '%s'; value ignored", sip->name);
2101 			sip->warpin_decel_exp = 1.0f;
2102 		}
2103 	}
2104 
2105 	if(optional_string("$Warpin radius:"))
2106 	{
2107 		stuff_float(&sip->warpin_radius);
2108 		if(sip->warpin_radius <= 0.0f) {
2109 			Warning(LOCATION, "Warp-in radius specified as 0 or less on ship '%s'; value ignored", sip->name);
2110 		}
2111 	}
2112 
2113 	if(optional_string("$Warpin animation:"))
2114 	{
2115 		stuff_string(sip->warpin_anim, F_NAME, MAX_FILENAME_LEN);
2116 	}
2117 
2118 	if(optional_string("$Warpout type:"))
2119 	{
2120 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
2121 		j = warptype_match(buf);
2122 		if(j >= 0) {
2123 			sip->warpout_type = j;
2124 		} else {
2125 			Warning(LOCATION, "Invalid warpout type '%s' specified for ship '%s'", buf, sip->name);
2126 			sip->warpout_type = WT_DEFAULT;
2127 		}
2128 	}
2129 
2130 	parse_sound("$Warpout Start Sound:", &sip->warpout_snd_start, sip->name);
2131 	parse_sound("$Warpout End Sound:", &sip->warpout_snd_end, sip->name);
2132 
2133 	if(optional_string("$Warpout engage time:"))
2134 	{
2135 		float t_time;
2136 		stuff_float(&t_time);
2137 		if (t_time >= 0)
2138 			sip->warpout_engage_time = fl2i(t_time*1000.0f);
2139 		else
2140 			Warning(LOCATION, "Warp-out engage time specified as 0 or less on ship '%s'; value ignored", sip->name);
2141 	} else {
2142 		sip->warpout_engage_time = -1;
2143 	}
2144 
2145 	if(optional_string("$Warpout speed:"))
2146 	{
2147 		stuff_float(&sip->warpout_speed);
2148 	}
2149 
2150 	if(optional_string("$Warpout time:"))
2151 	{
2152 		float t_time;
2153 		stuff_float(&t_time);
2154 		sip->warpout_time = fl2i(t_time*1000.0f);
2155 		if(sip->warpout_time <= 0) {
2156 			Warning(LOCATION, "Warp-out time specified as 0 or less on ship '%s'; value ignored", sip->name);
2157 		}
2158 	}
2159 
2160 	if(optional_string("$Warpout accel exp:"))
2161 	{
2162 		stuff_float(&sip->warpout_accel_exp);
2163 		if (sip->warpout_accel_exp < 0.0f) {
2164 			Warning(LOCATION, "Warp-out acceleration exponent specified as less than 0 on ship '%s'; value ignored", sip->name);
2165 			sip->warpout_accel_exp = 1.0f;
2166 		}
2167 	}
2168 
2169 	if(optional_string("$Warpout radius:"))
2170 	{
2171 		stuff_float(&sip->warpout_radius);
2172 		if(sip->warpout_radius <= 0.0f) {
2173 			Warning(LOCATION, "Warp-out radius specified as 0 or less on ship '%s'; value ignored", sip->name);
2174 		}
2175 	}
2176 
2177 	if(optional_string("$Warpout animation:"))
2178 	{
2179 		stuff_string(sip->warpout_anim, F_NAME, MAX_FILENAME_LEN);
2180 	}
2181 
2182 
2183 	if(optional_string("$Player warpout speed:"))
2184 	{
2185 		stuff_float(&sip->warpout_player_speed);
2186 		if(sip->warpout_player_speed == 0.0f) {
2187 			Warning(LOCATION, "Player warp-out speed cannot be 0; value ignored.");
2188 		}
2189 	}
2190 
2191 	// get ship explosion info
2192 	shockwave_create_info *sci = &sip->shockwave;
2193 	if(optional_string("$Expl inner rad:")){
2194 		stuff_float(&sci->inner_rad);
2195 	}
2196 
2197 	if(optional_string("$Expl outer rad:")){
2198 		stuff_float(&sci->outer_rad);
2199 	}
2200 
2201 	if(optional_string("$Expl damage:")){
2202 		stuff_float(&sci->damage);
2203 	}
2204 
2205 	if(optional_string("$Expl blast:")){
2206 		stuff_float(&sci->blast);
2207 	}
2208 
2209 	if(optional_string("$Expl Propagates:")){
2210 		stuff_boolean(&sip->explosion_propagates);
2211 	}
2212 
2213 	if(optional_string("$Propagating Expl Radius Multiplier:")){
2214 		stuff_float(&sip->prop_exp_rad_mult);
2215 		if(sip->prop_exp_rad_mult <= 0) {
2216 			// on invalid value return to default setting
2217 			Warning(LOCATION, "Propagating explosion radius multiplier was set to non-positive value.\nDefaulting multiplier to 1.0 on ship '%s'.\n", sip->name);
2218 			sip->prop_exp_rad_mult = 1.0f;
2219 		}
2220 	}
2221 
2222 	if(optional_string("$Expl Visual Rad:")){
2223 		stuff_float(&sip->big_exp_visual_rad);
2224 	}
2225 
2226 	if(optional_string("$Base Death-Roll Time:")){
2227 		stuff_int(&sip->death_roll_base_time);
2228 		if (sip->death_roll_base_time < 2)
2229 			sip->death_roll_base_time = 2;
2230 	}
2231 
2232 	if(optional_string("$Death-Roll Explosion Radius Mult:")){
2233 		stuff_float(&sip->death_roll_r_mult);
2234 		if (sip->death_roll_r_mult < 0)
2235 			sip->death_roll_r_mult = 0;
2236 	}
2237 
2238 	if(optional_string("$Death-Roll Explosion Intensity Mult:")){
2239 		stuff_float(&sip->death_roll_time_mult);
2240 		if (sip->death_roll_time_mult <= 0)
2241 			sip->death_roll_time_mult = 1.0f;
2242 	}
2243 
2244 	if(optional_string("$Death FX Explosion Radius Mult:")){
2245 		stuff_float(&sip->death_fx_r_mult);
2246 		if (sip->death_fx_r_mult < 0)
2247 			sip->death_fx_r_mult = 0;
2248 	}
2249 
2250 	if(optional_string("$Death FX Explosion Count:")){
2251 		stuff_int(&sip->death_fx_count);
2252 		if (sip->death_fx_count < 0)
2253 			sip->death_fx_count = 0;
2254 	}
2255 
2256 	if(optional_string("$Ship Splitting Particles:"))
2257 	{
2258 		parse_ship_particle_effect(sip, &sip->split_particles, "ship split spew");
2259 	}
2260 
2261 	if(optional_string("$Ship Death Particles:"))
2262 	{
2263 		parse_ship_particle_effect(sip, &sip->regular_end_particles, "normal death spew");
2264 	}
2265 
2266 	if(optional_string("$Alternate Death Particles:"))
2267 	{
2268 		parse_ship_particle_effect(sip, &sip->knossos_end_particles, "knossos death spew");
2269 	}
2270 
2271 	if(optional_string("$Vaporize Percent Chance:")){
2272 		stuff_float(&sip->vaporize_chance);
2273 		if (sip->vaporize_chance < 0.0f || sip->vaporize_chance > 100.0f) {
2274 			sip->vaporize_chance = 0.0f;
2275 			Warning(LOCATION, "$Vaporize Percent Chance should be between 0 and 100.0 (read %f). Setting to 0.", sip->vaporize_chance);
2276 		}
2277 		//Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0
2278 		sip->vaporize_chance /= 100.0;
2279 	}
2280 
2281 	if(optional_string("$Shockwave Damage Type:")) {
2282 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
2283 		sci->damage_type_idx_sav = damage_type_add(buf);
2284 		sci->damage_type_idx = sci->damage_type_idx_sav;
2285 	}
2286 
2287 	if(optional_string("$Shockwave Speed:")){
2288 		stuff_float( &sci->speed );
2289 	}
2290 
2291 	if(optional_string("$Shockwave Count:")){
2292 		stuff_int(&sip->shockwave_count);
2293 	}
2294 
2295 	if(optional_string("$Shockwave model:")){
2296 		stuff_string( sci->pof_name, F_NAME, MAX_FILENAME_LEN);
2297 	}
2298 
2299 	if(optional_string("$Shockwave name:")) {
2300 		stuff_string( sci->name, F_NAME, NAME_LENGTH);
2301 	}
2302 
2303 	if(optional_string("$Explosion Animations:")){
2304 		int temp[MAX_FIREBALL_TYPES];
2305 		int parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
2306 		sip->explosion_bitmap_anims.clear();
2307 		sip->explosion_bitmap_anims.insert(sip->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
2308 	}
2309 
2310 	if (optional_string("$Weapon Model Draw Distance:")) {
2311 		stuff_float( &sip->weapon_model_draw_distance );
2312 	}
2313 
2314 	// Goober5000 - fixed Bobboau's implementation of restricted banks
2315 	int bank;
2316 
2317 	// Set the weapons filter used in weapons loadout (for primary weapons)
2318 	if (optional_string("$Allowed PBanks:"))
2319 	{
2320 		bank = -1;
2321 
2322 		while (check_for_string("("))
2323 		{
2324 			bank++;
2325 
2326 			// make sure we don't specify more than we have banks for
2327 			if (bank >= MAX_SHIP_PRIMARY_BANKS)
2328 			{
2329 				Warning(LOCATION, "$Allowed PBanks bank-specific loadout for %s exceeds permissible number of primary banks.  Ignoring the rest...", sip->name);
2330 				bank--;
2331 				break;
2332 			}
2333 
2334 			num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
2335 
2336 			// actually say which weapons are allowed
2337 			for ( i = 0; i < num_allowed; i++ )
2338 			{
2339 				if ( allowed_weapons[i] >= 0 )		// MK, Bug fix, 9/6/99.  Used to be "allowed_weapons" not "allowed_weapons[i]".
2340 				{
2341 					sip->allowed_bank_restricted_weapons[bank][allowed_weapons[i]] |= REGULAR_WEAPON;
2342 				}
2343 			}
2344 		}
2345 
2346 		// set flags if need be
2347 		if (bank > 0)	// meaning there was a restricted bank table entry
2348 		{
2349 			for (i=0; i<=bank; i++)
2350 			{
2351 				sip->restricted_loadout_flag[i] |= REGULAR_WEAPON;
2352 			}
2353 		}
2354 	}
2355 
2356 	// Set the weapons filter used in weapons loadout (for primary weapons)
2357 	if (optional_string("$Allowed Dogfight PBanks:"))
2358 	{
2359 		bank = -1;
2360 
2361 		while (check_for_string("("))
2362 		{
2363 			bank++;
2364 
2365 			// make sure we don't specify more than we have banks for
2366 			if (bank >= MAX_SHIP_PRIMARY_BANKS)
2367 			{
2368 				Warning(LOCATION, "$Allowed Dogfight PBanks bank-specific loadout for %s exceeds permissible number of primary banks.  Ignoring the rest...", sip->name);
2369 				bank--;
2370 				break;
2371 			}
2372 
2373 			num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
2374 
2375 			// actually say which weapons are allowed
2376 			for ( i = 0; i < num_allowed; i++ )
2377 			{
2378 				if ( allowed_weapons[i] >= 0 )		// MK, Bug fix, 9/6/99.  Used to be "allowed_weapons" not "allowed_weapons[i]".
2379 				{
2380 					sip->allowed_bank_restricted_weapons[bank][allowed_weapons[i]] |= DOGFIGHT_WEAPON;
2381 				}
2382 			}
2383 		}
2384 
2385 		// set flags if need be
2386 		if (bank > 0)	// meaning there was a restricted bank table entry
2387 		{
2388 			for (i=0; i<=bank; i++)
2389 			{
2390 				sip->restricted_loadout_flag[i] |= DOGFIGHT_WEAPON;
2391 			}
2392 		}
2393 	}
2394 
2395 	// Get primary bank weapons
2396 	parse_weapon_bank(sip, true, &sip->num_primary_banks, sip->primary_bank_weapons, sip->primary_bank_ammo_capacity);
2397 
2398 	if(optional_string("$Show Primary Models:"))
2399 	{
2400 		sip->flags2 |= SIF2_DRAW_WEAPON_MODELS;
2401 		stuff_bool_list(sip->draw_primary_models, sip->num_primary_banks);
2402 	}
2403 
2404 	// Set the weapons filter used in weapons loadout (for secondary weapons)
2405 	if (optional_string("$Allowed SBanks:"))
2406 	{
2407 		bank = -1;
2408 
2409 		while (check_for_string("("))
2410 		{
2411 			bank++;
2412 
2413 			// make sure we don't specify more than we have banks for
2414 			if (bank >= MAX_SHIP_SECONDARY_BANKS)
2415 			{
2416 				Warning(LOCATION, "$Allowed SBanks bank-specific loadout for %s exceeds permissible number of secondary banks.  Ignoring the rest...", sip->name);
2417 				bank--;
2418 				break;
2419 			}
2420 
2421 			num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
2422 
2423 			// actually say which weapons are allowed
2424 			for ( i = 0; i < num_allowed; i++ )
2425 			{
2426 				if ( allowed_weapons[i] >= 0 )		// MK, Bug fix, 9/6/99.  Used to be "allowed_weapons" not "allowed_weapons[i]".
2427 				{
2428 					sip->allowed_bank_restricted_weapons[MAX_SHIP_PRIMARY_BANKS+bank][allowed_weapons[i]] |= REGULAR_WEAPON;
2429 				}
2430 			}
2431 		}
2432 
2433 		// set flags if need be
2434 		if (bank > 0)	// meaning there was a restricted bank table entry
2435 		{
2436 			for (i=0; i<=bank; i++)
2437 			{
2438 				sip->restricted_loadout_flag[MAX_SHIP_PRIMARY_BANKS+i] |= REGULAR_WEAPON;
2439 			}
2440 		}
2441 	}
2442 
2443 	// Set the weapons filter used in weapons loadout (for secondary weapons)
2444 	if (optional_string("$Allowed Dogfight SBanks:"))
2445 	{
2446 		bank = -1;
2447 
2448 		while (check_for_string("("))
2449 		{
2450 			bank++;
2451 
2452 			// make sure we don't specify more than we have banks for
2453 			if (bank >= MAX_SHIP_SECONDARY_BANKS)
2454 			{
2455 				Warning(LOCATION, "$Allowed Dogfight SBanks bank-specific loadout for %s exceeds permissible number of secondary banks.  Ignoring the rest...", sip->name);
2456 				bank--;
2457 				break;
2458 			}
2459 
2460 			num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
2461 
2462 			// actually say which weapons are allowed
2463 			for ( i = 0; i < num_allowed; i++ )
2464 			{
2465 				if ( allowed_weapons[i] >= 0 )		// MK, Bug fix, 9/6/99.  Used to be "allowed_weapons" not "allowed_weapons[i]".
2466 				{
2467 					sip->allowed_bank_restricted_weapons[MAX_SHIP_PRIMARY_BANKS+bank][allowed_weapons[i]] |= DOGFIGHT_WEAPON;
2468 				}
2469 			}
2470 		}
2471 
2472 		// set flags if need be
2473 		if (bank > 0)	// meaning there was a restricted bank table entry
2474 		{
2475 			for (i=0; i<=bank; i++)
2476 			{
2477 				sip->restricted_loadout_flag[MAX_SHIP_PRIMARY_BANKS+i] |= DOGFIGHT_WEAPON;
2478 			}
2479 		}
2480 	}
2481 
2482 	// Get secondary bank weapons
2483 	parse_weapon_bank(sip, false, &sip->num_secondary_banks, sip->secondary_bank_weapons, sip->secondary_bank_ammo_capacity);
2484 
2485 	if(optional_string("$Show Secondary Models:"))
2486 	{
2487 		sip->flags2 |= SIF2_DRAW_WEAPON_MODELS;
2488 		stuff_bool_list(sip->draw_secondary_models, sip->num_secondary_banks);
2489 	}
2490 
2491 	if(optional_string("$Shields:")) {
2492 		stuff_float(&sip->max_shield_strength);
2493 
2494 		if(optional_string("+Auto Spread:")) {
2495 			stuff_float(&sip->auto_shield_spread);
2496 		}
2497 		if(optional_string("+Allow Bypass:")) {
2498 			stuff_boolean(&sip->auto_shield_spread_bypass);
2499 		}
2500 		if(optional_string("+Spread From LOD:")) {
2501 			int temp;
2502 			stuff_int(&temp);
2503 
2504 			if (temp > sip->num_detail_levels)
2505 				Warning(LOCATION, "+Spread From LOD for %s was %i whereas ship only has %i detail levels, ignoring...", sip->name, temp, sip->num_detail_levels);
2506 			else
2507 				sip->auto_shield_spread_from_lod = temp;
2508 		}
2509 	}
2510 
2511 	if(optional_string("$Model Point Shield Controls:")) {
2512 		SCP_vector<SCP_string> ctrl_strings;
2513 		int num_strings = stuff_string_list(ctrl_strings);
2514 
2515 		// Init all to -1 in case some aren't supplied...
2516 		sip->shield_point_augment_ctrls[FRONT_QUAD] = -1;
2517 		sip->shield_point_augment_ctrls[REAR_QUAD] = -1;
2518 		sip->shield_point_augment_ctrls[LEFT_QUAD] = -1;
2519 		sip->shield_point_augment_ctrls[RIGHT_QUAD] = -1;
2520 
2521 		for (i = 0; i < num_strings; i++) {
2522 			const char *str = ctrl_strings[i].c_str();
2523 
2524 			if (!stricmp(str, "front"))
2525 				sip->shield_point_augment_ctrls[FRONT_QUAD] = i;
2526 			else if (!stricmp(str, "rear"))
2527 				sip->shield_point_augment_ctrls[REAR_QUAD] = i;
2528 			else if (!stricmp(str, "left"))
2529 				sip->shield_point_augment_ctrls[LEFT_QUAD] = i;
2530 			else if (!stricmp(str, "right"))
2531 				sip->shield_point_augment_ctrls[RIGHT_QUAD] = i;
2532 			else if (!stricmp(str, "none"))
2533 				sip->shield_point_augment_ctrls[RIGHT_QUAD] = -1;
2534 			else
2535 				Warning(LOCATION, "Unrecognized value \"%s\" passed to $Model Point Shield Controls, ignoring...", str);
2536 		}
2537 	}
2538 
2539 	// optional shield color
2540 	if(optional_string("$Shield Color:")){
2541 		stuff_ubyte(&sip->shield_color[0]);
2542 		stuff_ubyte(&sip->shield_color[1]);
2543 		stuff_ubyte(&sip->shield_color[2]);
2544 	}
2545 
2546 	// The next five fields are used for the ETS
2547 	if (optional_string("$Power Output:"))
2548 		stuff_float(&sip->power_output);
2549 
2550 	// Goober5000
2551 	if (optional_string("$Shield Regeneration Rate:"))
2552 		stuff_float(&sip->max_shield_regen_per_second);
2553 	else if (first_time)
2554 		sip->max_shield_regen_per_second = 0.02f;
2555 
2556 	// Support ship hull shield rate - if allowed
2557 	if(optional_string("$Support Shield Repair Rate:"))
2558 	{
2559 		stuff_float(&sip->sup_shield_repair_rate);
2560 		sip->sup_shield_repair_rate *= 0.01f;
2561 		CLAMP(sip->sup_shield_repair_rate, 0.0f, 1.0f);
2562 	}
2563 
2564 	// Goober5000
2565 	if (optional_string("$Weapon Regeneration Rate:"))
2566 		stuff_float(&sip->max_weapon_regen_per_second);
2567 	else if (first_time)
2568 		sip->max_weapon_regen_per_second = 0.04f;
2569 
2570 	if (optional_string("$Max Oclk Speed:") || optional_string("$Max Overclock Speed:"))
2571 		stuff_float(&sip->max_overclocked_speed);
2572 	else if (first_time)
2573 		sip->max_overclocked_speed = sip->max_vel.xyz.z * 1.5f;
2574 
2575 	if (optional_string("$Max Weapon Eng:") || optional_string("$Max Weapon Energy:"))
2576 		stuff_float(&sip->max_weapon_reserve);
2577 
2578 	if(optional_string("$Hitpoints:"))
2579 	{
2580 		stuff_float(&sip->max_hull_strength);
2581 		if (sip->max_hull_strength < 0.0f)
2582 		{
2583 			Warning(LOCATION, "Max hull strength on ship %s cannot be less than 0.  Defaulting to 100.\n", sip->name, sip->max_hull_strength);
2584 			sip->max_hull_strength = 100.0f;
2585 		}
2586 	}
2587 
2588 	//Hull rep rate
2589 
2590 	if(optional_string("$Hull Repair Rate:"))
2591 	{
2592 		stuff_float(&sip->hull_repair_rate);
2593 		sip->hull_repair_rate *= 0.01f;
2594 
2595 		//Sanity checking
2596 		if(sip->hull_repair_rate > 1.0f)
2597 			sip->hull_repair_rate = 1.0f;
2598 		else if(sip->hull_repair_rate < -1.0f)
2599 			sip->hull_repair_rate = -1.0f;
2600 	}
2601 
2602 	// Support ship hull repair rate - if allowed
2603 	if(optional_string("$Support Hull Repair Rate:"))
2604 	{
2605 		stuff_float(&sip->sup_hull_repair_rate);
2606 		sip->sup_hull_repair_rate *= 0.01f;
2607 		CLAMP(sip->sup_hull_repair_rate, 0.0f, 1.0f);
2608 	}
2609 
2610 	//Subsys rep rate
2611 	if(optional_string("$Subsystem Repair Rate:"))
2612 	{
2613 		stuff_float(&sip->subsys_repair_rate);
2614 		sip->subsys_repair_rate *= 0.01f;
2615 
2616 		//Sanity checking
2617 		if(sip->subsys_repair_rate > 1.0f)
2618 			sip->subsys_repair_rate = 1.0f;
2619 		else if(sip->subsys_repair_rate < -1.0f)
2620 			sip->subsys_repair_rate = -1.0f;
2621 	}
2622 
2623 	// Support ship hull repair rate
2624 	if(optional_string("$Support Subsystem Repair Rate:"))
2625 	{
2626 		stuff_float(&sip->sup_subsys_repair_rate);
2627 		sip->sup_subsys_repair_rate *= 0.01f;
2628 		CLAMP(sip->sup_subsys_repair_rate, 0.0f, 1.0f);
2629 	}
2630 
2631 	if(optional_string("$Armor Type:"))
2632 	{
2633 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
2634 		sip->armor_type_idx = armor_type_get_idx(buf);
2635 
2636 		if(sip->armor_type_idx == -1)
2637 			Warning(LOCATION,"Invalid armor name %s specified for hull in ship class %s", buf, sip->name);
2638 	}
2639 
2640 	if(optional_string("$Shield Armor Type:"))
2641 	{
2642 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
2643 		sip->shield_armor_type_idx = armor_type_get_idx(buf);
2644 
2645 		if(sip->shield_armor_type_idx == -1)
2646 			Warning(LOCATION,"Invalid armor name %s specified for shield in ship class %s", buf, sip->name);
2647 	}
2648 
2649 	if (optional_string("$Flags:"))
2650 	{
2651 		// we'll assume the list will contain no more than 20 distinct tokens
2652 		char ship_strings[20][NAME_LENGTH];
2653 		int num_strings = stuff_string_list(ship_strings, 20);
2654 
2655 		int ship_type_index = -1;
2656 
2657 		if (!optional_string("+noreplace")) {
2658 			// clear flags since we might have a modular table
2659 			// clear only those which are actually set in the flags
2660 			sip->flags = (sip->flags & SIF_MASK);
2661 			sip->flags2 = (sip->flags2 & SIF2_MASK);
2662 		}
2663 
2664 		for (i = 0; i < num_strings; i++)
2665 		{
2666 			// get ship type from ship flags
2667 			char *ship_type = ship_strings[i];
2668 			bool flag_found = false;
2669 
2670 			// Goober5000 - in retail FreeSpace, some ship classes were specified differently
2671 			// in ships.tbl and the ship type array; this patches those differences so that
2672 			// the ship type lookup will work properly
2673 			if (!stricmp(ship_type, "sentrygun"))
2674 				ship_type = "sentry gun";
2675 			else if (!stricmp(ship_type, "escapepod"))
2676 				ship_type = "escape pod";
2677 			else if (!stricmp(ship_type, "repair_rearm"))
2678 				ship_type = "support";
2679 			else if (!stricmp(ship_type, "supercap"))
2680 				ship_type = "super cap";
2681 			else if (!stricmp(ship_type, "knossos"))
2682 				ship_type = "knossos device";
2683 
2684 			// look it up in the object types table
2685 			ship_type_index = ship_type_name_lookup(ship_type);
2686 
2687 			// set ship class type
2688 			if ((ship_type_index >= 0) && (sip->class_type < 0))
2689 				sip->class_type = ship_type_index;
2690 
2691 			// check various ship flags
2692 			for (int idx = 0; idx < Num_ship_flags; idx++) {
2693 				if ( !stricmp(Ship_flags[idx].name, ship_strings[i]) ) {
2694 					flag_found = true;
2695 
2696 					if (Ship_flags[idx].var == 255)
2697 						Warning(LOCATION, "Use of '%s' flag for ship '%s' - this flag is no longer needed.", Ship_flags[idx].name, sip->name);
2698 					else if (Ship_flags[idx].var == 0)
2699 						sip->flags |= Ship_flags[idx].def;
2700 					else if (Ship_flags[idx].var == 1)
2701 						sip->flags2 |= Ship_flags[idx].def;
2702 				}
2703 			}
2704 
2705 			if ( !flag_found && (ship_type_index < 0) )
2706 				Warning(LOCATION, "Bogus string in ship flags: %s\n", ship_strings[i]);
2707 		}
2708 
2709 		// set original status of tech database flags - Goober5000
2710 		if (sip->flags & SIF_IN_TECH_DATABASE)
2711 			sip->flags2 |= SIF2_DEFAULT_IN_TECH_DATABASE;
2712 		if (sip->flags & SIF_IN_TECH_DATABASE_M)
2713 			sip->flags2 |= SIF2_DEFAULT_IN_TECH_DATABASE_M;
2714 	}
2715 
2716 	// Goober5000 - ensure number of banks checks out
2717 	if (sip->num_primary_banks > MAX_SHIP_PRIMARY_BANKS)
2718 	{
2719 		Error(LOCATION, "Ship Class %s has too many primary banks (%d).  Maximum for ships is currently %d.\n", sip->name, sip->num_primary_banks, MAX_SHIP_PRIMARY_BANKS);
2720 	}
2721 
2722 	// copy to regular allowed_weapons array
2723 	for (i=0; i<MAX_SHIP_WEAPONS; i++)
2724 	{
2725 		for (j=0; j<MAX_WEAPON_TYPES; j++)
2726 		{
2727 			if (sip->allowed_bank_restricted_weapons[i][j] & REGULAR_WEAPON)
2728 				sip->allowed_weapons[j] |= REGULAR_WEAPON;
2729 
2730 			if (sip->allowed_bank_restricted_weapons[i][j] & DOGFIGHT_WEAPON)
2731 				sip->allowed_weapons[j] |= DOGFIGHT_WEAPON;
2732 		}
2733 	}
2734 
2735 	//Set ship ballistic flag if necessary
2736 	for (i=0; i<MAX_SHIP_PRIMARY_BANKS; i++)
2737 	{
2738 		for (j=0; j<MAX_WEAPON_TYPES; j++)
2739 		{
2740 			if(sip->allowed_bank_restricted_weapons[i][j] && (Weapon_info[j].wi_flags2 & WIF2_BALLISTIC))
2741 			{
2742 				sip->flags |= SIF_BALLISTIC_PRIMARIES;
2743 				break;
2744 			}
2745 		}
2746 	}
2747 
2748 	find_and_stuff_optional("$AI Class:", &sip->ai_class, F_NAME, Ai_class_names, Num_ai_classes, "AI class names");
2749 
2750 	// Get Afterburner information
2751 	// Be aware that if $Afterburner is not 1, the other Afterburner fields are not read in
2752 	int has_afterburner = 0;
2753 
2754 	if(optional_string("$Afterburner:"))
2755 		stuff_boolean(&has_afterburner);
2756 
2757 	if ( has_afterburner == 1 )
2758 	{
2759 		sip->flags |= SIF_AFTERBURNER;
2760 
2761 		if(optional_string("+Aburn Max Vel:")) {
2762 			stuff_vec3d(&sip->afterburner_max_vel);
2763 		}
2764 
2765 		if(optional_string("+Aburn For accel:")) {
2766 			stuff_float(&sip->afterburner_forward_accel);
2767 		}
2768 
2769 		// SparK: added reverse burner capability
2770 		if(optional_string("+Aburn Max Reverse Vel:")) {
2771 			stuff_float(&sip->afterburner_max_reverse_vel);
2772 		}
2773 		if(optional_string("+Aburn Rev accel:")) {
2774 			stuff_float(&sip->afterburner_reverse_accel);
2775 		}
2776 
2777 		if(optional_string("+Aburn Fuel:")) {
2778 			stuff_float(&sip->afterburner_fuel_capacity);
2779 		}
2780 
2781 		if(optional_string("+Aburn Burn Rate:")) {
2782 			stuff_float(&sip->afterburner_burn_rate);
2783 		}
2784 
2785 		if(optional_string("+Aburn Rec Rate:")) {
2786 			stuff_float(&sip->afterburner_recover_rate);
2787 		}
2788 
2789 		if (!(sip->afterburner_fuel_capacity) ) {
2790 			Warning(LOCATION, "Ship class %s has an afterburner but has no afterburner fuel. Setting fuel to 1", sip->name);
2791 			sip->afterburner_fuel_capacity = 1.0f;
2792 		}
2793 	}
2794 
2795 	if ( optional_string("$Trails:") ) {
2796 		bool trails_warning = true;
2797 
2798 		if (optional_string("+Bitmap:") ) {
2799 			trails_warning = false;
2800 			generic_bitmap_init(&sip->afterburner_trail, NULL);
2801 			stuff_string(sip->afterburner_trail.filename, F_NAME, MAX_FILENAME_LEN);
2802 		}
2803 
2804 		if ( optional_string("+Width:") ) {
2805 			trails_warning = false;
2806 			stuff_float(&sip->afterburner_trail_width_factor);
2807 		}
2808 
2809 		if ( optional_string("+Alpha:") ) {
2810 			trails_warning = false;
2811 			stuff_float(&sip->afterburner_trail_alpha_factor);
2812 		}
2813 
2814 		if ( optional_string("+Life:") ) {
2815 			trails_warning = false;
2816 			stuff_float(&sip->afterburner_trail_life);
2817 		}
2818 
2819 		if ( optional_string("+Faded Out Sections:") ) {
2820 			trails_warning = false;
2821 			stuff_int(&sip->afterburner_trail_faded_out_sections);
2822 		}
2823 
2824 		if (trails_warning)
2825 			Warning(LOCATION, "Ship %s entry has $Trails field specified, but no properties given.", sip->name);
2826 	}
2827 
2828 	if (optional_string("$Countermeasure type:")) {
2829 		stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
2830 		int res = weapon_info_lookup(buf);
2831 		if (res < 0) {
2832 			Warning(LOCATION, "Could not find weapon type '%s' to use as countermeasure on ship class '%s'", buf, sip->name);
2833 		} else if (Weapon_info[res].wi_flags & WIF_BEAM) {
2834 			Warning(LOCATION, "Attempt made to set a beam weapon as a countermeasure on ship class '%s'", sip->name);
2835 		} else {
2836 			sip->cmeasure_type = res;
2837 		}
2838 	} else if (Species_info[sip->species].cmeasure_index >= 0) {
2839 		sip->cmeasure_type = Species_info[sip->species].cmeasure_index;
2840 	}
2841 
2842 	if(optional_string("$Countermeasures:"))
2843 		stuff_int(&sip->cmeasure_max);
2844 
2845 	if(optional_string("$Scan time:"))
2846 		stuff_int(&sip->scan_time);
2847 
2848 	//Parse the engine sound
2849 	parse_sound("$EngineSnd:", &sip->engine_snd, sip->name);
2850 
2851 	//Parse optional sound to be used for beginning of a glide
2852 	parse_sound("$GlideStartSnd:", &sip->glide_start_snd, sip->name);
2853 
2854 	//Parse optional sound to be used for end of a glide
2855 	parse_sound("$GlideEndSnd:", &sip->glide_end_snd, sip->name);
2856 
2857 	parse_ship_sounds(sip);
2858 
2859 	if(optional_string("$Closeup_pos:"))
2860 	{
2861 		stuff_vec3d(&sip->closeup_pos);
2862 	}
2863 	else if (first_time && strlen(sip->pof_file))
2864 	{
2865 		//Calculate from the model file. This is inefficient, but whatever
2866 		int model_idx = model_load(sip->pof_file, 0, NULL);
2867 		polymodel *pm = model_get(model_idx);
2868 
2869 		//Go through, find best
2870 		sip->closeup_pos.xyz.z = fabsf(pm->maxs.xyz.z);
2871 
2872 		float temp = fabsf(pm->mins.xyz.z);
2873 		if(temp > sip->closeup_pos.xyz.z)
2874 			sip->closeup_pos.xyz.z = temp;
2875 
2876 		//Now multiply by 2
2877 		sip->closeup_pos.xyz.z *= -2.0f;
2878 
2879 		//We're done with the model.
2880 		model_unload(model_idx);
2881 	}
2882 
2883 	if(optional_string("$Closeup_zoom:"))
2884 		stuff_float(&sip->closeup_zoom);
2885 
2886 	if(optional_string("$Topdown offset:")) {
2887 		sip->topdown_offset_def = true;
2888 		stuff_vec3d(&sip->topdown_offset);
2889 	}
2890 
2891 	if (optional_string("$Shield_icon:")) {
2892 		stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
2893 		hud_shield_assign_info(sip, name_tmp);
2894 	}
2895 
2896 	// read in filename for icon that is used in ship selection
2897 	if ( optional_string("$Ship_icon:") ) {
2898 		stuff_string(sip->icon_filename, F_NAME, MAX_FILENAME_LEN);
2899 	}
2900 
2901 	// read in filename for animation that is used in ship selection
2902 	if ( optional_string("$Ship_anim:") ) {
2903 		stuff_string(sip->anim_filename, F_NAME, MAX_FILENAME_LEN);
2904 	}
2905 
2906 	// read in filename for animation that is used in ship selection
2907 	if ( optional_string("$Ship_overhead:") ) {
2908 		stuff_string(sip->overhead_filename, F_NAME, MAX_FILENAME_LEN);
2909 	}
2910 
2911 	// read in briefing stuff
2912 	if ( optional_string("$Briefing icon:") )
2913 		sip->bii_index_ship = parse_and_add_briefing_icon_info();
2914 	if ( optional_string("$Briefing icon with cargo:") )
2915 		sip->bii_index_ship_with_cargo = parse_and_add_briefing_icon_info();
2916 	if ( optional_string("$Briefing wing icon:") )
2917 		sip->bii_index_wing = parse_and_add_briefing_icon_info();
2918 	if ( optional_string("$Briefing wing icon with cargo:") )
2919 		sip->bii_index_wing_with_cargo = parse_and_add_briefing_icon_info();
2920 
2921 	// check for inconsistencies
2922 	if ((sip->bii_index_wing_with_cargo >= 0) && (sip->bii_index_wing < 0 || sip->bii_index_ship_with_cargo < 0))
2923 		Warning(LOCATION, "Ship '%s' has a wing-with-cargo briefing icon but is missing a wing briefing icon or a ship-with-cargo briefing icon!", sip->name);
2924 	if ((sip->bii_index_wing_with_cargo < 0) && (sip->bii_index_wing >= 0) && (sip->bii_index_ship_with_cargo >= 0))
2925 		Warning(LOCATION, "Ship '%s' has both a wing briefing icon and a ship-with-cargo briefing icon but does not have a wing-with-cargo briefing icon!", sip->name);
2926 
2927 	if ( optional_string("$Score:") ){
2928 		stuff_int( &sip->score );
2929 	}
2930 
2931 	if (first_time)
2932 	{
2933 		species_info *species = &Species_info[sip->species];
2934 
2935 		sip->thruster_flame_info = species->thruster_info.flames;
2936 		sip->thruster_glow_info = species->thruster_info.glow;
2937 		sip->thruster_secondary_glow_info = species->thruster_secondary_glow_info;
2938 		sip->thruster_tertiary_glow_info = species->thruster_tertiary_glow_info;
2939 		sip->thruster_distortion_info = species->thruster_distortion_info;
2940 	}
2941 
2942 	if ( optional_string("$Thruster Normal Flame:") ) {
2943 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
2944 
2945 		if ( VALID_FNAME(name_tmp) )
2946 			generic_anim_init( &sip->thruster_flame_info.normal, name_tmp );
2947 	}
2948 
2949 	if ( optional_string("$Thruster Afterburner Flame:") ) {
2950 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
2951 
2952 		if ( VALID_FNAME(name_tmp) )
2953 			generic_anim_init( &sip->thruster_flame_info.afterburn, name_tmp );
2954 	}
2955 
2956 	if ( optional_string("$Thruster Bitmap 1:") ) {
2957 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
2958 
2959 		if ( VALID_FNAME(name_tmp) ) {
2960 			strcpy_s(sip->thruster_glow_info.normal.filename, name_tmp);
2961 			thruster_glow_anim_load( &sip->thruster_glow_info.normal );
2962 		}
2963 	}
2964 
2965 	if ( optional_string("$Thruster Bitmap 1a:") ) {
2966 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
2967 
2968 		if ( VALID_FNAME(name_tmp) ) {
2969 			strcpy_s(sip->thruster_glow_info.afterburn.filename, name_tmp);
2970 			thruster_glow_anim_load( &sip->thruster_glow_info.afterburn );
2971 		}
2972 	}
2973 
2974 	if ( optional_string("$Thruster01 Radius factor:") ) {
2975 		stuff_float(&sip->thruster01_glow_rad_factor);
2976 	}
2977 
2978 	if ( optional_string("$Thruster Bitmap 2:") ) {
2979 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
2980 
2981 		if ( VALID_FNAME(name_tmp) )
2982 			generic_bitmap_init( &sip->thruster_secondary_glow_info.normal, name_tmp );
2983 	}
2984 
2985 	if ( optional_string("$Thruster Bitmap 2a:") ) {
2986 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
2987 
2988 		if ( VALID_FNAME(name_tmp) )
2989 			generic_bitmap_init( &sip->thruster_secondary_glow_info.afterburn, name_tmp );
2990 	}
2991 
2992 	if ( optional_string("$Thruster02 Radius factor:") ) {
2993 		stuff_float(&sip->thruster02_glow_rad_factor);
2994 	}
2995 
2996 	if ( optional_string("$Thruster01 Length factor:") ) {
2997 		stuff_float(&sip->thruster02_glow_len_factor);
2998 		Warning(LOCATION, "Deprecated spelling: \"$Thruster01 Length factor:\".  Use \"$Thruster02 Length factor:\" instead.");
2999 	}
3000 
3001 	if ( optional_string("$Thruster02 Length factor:") ) {
3002 		stuff_float(&sip->thruster02_glow_len_factor);
3003 	}
3004 
3005 	if ( optional_string("$Thruster Bitmap 3:") ) {
3006 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3007 
3008 		if ( VALID_FNAME(name_tmp) )
3009 			generic_bitmap_init( &sip->thruster_tertiary_glow_info.normal, name_tmp );
3010 	}
3011 
3012 	if ( optional_string("$Thruster Bitmap 3a:") ) {
3013 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3014 
3015 		if ( VALID_FNAME(name_tmp) )
3016 			generic_bitmap_init( &sip->thruster_tertiary_glow_info.afterburn, name_tmp );
3017 	}
3018 
3019 	if ( optional_string("$Thruster03 Radius factor:") ) {
3020 		stuff_float(&sip->thruster03_glow_rad_factor);
3021 	}
3022 
3023 	// Valathil - Custom Thruster Distortion
3024 	if ( optional_string("$Thruster Bitmap Distortion:") ) {
3025 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3026 
3027 		if ( VALID_FNAME(name_tmp) )
3028 			generic_bitmap_init( &sip->thruster_distortion_info.normal, name_tmp );
3029 	}
3030 
3031 	if ( optional_string("$Thruster Bitmap Distortion a:") ) {
3032 		stuff_string( name_tmp, F_NAME, sizeof(name_tmp) );
3033 
3034 		if ( VALID_FNAME(name_tmp) )
3035 			generic_bitmap_init( &sip->thruster_distortion_info.afterburn, name_tmp );
3036 	}
3037 
3038 	if ( optional_string("$Thruster Distortion Radius factor:") ) {
3039 		stuff_float(&sip->thruster_dist_rad_factor);
3040 	}
3041 
3042 	if ( optional_string("$Thruster Distortion Length factor:") ) {
3043 		stuff_float(&sip->thruster_dist_len_factor);
3044 	}
3045 
3046 	if ( optional_string("$Thruster Distortion:") ) {
3047 		stuff_boolean(&sip->draw_distortion);
3048 	}
3049 
3050 	while ( optional_string("$Thruster Particles:") ) {
3051 		bool afterburner = false;
3052 		thruster_particles tpart;
3053 
3054 		if ( optional_string("$Thruster Particle Bitmap:") )
3055 			afterburner = false;
3056 		else if ( optional_string("$Afterburner Particle Bitmap:") )
3057 			afterburner = true;
3058 		else
3059 			Error( LOCATION, "formatting error in the thruster's particle section for ship %s\n", sip->name );
3060 
3061 		generic_anim_init(&tpart.thruster_bitmap, NULL);
3062 		stuff_string(tpart.thruster_bitmap.filename, F_NAME, MAX_FILENAME_LEN);
3063 
3064 		required_string("$Min Radius:");
3065 		stuff_float(&tpart.min_rad);
3066 
3067 		required_string("$Max Radius:");
3068 		stuff_float(&tpart.max_rad);
3069 
3070 		required_string("$Min created:");
3071 		stuff_int(&tpart.n_low);
3072 
3073 		required_string("$Max created:");
3074 		stuff_int(&tpart.n_high);
3075 
3076 		required_string("$Variance:");
3077 		stuff_float(&tpart.variance);
3078 
3079 		if (afterburner) {
3080 			sip->afterburner_thruster_particles.push_back( tpart );
3081 		} else {
3082 			sip->normal_thruster_particles.push_back( tpart );
3083 		}
3084 	}
3085 
3086 	// if the ship is a stealth ship
3087 	if ( optional_string("$Stealth:") ) {
3088 		sip->flags |= SIF_STEALTH;
3089 	}
3090 
3091 	else if ( optional_string("$Stealth") ) {
3092 		Warning(LOCATION, "Ship %s is missing the colon after \"$Stealth\". Note that you may also use the ship flag \"stealth\".", sip->name);
3093 		sip->flags |= SIF_STEALTH;
3094 	}
3095 
3096 	if ( optional_string("$max decals:") ){
3097 		int bogus;
3098 		stuff_int(&bogus);
3099 		WarningEx(LOCATION, "The decal system has been deactivated in FSO builds. Entries will be discarded.\n");
3100 		mprintf(("WARNING: The decal system has been deactivated in FSO builds. Entries will be discarded.\n"));
3101 		//Do nothing, left in for compatibility.
3102 	}
3103 
3104 	// parse contrail info
3105 	while ( optional_string("$Trail:") ) {
3106 		// setting '+ClearAll' resets the trails
3107 		if ( optional_string("+ClearAll")) {
3108 			memset(&sip->ct_info, 0, sizeof(trail_info) * MAX_SHIP_CONTRAILS);
3109 			sip->ct_count = 0;
3110 		}
3111 
3112 		// this means you've reached the max # of contrails for a ship
3113 		if (sip->ct_count >= MAX_SHIP_CONTRAILS) {
3114 			Warning(LOCATION, "%s has more contrails than the max of %d", sip->name, MAX_SHIP_CONTRAILS);
3115 			break;
3116 		}
3117 
3118 		trail_info *ci = &sip->ct_info[sip->ct_count++];
3119 
3120 		required_string("+Offset:");
3121 		stuff_vec3d(&ci->pt);
3122 
3123 		required_string("+Start Width:");
3124 		stuff_float(&ci->w_start);
3125 
3126 		required_string("+End Width:");
3127 		stuff_float(&ci->w_end);
3128 
3129 		required_string("+Start Alpha:");
3130 		stuff_float(&ci->a_start);
3131 
3132 		required_string("+End Alpha:");
3133 		stuff_float(&ci->a_end);
3134 
3135 		required_string("+Max Life:");
3136 		stuff_float(&ci->max_life);
3137 
3138 		required_string("+Spew Time:");
3139 		stuff_int(&ci->stamp);
3140 
3141 		required_string("+Bitmap:");
3142 		stuff_string(name_tmp, F_NAME, NAME_LENGTH);
3143 		generic_bitmap_init(&ci->texture, name_tmp);
3144 		generic_bitmap_load(&ci->texture);
3145 
3146 		if (optional_string("+Faded Out Sections:") ) {
3147 			stuff_int(&ci->n_fade_out_sections);
3148 		}
3149 	}
3150 
3151 	man_thruster *mtp = NULL;
3152 	man_thruster manwich;
3153 	while(optional_string("$Thruster:"))
3154 	{
3155 		int idx = -1;
3156 		if(optional_string("+index:")) {
3157 			stuff_int(&idx);
3158 		}
3159 
3160 		if(idx >= 0 && idx < sip->num_maneuvering) {
3161 			mtp = &sip->maneuvering[idx];
3162 		} else if(idx < 0) {
3163 			if(sip->num_maneuvering < MAX_MAN_THRUSTERS) {
3164 				mtp = &sip->maneuvering[sip->num_maneuvering++];
3165 			} else {
3166 				Warning(LOCATION, "Too many maneuvering thrusters on ship '%s'; maximum is %d", sip->name, MAX_MAN_THRUSTERS);
3167 			}
3168 		} else {
3169 			mtp = &manwich;
3170 			Warning(LOCATION, "Invalid index (%d) specified for maneuvering thruster on ship %s", idx, sip->name);
3171 		}
3172 
3173 		if(optional_string("+Used for:")) {
3174 			parse_string_flag_list(&mtp->use_flags, Man_types, Num_man_types);
3175 		}
3176 
3177 		if(optional_string("+Position:")) {
3178 			stuff_float_list(mtp->pos.a1d, 3);
3179 		}
3180 
3181 		if(optional_string("+Normal:")) {
3182 			stuff_float_list(mtp->norm.a1d, 3);
3183 		}
3184 
3185 		if(optional_string("+Texture:"))
3186 		{
3187 			stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
3188 			int tex_fps=0, tex_nframes=0, tex_id=-1;;
3189 			tex_id = bm_load_animation(name_tmp, &tex_nframes, &tex_fps, NULL, 1);
3190 			if(tex_id < 0)
3191 				tex_id = bm_load(name_tmp);
3192 			if(tex_id >= 0)
3193 			{
3194 				if(mtp->tex_id >= 0) {
3195 					bm_unload(mtp->tex_id);
3196 				}
3197 
3198 				mtp->tex_id = tex_id;
3199 				mtp->tex_fps = tex_fps;
3200 				mtp->tex_nframes = tex_nframes;
3201 			}
3202 		}
3203 
3204 		if(optional_string("+Radius:")) {
3205 			stuff_float(&mtp->radius);
3206 		}
3207 
3208 		if(optional_string("+Length:")) {
3209 			stuff_float(&mtp->length);
3210 		}
3211 
3212 		parse_sound("+StartSnd:", &mtp->start_snd, sip->name);
3213 		parse_sound("+LoopSnd:", &mtp->loop_snd, sip->name);
3214 		parse_sound("+StopSnd:", &mtp->stop_snd, sip->name);
3215 	}
3216 
3217 	if (optional_string("$Radar Image 2D:"))
3218 	{
3219 		stuff_string(name_tmp, F_NAME, NAME_LENGTH);
3220 		sip->radar_image_2d_idx = bm_load(name_tmp);
3221 
3222 		if ( optional_string("$Radar Color Image 2D:") ) {
3223 			stuff_string(name_tmp, F_NAME, NAME_LENGTH);
3224 			sip->radar_color_image_2d_idx = bm_load(name_tmp);
3225 		}
3226 
3227 		if (optional_string("$Radar Image Size:"))
3228 			stuff_int(&sip->radar_image_size);
3229 
3230 		if (optional_string("$3D Radar Blip Size Multiplier:"))
3231 			stuff_float(&sip->radar_projection_size_mult);
3232 	}
3233 
3234 	// Alternate - per ship class - IFF colors
3235 	while((optional_string("$Ship IFF Colors:")) || (optional_string("$Ship IFF Colours:")))
3236 	{
3237 		char iff_1[NAME_LENGTH];
3238 		char iff_2[NAME_LENGTH];
3239 		int iff_color_data[3];
3240 		int iff_data[2];
3241 
3242 		// Get the iff strings and get the iff indexes
3243 		required_string("+Seen By:");
3244 		stuff_string(iff_1, F_NAME, NAME_LENGTH);
3245 
3246 		required_string("+When IFF Is:");
3247 		stuff_string(iff_2, F_NAME, NAME_LENGTH);
3248 		iff_data[0] = iff_lookup(iff_1);
3249 		iff_data[1] = iff_lookup(iff_2);
3250 
3251 		if (iff_data[0] == -1)
3252 			WarningEx(LOCATION, "Ship %s\nIFF colour seen by \"%s\" invalid!", sip->name, iff_1);
3253 
3254 		if (iff_data[1] == -1)
3255 			WarningEx(LOCATION, "Ship %s\nIFF colour when IFF is \"%s\" invalid!", sip->name, iff_2);
3256 
3257 		// Set the color
3258 		required_string("+As Color:");
3259 		stuff_int_list(iff_color_data, 3, RAW_INTEGER_TYPE);
3260 		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]);
3261 	}
3262 
3263 	if (optional_string("$Target Priority Groups:") ) {
3264 		SCP_vector<SCP_string> target_group_strings;
3265 		int num_strings = stuff_string_list(target_group_strings);
3266 		int num_groups = Ai_tp_list.size();
3267 		int k;
3268 		bool override_strings = false;
3269 
3270 		if (optional_string("+Override")) {
3271 			override_strings = true;
3272 		}
3273 
3274 		for(j = 0; j < num_strings; j++) {
3275 			for(i = 0; i < num_groups; i++) {
3276 				if ( !stricmp(target_group_strings[j].c_str(), Ai_tp_list[i].name) ) {
3277 					//so now the string from the list above as well as the ai priority group name match
3278 					//clear it if override has been set
3279 					if (override_strings) {
3280 						Ai_tp_list[i].ship_class.clear();
3281 						override_strings = false;
3282 					}
3283 					for (k = 0; k < Num_ship_classes; k++) {
3284 						//find the index number of the current ship info type
3285 						if (Ship_info[k].name == sip->name) {
3286 							Ai_tp_list[i].ship_class.push_back(k);
3287 							break;
3288 						}
3289 					}
3290 					// found something, try next string
3291 					break;
3292 				}
3293 			}
3294 			if (i == num_groups) {
3295 				Warning(LOCATION,"Unidentified priority group '%s' set for ship class '%s'\n", target_group_strings[j].c_str(), sip->name);
3296 			}
3297 		}
3298 	}
3299 
3300 	if (optional_string("$EMP Resistance Modifier:")) {
3301 		stuff_float(&sip->emp_resistance_mod);
3302 	}
3303 
3304 	if (optional_string("$Piercing Damage Draw Limit:")) {
3305 		float tempf;
3306 		stuff_float(&tempf);
3307 		sip->piercing_damage_draw_limit = tempf / 100.0f;
3308 	}
3309 
3310 	while(optional_string("$Path Metadata:"))
3311 	{
3312 		char path_name[64];
3313 		stuff_string(path_name, F_NAME, sizeof(path_name));
3314 
3315 		path_metadata metadata;
3316 		init_path_metadata(metadata);
3317 
3318 		//Get +departure rvec and store on the path_metadata object
3319 		if (optional_string("+departure rvec:"))
3320 		{
3321 			stuff_vec3d(&metadata.departure_rvec);
3322 		}
3323 
3324 		//Add the new path_metadata to sip->pathMetadata keyed by path name
3325 		SCP_string pathName(path_name);
3326 		sip->pathMetadata[pathName] = metadata;
3327 	}
3328 
3329 	int n_subsystems = 0;
3330 	int cont_flag = 1;
3331 	model_subsystem subsystems[MAX_MODEL_SUBSYSTEMS];		// see model.h for max_model_subsystems
3332 	for (i=0; i<MAX_MODEL_SUBSYSTEMS; i++) {
3333 		subsystems[i].stepped_rotation = NULL;
3334 	}
3335 
3336 	float	hull_percentage_of_hits = 100.0f;
3337 	//If the ship already has subsystem entries (ie this is a modular table)
3338 	//make sure hull_percentage_of_hits is set properly
3339 	for(i=0; i < sip->n_subsystems; i++) {
3340 		hull_percentage_of_hits -= sip->subsystems[i].max_subsys_strength / sip->max_hull_strength;
3341 	}
3342 
3343 	while (cont_flag) {
3344 		int r = required_string_4("#End", "$Subsystem:", "$Name", "$Template" );
3345 		switch (r) {
3346 		case 0:
3347 			cont_flag = 0;
3348 			break;
3349 		case 1:
3350 		{
3351 			float	turning_rate;
3352 			float	percentage_of_hits;
3353 			bool turret_has_base_fov = false;
3354 			model_subsystem *sp = NULL;			// to append on the ships list of subsystems
3355 
3356 			int sfo_return;
3357 			required_string("$Subsystem:");
3358 			stuff_string(name_tmp, F_NAME, sizeof(name_tmp), ",");
3359 			Mp++;
3360 			for(i = 0;i < sip->n_subsystems; i++)
3361 			{
3362 				if(!subsystem_stricmp(sip->subsystems[i].subobj_name, name_tmp))
3363 					sp = &sip->subsystems[i];
3364 			}
3365 
3366 			if(sp == NULL)
3367 			{
3368 				if( sip->n_subsystems + n_subsystems >= MAX_MODEL_SUBSYSTEMS )
3369 				{
3370 					Warning(LOCATION, "Number of subsystems for ship entry '%s' (%d) exceeds max of %d; only the first %d will be used", sip->name, sip->n_subsystems, n_subsystems, MAX_MODEL_SUBSYSTEMS);
3371 					break;
3372 				}
3373 				sp = &subsystems[n_subsystems++];			// subsystems a local -- when done, we will malloc and copy
3374 				strcpy_s(sp->subobj_name, name_tmp);
3375 
3376 				//Init blank values
3377 				sp->max_subsys_strength = 0.0f;
3378 				sp->turret_turning_rate = 0.0f;
3379 				sp->weapon_rotation_pbank = -1;
3380 
3381 				memset(sp->alt_sub_name, 0, sizeof(sp->alt_sub_name) );
3382 				memset(sp->alt_dmg_sub_name, 0, sizeof(sp->alt_dmg_sub_name) );
3383 
3384 				for (i=0; i<MAX_SHIP_PRIMARY_BANKS; i++) {
3385 					sp->primary_banks[i] = -1;
3386 					sp->primary_bank_capacity[i] = 0;
3387 				}
3388 
3389 				for (i=0; i<MAX_SHIP_SECONDARY_BANKS; i++) {
3390 					sp->secondary_banks[i] = -1;
3391 					sp->secondary_bank_capacity[i] = 0;
3392 				}
3393 
3394 				sp->engine_wash_pointer = NULL;
3395 
3396 				sp->alive_snd = -1;
3397 				sp->dead_snd = -1;
3398 				sp->rotation_snd = -1;
3399 				sp->turret_gun_rotation_snd = -1;
3400 				sp->turret_gun_rotation_snd_mult = 1.0f;
3401 				sp->turret_base_rotation_snd = -1;
3402 				sp->turret_base_rotation_snd_mult = 1.0f;
3403 
3404 				sp->flags = 0;
3405 				sp->flags2 = 0;
3406 
3407 				sp->n_triggers = 0;
3408 				sp->triggers = NULL;
3409 
3410 				sp->model_num = -1;		// init value for later sanity checking!!
3411 				sp->armor_type_idx = -1;
3412 				sp->path_num = -1;
3413 				sp->turret_max_fov = 1.0f;
3414 
3415 				sp->turret_reset_delay = 2000;
3416 
3417 				sp->num_target_priorities = 0;
3418 				for (i = 0; i < 32; i++) {
3419 					sp->target_priority[i] = -1;
3420 				}
3421 				sp->optimum_range = 0.0f;
3422 				sp->favor_current_facing = 0.0f;
3423 
3424 				sp->turret_rof_scaler = 1.0f;
3425 
3426 				sp->turret_max_bomb_ownage = -1;
3427 				sp->turret_max_target_ownage = -1;
3428 			}
3429 			sfo_return = stuff_float_optional(&percentage_of_hits);
3430 			if(sfo_return==2)
3431 			{
3432 				hull_percentage_of_hits -= percentage_of_hits;
3433 				sp->max_subsys_strength = sip->max_hull_strength * (percentage_of_hits / 100.0f);
3434 				sp->type = SUBSYSTEM_UNKNOWN;
3435 			}
3436 			if(sfo_return > 0)
3437 			{
3438 				if(stuff_float_optional(&turning_rate)==2)
3439 				{
3440 					// specified as how long to turn 360 degrees in ships.tbl
3441 					if ( turning_rate > 0.0f ){
3442 						sp->turret_turning_rate = PI2 / turning_rate;
3443 					} else {
3444 						sp->turret_turning_rate = 0.0f;
3445 					}
3446 				}
3447 				else
3448 				{
3449 					Error(LOCATION, "Malformed $Subsystem entry '%s' %s.\n\n"
3450 						"Specify a turning rate or remove the trailing comma.",
3451 						sp->subobj_name, sip->name);
3452 				}
3453 			}
3454 
3455 			if(optional_string("$Alt Subsystem Name:")) {
3456 				stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
3457 				strcpy_s(sp->alt_sub_name, buf);
3458 			}
3459 
3460 			if(optional_string("$Alt Damage Popup Subsystem Name:")) {
3461 				stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
3462 				strcpy_s(sp->alt_dmg_sub_name, buf);
3463 			}
3464 
3465 			if(optional_string("$Armor Type:")) {
3466 				stuff_string(buf, F_NAME, SHIP_MULTITEXT_LENGTH);
3467 				sp->armor_type_idx = armor_type_get_idx(buf);
3468 
3469 				if (sp->armor_type_idx == -1)
3470 					WarningEx(LOCATION, "Ship %s, subsystem %s\nInvalid armor type %s!", sip->name, sp->subobj_name, buf);
3471 			}
3472 
3473 			//	Get primary bank weapons
3474 			parse_weapon_bank(sip, true, NULL, sp->primary_banks, sp->primary_bank_capacity);
3475 
3476 			//	Get secondary bank weapons
3477 			parse_weapon_bank(sip, false, NULL, sp->secondary_banks, sp->secondary_bank_capacity);
3478 
3479 			// Get optional engine wake info
3480 			if (optional_string("$Engine Wash:")) {
3481 				stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
3482 				// get and set index
3483 				sp->engine_wash_pointer = get_engine_wash_pointer(name_tmp);
3484 
3485 				if(sp->engine_wash_pointer == NULL)
3486 					WarningEx(LOCATION,"Invalid engine wash name %s specified for subsystem %s in ship class %s", name_tmp, sp->subobj_name, sip->name);
3487 			}
3488 
3489 			parse_sound("$AliveSnd:", &sp->alive_snd, sp->subobj_name);
3490 			parse_sound("$DeadSnd:", &sp->dead_snd, sp->subobj_name);
3491 			parse_sound("$RotationSnd:", &sp->rotation_snd, sp->subobj_name);
3492 			parse_sound("$Turret Base RotationSnd:", &sp->turret_base_rotation_snd, sp->subobj_name);
3493 			parse_sound("$Turret Gun RotationSnd:", &sp->turret_gun_rotation_snd, sp->subobj_name);
3494 
3495 			if (optional_string("$Turret BaseSnd Volume:"))
3496 				stuff_float(&sp->turret_base_rotation_snd_mult);
3497 
3498 			if (optional_string("$Turret GunSnd Volume:"))
3499 				stuff_float(&sp->turret_gun_rotation_snd_mult);
3500 
3501 			// Get any AWACS info
3502 			sp->awacs_intensity = 0.0f;
3503 			if(optional_string("$AWACS:")){
3504 				sfo_return = stuff_float_optional(&sp->awacs_intensity);
3505 				if(sfo_return > 0)
3506 					stuff_float_optional(&sp->awacs_radius);
3507 				sip->flags |= SIF_HAS_AWACS;
3508 			}
3509 
3510 			if(optional_string("$Maximum Barrel Elevation:")){
3511 				int value;
3512 				stuff_int(&value);
3513 				CAP(value, 0, 90);
3514 				float angle = ANG_TO_RAD((float) (90 - value));
3515 				sp->turret_max_fov = (float)cos(angle);
3516 			}
3517 
3518 			if(optional_string("$Turret Base FOV:")) {
3519 				int value;
3520 				stuff_int(&value);
3521 				CAP(value, 0, 359);
3522 				float angle = ANG_TO_RAD((float) value)/2.0f;
3523 				sp->turret_y_fov = (float)cos(angle);
3524 				turret_has_base_fov = true;
3525 			}
3526 
3527 			if (optional_string("$Turret Reset Delay:"))
3528 				stuff_int(&sp->turret_reset_delay);
3529 
3530 			if (optional_string("$Turret Optimum Range:"))
3531 				stuff_float(&sp->optimum_range);
3532 
3533 			if (optional_string("$Turret Direction Preference:")) {
3534 				int temp;
3535 				stuff_int(&temp);
3536 				if (temp == 0) {
3537 					sp->favor_current_facing = 0.0f;
3538 				} else {
3539 					CAP(temp, 1, 100);
3540 					sp->favor_current_facing = 1.0f + (((float) (100 - temp)) / 10.0f);
3541 				}
3542 			}
3543 
3544 			if (optional_string("$Target Priority:")) {
3545 				SCP_vector <SCP_string> tgt_priorities;
3546 				int num_strings = stuff_string_list(tgt_priorities);
3547 				sp->num_target_priorities = 0;
3548 
3549 				if (num_strings > 32)
3550 					num_strings = 32;
3551 
3552 				int num_groups = Ai_tp_list.size();
3553 
3554 				for(i = 0; i < num_strings; i++) {
3555 					for(j = 0; j < num_groups; j++) {
3556 						if ( !stricmp(Ai_tp_list[j].name, tgt_priorities[i].c_str()))  {
3557 							sp->target_priority[i] = j;
3558 							sp->num_target_priorities++;
3559 							break;
3560 						}
3561 					}
3562 					if (j == num_groups) {
3563 						Warning(LOCATION, "Unidentified target priority '%s' set for\nsubsystem '%s' in ship class '%s'.", tgt_priorities[i].c_str(), sp->subobj_name, sip->name);
3564 					}
3565 				}
3566 			}
3567 
3568 			if (optional_string("$Max Turrets per Bomb:")) {
3569 				stuff_int(&sp->turret_max_bomb_ownage);
3570 			}
3571 
3572 			if (optional_string("$Max Turrets per Target:")) {
3573 				stuff_int(&sp->turret_max_target_ownage);
3574 			}
3575 
3576 			if (optional_string("$ROF:")) {
3577 
3578 				if (optional_string("+Use firingpoints")) {
3579 					sp->turret_rof_scaler = 0;
3580 				} else {
3581 					if (optional_string("+Multiplier:")) {
3582 						float tempf;
3583 						stuff_float(&tempf);
3584 
3585 						if (tempf < 0) {
3586 							mprintf(("RoF multiplier clamped to 0 for subsystem '%s' in ship class '%s'.\n", sp->subobj_name, sip->name));
3587 							sp->turret_rof_scaler = 0;
3588 						} else {
3589 							sp->turret_rof_scaler = tempf;
3590 						}
3591 					} else {
3592 						Warning(LOCATION, "RoF multiplier not set for subsystem\n'%s' in ship class '%s'.", sp->subobj_name, sip->name);
3593 					}
3594 				}
3595 			}
3596 
3597 			if (optional_string("$Flags:")) {
3598 				char flag_strings[Num_subsystem_flags][NAME_LENGTH];
3599 				int num_strings = stuff_string_list(flag_strings, NUM_SUBSYSTEM_FLAGS);
3600 
3601 				if (!optional_string("+noreplace")) {
3602 					// clear flags since we might have a modular table
3603 					// clear only those which are actually set in the flags
3604 					sp->flags = 0;
3605 					sp->flags2 = 0;
3606 				}
3607 
3608 				for (i = 0; i < num_strings; i++)
3609 				{
3610 
3611 					bool flag_found = false;
3612 					// check various subsystem flags
3613 					for (int idx = 0; idx < Num_subsystem_flags; idx++) {
3614 						if ( !stricmp(Subsystem_flags[idx].name, flag_strings[i]) ) {
3615 							flag_found = true;
3616 
3617 							if (Subsystem_flags[idx].var == 0)
3618 								sp->flags |= Subsystem_flags[idx].def;
3619 							else if (Subsystem_flags[idx].var == 1)
3620 								sp->flags2 |= Subsystem_flags[idx].def;
3621 						}
3622 					}
3623 
3624 					if ( !flag_found )
3625 						Warning(LOCATION, "Bogus string in subsystem flags: %s\n", flag_strings[i]);
3626 				}
3627 
3628 				//If we've set any subsystem as landable, set a ship-info flag as a shortcut for later
3629 				if (sp->flags & MSS_FLAG_ALLOW_LANDING)
3630 					sip->flags2 |= SIF2_ALLOW_LANDINGS;
3631 			}
3632 
3633 			if (turret_has_base_fov)
3634 				sp->flags |= MSS_FLAG_TURRET_ALT_MATH;
3635 
3636 			if (optional_string("+non-targetable")) {
3637 				Warning(LOCATION, "Grammar error in table file.  Please change \"+non-targetable\" to \"+untargetable\".");
3638 				sp->flags |= MSS_FLAG_UNTARGETABLE;
3639 			}
3640 
3641 			bool old_flags = false;
3642 			if (optional_string("+untargetable")) {
3643 				sp->flags |= MSS_FLAG_UNTARGETABLE;
3644 				old_flags = true;
3645 			}
3646 
3647 			if (optional_string("+carry-no-damage")) {
3648 				sp->flags |= MSS_FLAG_CARRY_NO_DAMAGE;
3649 				old_flags = true;
3650 			}
3651 
3652 			if (optional_string("+use-multiple-guns")) {
3653 				sp->flags |= MSS_FLAG_USE_MULTIPLE_GUNS;
3654 				old_flags = true;
3655 			}
3656 
3657 			if (optional_string("+fire-down-normals")) {
3658 				sp->flags |= MSS_FLAG_FIRE_ON_NORMAL;
3659 				old_flags = true;
3660 			}
3661 
3662 			if ((sp->flags & MSS_FLAG_TURRET_FIXED_FP) && !(sp->flags & MSS_FLAG_USE_MULTIPLE_GUNS)) {
3663 				Warning(LOCATION, "\"fixed firingpoints\" flag used without \"use multiple guns\" flag on a subsystem on ship %s.\n\"use multiple guns\" flags added by default\n", sip->name);
3664 				sp->flags |= MSS_FLAG_USE_MULTIPLE_GUNS;
3665 			}
3666 
3667 			if (old_flags) {
3668 				mprintf(("Use of deprecated subsystem syntax.  Please use the $Flags: field for subsystem flags.\n\n" \
3669 				"At least one of the following tags was used on ship %s, subsystem %s:\n" \
3670 				"\t+untargetable\n" \
3671 				"\t+carry-no-damage\n" \
3672 				"\t+use-multiple-guns\n" \
3673 				"\t+fire-down-normals\n", sip->name, sp->subobj_name));
3674 			}
3675 
3676 			while(optional_string("$animation:"))
3677 			{
3678 				stuff_string(name_tmp, F_NAME, sizeof(name_tmp));
3679 				if(!stricmp(name_tmp, "triggered"))
3680 				{
3681 					queued_animation *current_trigger;
3682 
3683 					sp->triggers = (queued_animation*)vm_realloc(sp->triggers, sizeof(queued_animation) * (sp->n_triggers + 1));
3684 					Verify(sp->triggers != NULL);
3685 
3686 					//add a new trigger
3687 					current_trigger = &sp->triggers[sp->n_triggers];
3688 					queued_animation_init(current_trigger);
3689 					sp->n_triggers++;
3690 
3691 					required_string("$type:");
3692 					char atype[NAME_LENGTH];
3693 					stuff_string(atype, F_NAME, NAME_LENGTH);
3694 					current_trigger->type = model_anim_match_type(atype);
3695 
3696 					if(optional_string("+sub_type:")){
3697 						stuff_int(&current_trigger->subtype);
3698 					}else{
3699 						current_trigger->subtype = ANIMATION_SUBTYPE_ALL;
3700 					}
3701 
3702 					if(optional_string("+sub_name:")) {
3703 						stuff_string(current_trigger->sub_name, F_NAME, NAME_LENGTH);
3704 					} else {
3705 						strcpy_s(current_trigger->sub_name, "<none>");
3706 					}
3707 
3708 
3709 					if(current_trigger->type == TRIGGER_TYPE_INITIAL){
3710 						//the only thing initial animation type needs is the angle,
3711 						//so to save space lets just make everything optional in this case
3712 
3713 						if(optional_string("+delay:"))
3714 							stuff_int(&current_trigger->start);
3715 						else
3716 							current_trigger->start = 0;
3717 
3718 						if ( optional_string("+reverse_delay:") )
3719 							stuff_int(&current_trigger->reverse_start);
3720 						else
3721 							current_trigger->reverse_start = 0;
3722 
3723 						if(optional_string("+absolute_angle:")){
3724 							current_trigger->absolute = true;
3725 							stuff_vec3d(&current_trigger->angle );
3726 
3727 							current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
3728 							current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
3729 							current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
3730 						}else{
3731 							current_trigger->absolute = false;
3732 							if(!optional_string("+relative_angle:"))
3733 								required_string("+relative_angle:");
3734 
3735 							stuff_vec3d(&current_trigger->angle );
3736 
3737 							current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
3738 							current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
3739 							current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
3740 						}
3741 
3742 						if(optional_string("+velocity:")){
3743 							stuff_vec3d(&current_trigger->vel );
3744 							current_trigger->vel.xyz.x = fl_radians(current_trigger->vel.xyz.x);
3745 							current_trigger->vel.xyz.y = fl_radians(current_trigger->vel.xyz.y);
3746 							current_trigger->vel.xyz.z = fl_radians(current_trigger->vel.xyz.z);
3747 						}
3748 
3749 						if(optional_string("+acceleration:")){
3750 							stuff_vec3d(&current_trigger->accel );
3751 							current_trigger->accel.xyz.x = fl_radians(current_trigger->accel.xyz.x);
3752 							current_trigger->accel.xyz.y = fl_radians(current_trigger->accel.xyz.y);
3753 							current_trigger->accel.xyz.z = fl_radians(current_trigger->accel.xyz.z);
3754 						}
3755 
3756 						if(optional_string("+time:"))
3757 							stuff_int(&current_trigger->end );
3758 						else
3759 							current_trigger->end = 0;
3760 					}else{
3761 
3762 						if(optional_string("+delay:"))
3763 							stuff_int(&current_trigger->start);
3764 						else
3765 							current_trigger->start = 0;
3766 
3767 						if ( optional_string("+reverse_delay:") )
3768 							stuff_int(&current_trigger->reverse_start);
3769 						else
3770 							current_trigger->reverse_start = -1; //have some code figure this out for us
3771 
3772 						if(optional_string("+absolute_angle:")){
3773 							current_trigger->absolute = true;
3774 							stuff_vec3d(&current_trigger->angle );
3775 
3776 							current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
3777 							current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
3778 							current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
3779 						}else{
3780 							current_trigger->absolute = false;
3781 							required_string("+relative_angle:");
3782 							stuff_vec3d(&current_trigger->angle );
3783 
3784 							current_trigger->angle.xyz.x = fl_radians(current_trigger->angle.xyz.x);
3785 							current_trigger->angle.xyz.y = fl_radians(current_trigger->angle.xyz.y);
3786 							current_trigger->angle.xyz.z = fl_radians(current_trigger->angle.xyz.z);
3787 						}
3788 
3789 						required_string("+velocity:");
3790 						stuff_vec3d(&current_trigger->vel );
3791 						current_trigger->vel.xyz.x = fl_radians(current_trigger->vel.xyz.x);
3792 						current_trigger->vel.xyz.y = fl_radians(current_trigger->vel.xyz.y);
3793 						current_trigger->vel.xyz.z = fl_radians(current_trigger->vel.xyz.z);
3794 
3795 						if (optional_string("+acceleration:")){
3796 							stuff_vec3d(&current_trigger->accel );
3797 							current_trigger->accel.xyz.x = fl_radians(current_trigger->accel.xyz.x);
3798 							current_trigger->accel.xyz.y = fl_radians(current_trigger->accel.xyz.y);
3799 							current_trigger->accel.xyz.z = fl_radians(current_trigger->accel.xyz.z);
3800 						} else {
3801 							current_trigger->accel.xyz.x = 0.0f;
3802 							current_trigger->accel.xyz.y = 0.0f;
3803 							current_trigger->accel.xyz.z = 0.0f;
3804 						}
3805 
3806 						if(optional_string("+time:"))
3807 							stuff_int(&current_trigger->end );
3808 						else
3809 							current_trigger->end = 0;
3810 
3811 						if(optional_string("$Sound:")){
3812 							required_string("+Start:");
3813 							stuff_int(&current_trigger->start_sound );
3814 							required_string("+Loop:");
3815 							stuff_int(&current_trigger->loop_sound );
3816 							required_string("+End:");
3817 							stuff_int(&current_trigger->end_sound );
3818 							required_string("+Radius:");
3819 							stuff_float(&current_trigger->snd_rad );
3820 						}else{
3821 							current_trigger->start_sound = -1;
3822 							current_trigger->loop_sound = -1;
3823 							current_trigger->end_sound = -1;
3824 							current_trigger->snd_rad = 0;
3825 						}
3826 					}
3827 
3828 					//make sure that the amount of time it takes to accelerate up and down doesn't make it go farther than the angle
3829 					queued_animation_correct(current_trigger);
3830 				}
3831 				else if(!stricmp(name_tmp, "linked"))
3832 				{
3833 					mprintf(("TODO: set up linked animation\n"));
3834 				}
3835 			}
3836 
3837 		}
3838 		break;
3839 		case 2:
3840 			cont_flag = 0;
3841 			break;
3842 		case 3:
3843 			if (isTemplate) {
3844 				cont_flag = 0;
3845 				break;
3846 			}
3847 		default:
3848 			Int3();	// Impossible return value from required_string_3.
3849 		}
3850 	}
3851 
3852 	// must be > 0//no it doesn't :P -Bobboau
3853 	// yes it does! - Goober5000
3854 	// (we don't want a div-0 error)
3855 	if (hull_percentage_of_hits <= 0.0f )
3856 	{
3857 		//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);
3858 	}
3859 	// when done reading subsystems, malloc and copy the subsystem data to the ship info structure
3860 	int orig_n_subsystems = sip->n_subsystems;
3861 	if ( n_subsystems > 0 ) {
3862 		if(sip->n_subsystems < 1) {
3863 			sip->n_subsystems = n_subsystems;
3864 			sip->subsystems = (model_subsystem *)vm_malloc(sizeof(model_subsystem) * sip->n_subsystems );
3865 		} else {
3866 			sip->n_subsystems += n_subsystems;
3867 			sip->subsystems = (model_subsystem *)vm_realloc(sip->subsystems, sizeof(model_subsystem) * sip->n_subsystems);
3868 		}
3869 		Assert( sip->subsystems != NULL );
3870 
3871 		for ( i = 0; i < n_subsystems; i++ ){
3872 			sip->subsystems[orig_n_subsystems+i] = subsystems[i];
3873 		}
3874 	}
3875 
3876 	model_anim_fix_reverse_times(sip);
3877 
3878 	return rtn;	//0 for success
3879 }
3880 
get_engine_wash_pointer(char * engine_wash_name)3881 engine_wash_info *get_engine_wash_pointer(char *engine_wash_name)
3882 {
3883 	for(int i = 0; i < Num_engine_wash_types; i++)
3884 	{
3885 		if(!stricmp(engine_wash_name, Engine_wash_info[i].name))
3886 		{
3887 			return &Engine_wash_info[i];
3888 		}
3889 	}
3890 
3891 	//Didn't find anything.
3892 	return NULL;
3893 }
3894 
parse_ship_type()3895 void parse_ship_type()
3896 {
3897 	char name_buf[NAME_LENGTH];
3898 	bool nocreate = false;
3899 	ship_type_info stp_buf, *stp = NULL;
3900 
3901 	required_string("$Name:");
3902 	stuff_string(name_buf, F_NAME, NAME_LENGTH);
3903 
3904 	if(optional_string("+nocreate")) {
3905 		nocreate = true;
3906 	}
3907 
3908 	int idx = ship_type_name_lookup(name_buf);
3909 	if (idx >= 0)
3910 	{
3911 		stp = &Ship_types[idx];
3912 	}
3913 	else
3914 	{
3915 		stp = &stp_buf;
3916 		strcpy_s(stp->name, name_buf);
3917 	}
3918 
3919 	char *ship_type = NULL;
3920 	if (!stricmp(stp->name, "sentrygun")) {
3921 		ship_type = "sentry gun";
3922 	} else if (!stricmp(stp->name, "escapepod")) {
3923 		ship_type = "escape pod";
3924 	} else if (!stricmp(stp->name, "repair_rearm")) {
3925 		ship_type = "support";
3926 	} else if (!stricmp(stp->name, "supercap")) {
3927 		ship_type = "super cap";
3928 	} else if (!stricmp(stp->name, "knossos")) {
3929 		ship_type = "knossos device";
3930 	}
3931 
3932 	if (ship_type != NULL) {
3933 		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);
3934 	}
3935 
3936 	//Okay, now we should have the values to parse
3937 	//But they aren't here!! :O
3938 	//Now they are!! Whee fogging!!
3939 
3940 	//AI turret targeting priority setup
3941 	if (optional_string("$Target Priority Groups:") ) {
3942 		SCP_vector <SCP_string> target_group_strings;
3943 		int num_strings = stuff_string_list(target_group_strings);
3944 		int num_groups = Ai_tp_list.size();
3945 		int i, j;
3946 		bool override_strings = false;
3947 
3948 		if (optional_string("+Override")) {
3949 			override_strings = true;
3950 		}
3951 
3952 		for(j = 0; j < num_strings; j++) {
3953 			for(i = 0; i < num_groups; i++) {
3954 				if ( !stricmp(target_group_strings[j].c_str(), Ai_tp_list[i].name) ) {
3955 					//so now the string from the list above as well as the ai priority group name match
3956 					//clear it if override has been set
3957 					if (override_strings) {
3958 						Ai_tp_list[i].ship_type.clear();
3959 						override_strings = false;
3960 					}
3961 					//find the index number of the current ship info type
3962 					Ai_tp_list[i].ship_type.push_back(ship_type_name_lookup(name_buf));
3963 					break;
3964 				}
3965 			}
3966 			if (i == num_groups) {
3967 				Warning(LOCATION,"Unidentified priority group '%s' set for objecttype '%s'\n", target_group_strings[j].c_str(), stp->name);
3968 			}
3969 		}
3970 	}
3971 
3972 	if(optional_string("$Counts for Alone:")) {
3973 		stuff_boolean_flag(&stp->message_bools, STI_MSG_COUNTS_FOR_ALONE);
3974 	}
3975 
3976 	if(optional_string("$Praise Destruction:")) {
3977 		stuff_boolean_flag(&stp->message_bools, STI_MSG_PRAISE_DESTRUCTION);
3978 	}
3979 
3980 	if(optional_string("$On Hotkey list:")) {
3981 		stuff_boolean_flag(&stp->hud_bools, STI_HUD_HOTKEY_ON_LIST);
3982 	}
3983 
3984 	if(optional_string("$Target as Threat:")) {
3985 		stuff_boolean_flag(&stp->hud_bools, STI_HUD_TARGET_AS_THREAT);
3986 	}
3987 
3988 	if(optional_string("$Show Attack Direction:")) {
3989 		stuff_boolean_flag(&stp->hud_bools, STI_HUD_SHOW_ATTACK_DIRECTION);
3990 	}
3991 
3992 	if(optional_string("$Scannable:")) {
3993 		stuff_boolean_flag(&stp->ship_bools, STI_SHIP_SCANNABLE);
3994 	}
3995 
3996 	if(optional_string("$Warp Pushes:")) {
3997 		stuff_boolean_flag(&stp->ship_bools, STI_SHIP_WARP_PUSHES);
3998 	}
3999 
4000 	if(optional_string("$Warp Pushable:")) {
4001 		stuff_boolean_flag(&stp->ship_bools, STI_SHIP_WARP_PUSHABLE);
4002 	}
4003 
4004 	if(optional_string("$Turrets prioritize ship target:")) {
4005 		stuff_boolean_flag(&stp->ship_bools, STI_TURRET_TGT_SHIP_TGT);
4006 	}
4007 
4008 	if(optional_string("$Max Debris Speed:")) {
4009 		stuff_float(&stp->debris_max_speed);
4010 	}
4011 
4012 	if(optional_string("$FF Multiplier:")) {
4013 		stuff_float(&stp->ff_multiplier);
4014 	}
4015 
4016 	if(optional_string("$EMP Multiplier:")) {
4017 		stuff_float(&stp->emp_multiplier);
4018 	}
4019 
4020 	if(optional_string("$Beams Easily Hit:")) {
4021 		stuff_boolean_flag(&stp->weapon_bools, STI_WEAP_BEAMS_EASILY_HIT);
4022 	}
4023 
4024 	if(optional_string("$Protected on cripple:")) {
4025 		stuff_boolean_flag(&stp->ai_bools, STI_AI_PROTECTED_ON_CRIPPLE);
4026 	}
4027 
4028 	if(optional_string("$No Huge Beam Impact Effects:")) {
4029 		stuff_boolean_flag(&stp->weapon_bools, STI_WEAP_NO_HUGE_IMPACT_EFF);
4030 	}
4031 
4032 	if(optional_string("$Don't display class in briefing:")) {
4033 		stuff_boolean_flag(&stp->hud_bools, STI_HUD_NO_CLASS_DISPLAY);
4034 	}
4035 
4036 	if(optional_string("$Fog:"))
4037 	{
4038 		if(optional_string("+Start dist:")) {
4039 			stuff_float(&stp->fog_start_dist);
4040 		}
4041 
4042 		if(optional_string("+Compl dist:")) {
4043 			stuff_float(&stp->fog_complete_dist);
4044 		}
4045 	}
4046 
4047 	if(optional_string("$AI:"))
4048 	{
4049 		if(optional_string("+Valid goals:")) {
4050 			parse_string_flag_list(&stp->ai_valid_goals, Ai_goal_names, Num_ai_goals);
4051 		}
4052 
4053 		if(optional_string("+Accept Player Orders:")) {
4054 			stuff_boolean_flag(&stp->ai_bools, STI_AI_ACCEPT_PLAYER_ORDERS);
4055 		}
4056 
4057 		if(optional_string("+Player Orders:")) {
4058 			parse_string_flag_list(&stp->ai_player_orders, Player_orders, Num_player_orders);
4059 		}
4060 
4061 		if(optional_string("+Auto attacks:")) {
4062 			stuff_boolean_flag(&stp->ai_bools, STI_AI_AUTO_ATTACKS);
4063 		}
4064 
4065 		if(optional_string("+Attempt broadside:")) {
4066 			stuff_boolean_flag(&stp->ai_bools, STI_AI_ATTEMPT_BROADSIDE);
4067 		}
4068 
4069 		if(optional_string("+Actively Pursues:")) {
4070 			stuff_string_list(stp->ai_actively_pursues_temp);
4071 		}
4072 
4073 		if(optional_string("+Guards attack this:")) {
4074 			stuff_boolean_flag(&stp->ai_bools, STI_AI_GUARDS_ATTACK);
4075 		}
4076 
4077 		if(optional_string("+Turrets attack this:")) {
4078 			stuff_boolean_flag(&stp->ai_bools, STI_AI_TURRETS_ATTACK);
4079 		}
4080 
4081 		if(optional_string("+Can form wing:")) {
4082 			stuff_boolean_flag(&stp->ai_bools, STI_AI_CAN_FORM_WING);
4083 		}
4084 
4085 		if(optional_string("+Active docks:")) {
4086 			parse_string_flag_list(&stp->ai_active_dock, Dock_type_names, Num_dock_type_names);
4087 		}
4088 
4089 		if(optional_string("+Passive docks:")) {
4090 			parse_string_flag_list(&stp->ai_passive_dock, Dock_type_names, Num_dock_type_names);
4091 		}
4092 
4093 		if(optional_string("+Ignored on cripple by:")) {
4094 			stuff_string_list(stp->ai_cripple_ignores_temp);
4095 		}
4096 	}
4097 
4098 	if(optional_string("$Explosion Animations:"))
4099 	{
4100 		int temp[MAX_FIREBALL_TYPES];
4101 		int parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
4102 		stp->explosion_bitmap_anims.clear();
4103 		stp->explosion_bitmap_anims.insert(stp->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
4104 	}
4105 
4106 	if(optional_string("$Vaporize Percent Chance:")) {
4107 		stuff_float(&stp->vaporize_chance);
4108 		if (stp->vaporize_chance < 0.0f || stp->vaporize_chance > 100.0f) {
4109 			stp->vaporize_chance = 0.0f;
4110 			Warning(LOCATION, "$Vaporize Percent Chance should be between 0 and 100.0 (read %f). Setting to 0.", stp->vaporize_chance);
4111 		}
4112 		//Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0
4113 		stp->vaporize_chance /= 100.0;
4114 	}
4115 
4116 	if (!nocreate)
4117 		Ship_types.push_back(stp_buf);
4118 }
4119 
parse_shiptype_tbl(const char * filename)4120 void parse_shiptype_tbl(const char *filename)
4121 {
4122 	int rval;
4123 
4124 	// open localization
4125 	lcl_ext_open();
4126 
4127 	if ((rval = setjmp(parse_abort)) != 0) {
4128 		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", filename, rval));
4129 		lcl_ext_close();
4130 		return;
4131 	}
4132 
4133 	if (filename != NULL)
4134 		read_file_text(filename, CF_TYPE_TABLES);
4135 	else
4136 		read_file_text_from_array(defaults_get_file("objecttypes.tbl"));
4137 
4138 	reset_parse();
4139 
4140 	if (optional_string("#Target Priorities"))
4141 	{
4142 		while (required_string_either("#End", "$Name:"))
4143 			parse_ai_target_priorities();
4144 
4145 		required_string("#End");
4146 	}
4147 
4148 	if (optional_string("#Weapon Targeting Priorities"))
4149 	{
4150 		while (required_string_either("#End", "$Name:"))
4151 			parse_weapon_targeting_priorities();
4152 
4153 		required_string("#End");
4154 	}
4155 
4156 	if (optional_string("#Ship Types"))
4157 	{
4158 		while (required_string_either("#End", "$Name:"))
4159 			parse_ship_type();
4160 
4161 		required_string("#End");
4162 	}
4163 
4164 	// add tbl/tbm to multiplayer validation list
4165 	fs2netd_add_table_validation(filename);
4166 
4167 	// close localization
4168 	lcl_ext_close();
4169 }
4170 
4171 // The E - Simple lookup function for FRED.
get_default_player_ship_index()4172 int get_default_player_ship_index()
4173 {
4174 	if (strlen(default_player_ship))
4175 	{
4176 		for (int i = 0; i < Num_ship_classes; i++)
4177 		{
4178 			if (stricmp(default_player_ship, Ship_info[i].name) == 0)
4179 				return i;
4180 		}
4181 		return 0;
4182 	} else
4183 		return 0;
4184 }
4185 
4186 // Goober5000 - this works better in its own function
ship_set_default_player_ship()4187 void ship_set_default_player_ship()
4188 {
4189 	int i;
4190 
4191 	// already have one
4192 	if(strlen(default_player_ship))
4193 		return;
4194 
4195 	// find the first with the default flag
4196 	for(i = 0; i < Num_ship_classes; i++)
4197 	{
4198 		if(Ship_info[i].flags & SIF_DEFAULT_PLAYER_SHIP)
4199 		{
4200 			strcpy_s(default_player_ship, Ship_info[i].name);
4201 			return;
4202 		}
4203 	}
4204 
4205 	// find the first player ship
4206 	for(i = 0; i < Num_ship_classes; i++)
4207 	{
4208 		if(Ship_info[i].flags & SIF_PLAYER_SHIP)
4209 		{
4210 			strcpy_s(default_player_ship, Ship_info[i].name);
4211 			return;
4212 		}
4213 	}
4214 
4215 	// find the first ship
4216 	if(Num_ship_classes > 0)
4217 	{
4218 		strcpy_s(default_player_ship, Ship_info[0].name);
4219 	}
4220 }
4221 
parse_shiptbl(const char * filename)4222 void parse_shiptbl(const char *filename)
4223 {
4224 	int rval;
4225 
4226 	// open localization
4227 	lcl_ext_open();
4228 
4229 	if ((rval = setjmp(parse_abort)) != 0)
4230 	{
4231 		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", filename, rval));
4232 		lcl_ext_close();
4233 		return;
4234 	}
4235 
4236 	read_file_text(filename, CF_TYPE_TABLES);
4237 	reset_parse();
4238 
4239 	// parse default ship
4240 	//Override default player ship
4241 	if(optional_string("#Default Player Ship"))
4242 	{
4243 		required_string("$Name:");
4244 		stuff_string(default_player_ship, F_NAME, sizeof(default_player_ship));
4245 		required_string("#End");
4246 	}
4247 	//Add engine washes
4248 	//This will override if they already exist
4249 	if(optional_string("#Engine Wash Info"))
4250 	{
4251 		while (required_string_either("#End", "$Name:"))
4252 		{
4253 			parse_engine_wash(Parsing_modular_table);
4254 		}
4255 
4256 		required_string("#End");
4257 	}
4258 
4259 	if( optional_string("#Ship Templates") ) {
4260 		while ( required_string_either("#End","$Template:") ) {
4261 
4262 			if ( parse_ship_template() ) {
4263 				continue;
4264 			}
4265 		}
4266 
4267 		required_string("#End");
4268 	}
4269 
4270 	//Add ship classes
4271 	if(optional_string("#Ship Classes"))
4272 	{
4273 
4274 		while (required_string_either("#End","$Name:"))
4275 		{
4276 			if ( parse_ship(filename, Parsing_modular_table) ) {
4277 				continue;
4278 			}
4279 		}
4280 
4281 		required_string("#End");
4282 	}
4283 
4284 	//Set default player ship
4285 	ship_set_default_player_ship();
4286 
4287 	// add tbl/tbm to multiplayer validation list
4288 	fs2netd_add_table_validation(filename);
4289 
4290 	// close localization
4291 	lcl_ext_close();
4292 }
4293 
4294 int ship_show_velocity_dot = 0;
4295 
4296 
DCF_BOOL(show_velocity_dot,ship_show_velocity_dot)4297 DCF_BOOL( show_velocity_dot, ship_show_velocity_dot )
4298 
4299 /**
4300  * Clean up ship entries, making sure various flags and settings are correct
4301  */
4302 void ship_parse_post_cleanup()
4303 {
4304 	int i, j;
4305 	char name_tmp[NAME_LENGTH];
4306 	ship_info *sip;
4307 
4308 	for (i = 0; i < Num_ship_classes; i++)
4309 	{
4310 		sip = &Ship_info[i];
4311 
4312 		// ballistic primary fixage...
4313 		{
4314 			bool pbank_capacity_specified = false;
4315 
4316 			// determine whether this ship had primary capacities specified for it
4317 			for (j = 0; j < sip->num_primary_banks; j++) {
4318 				if (sip->primary_bank_ammo_capacity[j] > 0) {
4319 					pbank_capacity_specified = true;
4320 					break;
4321 				}
4322 			}
4323 
4324 			// be friendly; ensure ballistic flags check out
4325 			if (pbank_capacity_specified) {
4326 				if ( !(sip->flags & SIF_BALLISTIC_PRIMARIES) ) {
4327 					Warning(LOCATION, "Pbank capacity specified for non-ballistic-primary-enabled ship %s.\nResetting capacities to 0.\nTo fix this, add a ballistic primary to the list of allowed primaries.\n", sip->name);
4328 
4329 					for (j = 0; j < MAX_SHIP_PRIMARY_BANKS; j++) {
4330 						sip->primary_bank_ammo_capacity[j] = 0;
4331 					}
4332 				}
4333 			} else {
4334 				if (sip->flags & SIF_BALLISTIC_PRIMARIES) {
4335 					Warning(LOCATION, "Pbank capacity not specified for ballistic-primary-enabled ship %s.\nDefaulting to capacity of 1 per bank.\n", sip->name);
4336 
4337 					for (j = 0; j < MAX_SHIP_PRIMARY_BANKS; j++) {
4338 						sip->primary_bank_ammo_capacity[j] = 1;
4339 					}
4340 				}
4341 			}
4342 		}
4343 
4344 		// ultra stupid compatbility handling for the once broken "generate hud" flag.
4345 		// it was previously testing the afterburner flag, so that's what we check for that
4346 		if ( (sip->shield_icon_index == 255) && (sip->flags & SIF_AFTERBURNER)
4347 				&& !(sip->flags2 & SIF2_GENERATE_HUD_ICON) && (sip->flags & SIF_PLAYER_SHIP) )
4348 		{
4349 			Warning(LOCATION, "Compatibility warning:\nNo shield icon specified for '%s' but the \"generate icon\" flag is not specified.\nEnabling flag by default.\n", sip->name);
4350 			sip->flags2 |= SIF2_GENERATE_HUD_ICON;
4351 		}
4352 
4353 		// if we have a ship copy, then check to be sure that our base ship exists
4354 		if (sip->flags & SIF_SHIP_COPY)
4355 		{
4356 			strcpy_s(name_tmp, sip->name);
4357 
4358 			if (end_string_at_first_hash_symbol(name_tmp))
4359 			{
4360 				if (ship_info_lookup(name_tmp) < 0)
4361 				{
4362 					Warning(LOCATION, "Ship %s is a copy, but base ship %s couldn't be found.", sip->name, name_tmp);
4363 					sip->flags &= ~SIF_SHIP_COPY;
4364 				}
4365 			}
4366 			else
4367 			{
4368 				Warning(LOCATION, "Ships %s is a copy, but does not use the ship copy name extension.");
4369 				sip->flags &= ~SIF_SHIP_COPY;
4370 			}
4371 		}
4372 	}
4373 
4374 	// check also target groups here
4375 	int n_tgt_groups = Ai_tp_list.size();
4376 
4377 	if (n_tgt_groups > 0) {
4378 		for(i = 0; i < n_tgt_groups; i++) {
4379 			if (!(Ai_tp_list[i].obj_flags || Ai_tp_list[i].sif_flags || Ai_tp_list[i].sif2_flags || Ai_tp_list[i].wif2_flags || Ai_tp_list[i].wif_flags)) {
4380 				//had none of these, check next
4381 				if (Ai_tp_list[i].obj_type == -1) {
4382 					//didn't have this one
4383 					if (!(Ai_tp_list[i].ship_class.size() || Ai_tp_list[i].ship_type.size() || Ai_tp_list[i].weapon_class.size())) {
4384 						// had nothing - time to issue a warning
4385 						Warning(LOCATION, "Target priority group '%s' had no targeting rules issued for it.\n", Ai_tp_list[i].name);
4386 					}
4387 				}
4388 			}
4389 		}
4390 	}
4391 }
4392 
4393 /**
4394  * Called once at the beginning of the game to parse ships.tbl and stuff the ::Ship_info[]
4395  * structure
4396  */
ship_init()4397 void ship_init()
4398 {
4399 	if ( !ships_inited )
4400 	{
4401 		//Parse main TBL first
4402 		if (cf_exists_full("objecttypes.tbl", CF_TYPE_TABLES))
4403 			parse_shiptype_tbl("objecttypes.tbl");
4404 		else
4405 			parse_shiptype_tbl(NULL);
4406 
4407 		//Then other ones
4408 		parse_modular_table(NOX("*-obt.tbm"), parse_shiptype_tbl);
4409 
4410 		// DO ALL THE STUFF WE NEED TO DO AFTER LOADING Ship_types
4411 		ship_type_info *stp;
4412 
4413 		uint i,j;
4414 		int idx;
4415 		for(i = 0; i < Ship_types.size(); i++)
4416 		{
4417 			stp = &Ship_types[i];
4418 
4419 			//Handle active pursuit
4420 			for(j = 0; j < stp->ai_actively_pursues_temp.size(); j++)
4421 			{
4422 				idx = ship_type_name_lookup((char*)stp->ai_actively_pursues_temp[j].c_str());
4423 				if(idx >= 0) {
4424 					stp->ai_actively_pursues.push_back(idx);
4425 				}
4426 			}
4427 			stp->ai_actively_pursues_temp.clear();
4428 
4429 			//Handle disabled/disarmed behaviour
4430 			for(j = 0; j < stp->ai_cripple_ignores_temp.size(); j++) {
4431 				idx = ship_type_name_lookup((char*)stp->ai_cripple_ignores_temp[j].c_str());
4432 				if(idx >= 0) {
4433 					stp->ai_cripple_ignores.push_back(idx);
4434 				}
4435 			}
4436 			stp->ai_cripple_ignores_temp.clear();
4437 		}
4438 
4439 		//ships.tbl
4440 		{
4441 			Num_engine_wash_types = 0;
4442 			Num_ship_classes = 0;
4443 			strcpy_s(default_player_ship, "");
4444 
4445 			// static alias stuff - stupid, but it seems to be necessary
4446 			tspecies_names = (char **) vm_malloc( Species_info.size() * sizeof(char*) );
4447 			for (i = 0; i < Species_info.size(); i++)
4448 				tspecies_names[i] = Species_info[i].species_name;
4449 
4450 			//Parse main TBL first
4451 			parse_shiptbl("ships.tbl");
4452 
4453 			//Then other ones
4454 			parse_modular_table(NOX("*-shp.tbm"), parse_shiptbl);
4455 
4456 			ship_parse_post_cleanup();
4457 
4458 			ships_inited = 1;
4459 
4460 			// cleanup
4461 
4462 			//Unload ship templates, we don't need them anymore.
4463 			Ship_templates.clear();
4464 
4465 			vm_free(tspecies_names);
4466 			tspecies_names = NULL;
4467 		}
4468 
4469 		// NULL out "dynamic" subsystem ptr's
4470 		for (i = 0; i < NUM_SHIP_SUBSYSTEM_SETS; i++)
4471 			Ship_subsystems[i] = NULL;
4472 	}
4473 
4474 	ship_level_init();	// needed for FRED
4475 }
4476 
4477 int Man_thruster_reset_timestamp = 0;
4478 
ship_clear_subsystems()4479 static void ship_clear_subsystems()
4480 {
4481 	int i;
4482 
4483 	for (i = 0; i < NUM_SHIP_SUBSYSTEM_SETS; i++) {
4484 		if (Ship_subsystems[i] != NULL) {
4485 			delete[] Ship_subsystems[i];
4486 			Ship_subsystems[i] = NULL;
4487 		}
4488 	}
4489 
4490 	Num_ship_subsystems = 0;
4491 	Num_ship_subsystems_allocated = 0;
4492 
4493 	Triggered_rotations.clear();
4494 }
4495 
ship_allocate_subsystems(int num_so,bool page_in=false)4496 static int ship_allocate_subsystems(int num_so, bool page_in = false)
4497 {
4498 	int idx, i;
4499 	int num_subsystems_save = 0;
4500 
4501 	// "0" itself is safe
4502 	if (num_so < 0) {
4503 		Int3();
4504 		return 0;
4505 	}
4506 
4507 	// allow a page-in thingy, so that we can grab as much as possible before mission
4508 	// start, but without messing up our count for future things
4509 	if (page_in)
4510 		num_subsystems_save = Num_ship_subsystems;
4511 
4512 	Num_ship_subsystems += num_so;
4513 
4514 	// bail if we don't actually need any more
4515 	if ( Num_ship_subsystems < Num_ship_subsystems_allocated )
4516 		return 1;
4517 
4518 	mprintf(("Allocating space for at least %i new ship subsystems ... ", num_so));
4519 
4520 	// we might need more than one set worth of new subsystems, so make as many as required
4521 	do {
4522 		for (idx = 0; idx < NUM_SHIP_SUBSYSTEM_SETS; idx++) {
4523 			if (Ship_subsystems[idx] == NULL)
4524 				break;
4525 		}
4526 
4527 		// safety check, but even if we have this here it will fubar something else later, so we're screwed either way
4528 		if (idx == NUM_SHIP_SUBSYSTEM_SETS) {
4529 			return 0;
4530 		}
4531 
4532 		Ship_subsystems[idx] = new ship_subsys[NUM_SHIP_SUBSYSTEMS_PER_SET];
4533 
4534 		// append the new set to our free list
4535 		for (i = 0; i < NUM_SHIP_SUBSYSTEMS_PER_SET; i++)
4536 			list_append( &ship_subsys_free_list, &Ship_subsystems[idx][i] );
4537 
4538 		Num_ship_subsystems_allocated += NUM_SHIP_SUBSYSTEMS_PER_SET;
4539 	} while ( (Num_ship_subsystems - Num_ship_subsystems_allocated) > 0 );
4540 
4541 	if (page_in)
4542 		Num_ship_subsystems = num_subsystems_save;
4543 
4544 	mprintf((" a total of %i is now available (%i in-use).\n", Num_ship_subsystems_allocated, Num_ship_subsystems));
4545 	return 1;
4546 }
4547 
4548 /**
4549  * This will get called at the start of each level.
4550  */
ship_level_init()4551 void ship_level_init()
4552 {
4553 	int i;
4554 
4555 	// Reset everything between levels
4556 	Ships_exited.clear();
4557 	Ships_exited.reserve(100);
4558 	for (i=0; i<MAX_SHIPS; i++ )
4559 	{
4560 		Ships[i].ship_name[0] = '\0';
4561 		Ships[i].objnum = -1;
4562 	}
4563 
4564 	Num_wings = 0;
4565 	for (i = 0; i < MAX_WINGS; i++ )
4566 	{
4567 		Wings[i].num_waves = -1;
4568 		Wings[i].wing_squad_filename[0] = '\0';
4569 		Wings[i].wing_insignia_texture = -1;	// Goober5000 - default to no wing insignia
4570 												// don't worry about releasing textures because
4571 												// they are released automatically when the model
4572 												// is unloaded (because they are part of the model)
4573 	}
4574 
4575 	for (i=0; i<MAX_STARTING_WINGS; i++)
4576 		Starting_wings[i] = -1;
4577 
4578 	for (i=0; i<MAX_SQUADRON_WINGS; i++)
4579 		Squadron_wings[i] = -1;
4580 
4581 	for (i=0; i<MAX_TVT_WINGS; i++)
4582 		TVT_wings[i] = -1;
4583 
4584 	// Goober5000
4585 
4586 	// set starting wing names to default
4587 	strcpy_s(Starting_wing_names[0], "Alpha");
4588 	strcpy_s(Starting_wing_names[1], "Beta");
4589 	strcpy_s(Starting_wing_names[2], "Gamma");
4590 
4591 	// set squadron wing names to default
4592 	strcpy_s(Squadron_wing_names[0], "Alpha");
4593 	strcpy_s(Squadron_wing_names[1], "Beta");
4594 	strcpy_s(Squadron_wing_names[2], "Gamma");
4595 	strcpy_s(Squadron_wing_names[3], "Delta");
4596 	strcpy_s(Squadron_wing_names[4], "Epsilon");
4597 
4598 	// set tvt wing names to default
4599 	strcpy_s(TVT_wing_names[0], "Alpha");
4600 	strcpy_s(TVT_wing_names[1], "Zeta");
4601 
4602 	// Empty the subsys list
4603 	ship_clear_subsystems();
4604 	list_init( &ship_subsys_free_list );
4605 
4606 	Laser_energy_out_snd_timer = 1;
4607 	Missile_out_snd_timer		= 1;
4608 
4609 	ship_obj_list_init();
4610 
4611 	Ship_cargo_check_timer = 1;
4612 
4613 	shipfx_large_blowup_level_init();
4614 
4615 	Man_thruster_reset_timestamp = timestamp(0);
4616 }
4617 
4618 /**
4619  * Add a ship onto the exited ships list.
4620  *
4621  * The reason parameter tells us why the ship left the mission (i.e. departed or destroyed)
4622  */
ship_add_exited_ship(ship * sp,int reason)4623 void ship_add_exited_ship( ship *sp, int reason )
4624 {
4625 	exited_ship entry;
4626 
4627 	strcpy_s(entry.ship_name, sp->ship_name );
4628 	entry.obj_signature = Objects[sp->objnum].signature;
4629 	entry.ship_class = sp->ship_info_index;
4630 	entry.team = sp->team;
4631 	entry.flags = reason;
4632 	// if ship is red alert, flag as such
4633 	if (sp->flags & SF_RED_ALERT_STORE_STATUS) {
4634 		entry.flags |= SEF_RED_ALERT_CARRY;
4635 	}
4636 	entry.time = Missiontime;
4637 	entry.hull_strength = int(Objects[sp->objnum].hull_strength);
4638 
4639 	entry.cargo1 = sp->cargo1;
4640 
4641 	entry.time_cargo_revealed = (fix)0;
4642 	if ( sp->flags & SF_CARGO_REVEALED )
4643 	{
4644 		entry.flags |= SEF_CARGO_KNOWN;
4645 		entry.time_cargo_revealed = sp->time_cargo_revealed;
4646 	}
4647 
4648 	if ( sp->time_first_tagged > 0 )
4649 		entry.flags |= SEF_BEEN_TAGGED;
4650 
4651 	//copy across the damage_ship arrays
4652 	for (int i = 0; i < MAX_DAMAGE_SLOTS ; i++) {
4653 		entry.damage_ship_id[i] = sp->damage_ship_id[i] ;
4654 		entry.damage_ship[i] = sp->damage_ship[i] ;
4655 	}
4656 
4657 	Ships_exited.push_back(entry);
4658 }
4659 
4660 /**
4661  * Attempt to find information about an exited ship based on shipname
4662  */
ship_find_exited_ship_by_name(char * name)4663 int ship_find_exited_ship_by_name( char *name )
4664 {
4665 	int i;
4666 
4667 	for (i = 0; i < (int)Ships_exited.size(); i++) {
4668 		if ( !stricmp(name, Ships_exited[i].ship_name) )
4669 			return i;
4670 	}
4671 
4672 	return -1;
4673 }
4674 
4675 /**
4676  * Attempt to find information about an exited ship based on signature
4677  */
ship_find_exited_ship_by_signature(int signature)4678 int ship_find_exited_ship_by_signature( int signature )
4679 {
4680 	int i;
4681 
4682 	for (i = 0; i < (int)Ships_exited.size(); i++) {
4683 		if ( signature == Ships_exited[i].obj_signature )
4684 			return i;
4685 	}
4686 
4687 	return -1;
4688 }
4689 
4690 
physics_ship_init(object * objp)4691 void physics_ship_init(object *objp)
4692 {
4693 	ship_info	*sinfo = &Ship_info[Ships[objp->instance].ship_info_index];
4694 	physics_info	*pi = &objp->phys_info;
4695 	polymodel *pm = model_get(sinfo->model_num);
4696 
4697 	// use mass and I_body_inv from POF read into polymodel
4698 	physics_init(pi);
4699 
4700 	pi->mass = pm->mass * sinfo->density;
4701 	if (pi->mass==0.0f)
4702 	{
4703 		vec3d size;
4704 		vm_vec_sub(&size,&pm->maxs,&pm->mins);
4705 		float vmass=size.xyz.x*size.xyz.y*size.xyz.z;
4706 		float amass=4.65f*(float)pow(vmass,(2.0f/3.0f));
4707 
4708 		nprintf(("Physics", "pi->mass==0.0f. setting to %f",amass));
4709 		Warning(LOCATION, "%s (%s) has no mass! setting to %f", sinfo->name, sinfo->pof_file, amass);
4710 		pm->mass=amass;
4711 		pi->mass=amass*sinfo->density;
4712 	}
4713 
4714 	// ack!
4715 	// if pm's MOI is invalid, compensate
4716 	if ( IS_VEC_NULL(&pm->moment_of_inertia.vec.rvec)
4717 		&& IS_VEC_NULL(&pm->moment_of_inertia.vec.uvec)
4718 		&& IS_VEC_NULL(&pm->moment_of_inertia.vec.fvec) )
4719 	{
4720 		nprintf(("Physics", "pm->moment_of_inertia is invalid for %s!", pm->filename));
4721 		Warning(LOCATION, "%s (%s) has a null moment of inertia!", sinfo->name, sinfo->pof_file);
4722 
4723 		// TODO: generate MOI properly
4724 		pi->I_body_inv = pm->moment_of_inertia;
4725 	}
4726 	// it's valid, so we can use it
4727 	else
4728 		pi->I_body_inv = pm->moment_of_inertia;
4729 
4730 	// scale pm->I_body_inv value by density
4731 	vm_vec_scale( &pi->I_body_inv.vec.rvec, sinfo->density );
4732 	vm_vec_scale( &pi->I_body_inv.vec.uvec, sinfo->density );
4733 	vm_vec_scale( &pi->I_body_inv.vec.fvec, sinfo->density );
4734 
4735 	pi->center_of_mass = pm->center_of_mass;
4736 	pi->side_slip_time_const = sinfo->damp;
4737 	pi->delta_bank_const = sinfo->delta_bank_const;
4738 	pi->rotdamp = sinfo->rotdamp;
4739 	pi->max_vel = sinfo->max_vel;
4740 	pi->afterburner_max_vel = sinfo->afterburner_max_vel;
4741 	pi->max_rotvel = sinfo->max_rotvel;
4742 	pi->max_rear_vel = sinfo->max_rear_vel;
4743 	pi->flags |= PF_ACCELERATES;
4744 	pi->flags &= ~PF_GLIDING; //Turn off glide
4745 	pi->flags &= ~PF_FORCE_GLIDE;
4746 
4747 	pi->forward_accel_time_const=sinfo->forward_accel;
4748 	pi->afterburner_forward_accel_time_const=sinfo->afterburner_forward_accel;
4749 	pi->forward_decel_time_const=sinfo->forward_decel;
4750 	pi->slide_accel_time_const=sinfo->slide_accel;
4751 	pi->slide_decel_time_const=sinfo->slide_decel;
4752 
4753 	if ( (pi->max_vel.xyz.x > 0.000001f) || (pi->max_vel.xyz.y > 0.000001f) )
4754 		pi->flags |= PF_SLIDE_ENABLED;
4755 
4756 	pi->cur_glide_cap = pi->max_vel.xyz.z; //Init dynamic glide cap stuff to the max vel.
4757 	if ( sinfo->glide_cap > 0.000001f || sinfo->glide_cap < -0.000001f )		//Backslash
4758 		pi->glide_cap = sinfo->glide_cap;
4759 	else
4760 		pi->glide_cap = MAX(MAX(pi->max_vel.xyz.z, sinfo->max_overclocked_speed), pi->afterburner_max_vel.xyz.z);
4761 	// If there's not a value for +Max Glide Speed set in the table, we want this cap to default to the fastest speed the ship can go.
4762 	// However, a negative value means we want no cap, thus allowing nearly infinite maximum gliding speeds.
4763 
4764 	//SUSHI: If we are using dynamic glide capping, force the glide cap to 0 (understood by physics.cpp to mean the cap should be dynamic)
4765 	if (sinfo->glide_dynamic_cap)
4766 		pi->glide_cap = 0;
4767 
4768 	pi->glide_accel_mult = sinfo->glide_accel_mult;
4769 
4770 	//SUSHI: This defaults to the AI_Profile value, and is only optionally overridden
4771 	pi->use_newtonian_damp = ((The_mission.ai_profile->flags & AIPF_USE_NEWTONIAN_DAMPENING) != 0);
4772 	if (sinfo->newtonian_damp_override)
4773 		pi->use_newtonian_damp = sinfo->use_newtonian_damp;
4774 
4775 	vm_vec_zero(&pi->vel);
4776 	vm_vec_zero(&pi->rotvel);
4777 	pi->speed = 0.0f;
4778 	pi->heading = 0.0f;
4779 	vm_set_identity(&pi->last_rotmat);
4780 
4781 	//SparK: setting the reverse burners
4782 	pi->afterburner_max_reverse_vel = sinfo->afterburner_max_reverse_vel;
4783 	pi->afterburner_reverse_accel = sinfo->afterburner_reverse_accel;
4784 }
4785 
4786 /**
4787  * Get the type of the given ship as a string
4788  */
ship_get_type(char * output,ship_info * sip)4789 int ship_get_type(char* output, ship_info *sip)
4790 {
4791 	if(sip->class_type < 0) {
4792 		strcpy(output, "Unknown");
4793 		return 0;
4794 	}
4795 
4796 	strcpy(output, Ship_types[sip->class_type].name);
4797 	return 1;
4798 }
4799 
4800 /**
4801  * Set the orders allowed for a ship -- based on ship type.
4802  *
4803  * This value might get overridden by a value in the mission file.
4804  */
ship_get_default_orders_accepted(ship_info * sip)4805 int ship_get_default_orders_accepted( ship_info *sip )
4806 {
4807 	if(sip->class_type >= 0) {
4808 		return Ship_types[sip->class_type].ai_player_orders;
4809 	} else {
4810 		return 0;
4811 	}
4812 }
4813 
get_submodel_offset(int model,int submodel)4814 vec3d get_submodel_offset(int model, int submodel){
4815 	polymodel*pm = model_get(model);
4816 	if(pm->submodel[submodel].parent == -1)
4817 		return pm->submodel[submodel].offset;
4818 	vec3d ret = pm->submodel[submodel].offset;
4819 	vec3d v = get_submodel_offset(model,pm->submodel[submodel].parent);
4820 	vm_vec_add2(&ret, &v);
4821 	return ret;
4822 
4823 }
4824 
ship_set_warp_effects(object * objp,ship_info * sip)4825 void ship_set_warp_effects(object *objp, ship_info *sip)
4826 {
4827 	ship *shipp = &Ships[objp->instance];
4828 
4829 	if(shipp->warpin_effect != NULL)
4830 		delete shipp->warpin_effect;
4831 
4832 	switch(sip->warpin_type)
4833 	{
4834 		case WT_DEFAULT:
4835 		case WT_KNOSSOS:
4836 		case WT_DEFAULT_THEN_KNOSSOS:
4837 			shipp->warpin_effect = new WE_Default(objp, WD_WARP_IN);
4838 			break;
4839 		case WT_IN_PLACE_ANIM:
4840 			shipp->warpin_effect = new WE_BSG(objp, WD_WARP_IN);
4841 			break;
4842 		case WT_SWEEPER:
4843 			shipp->warpin_effect = new WE_Homeworld(objp, WD_WARP_IN);
4844 			break;
4845 		case WT_HYPERSPACE:
4846 			shipp->warpin_effect = new WE_Hyperspace(objp, WD_WARP_IN);
4847 			break;
4848 		default:
4849 			shipp->warpin_effect = new WarpEffect();
4850 	}
4851 
4852 	if(shipp->warpout_effect != NULL)
4853 		delete shipp->warpout_effect;
4854 
4855 	switch(sip->warpout_type)
4856 	{
4857 		case WT_DEFAULT:
4858 		case WT_KNOSSOS:
4859 		case WT_DEFAULT_THEN_KNOSSOS:
4860 			shipp->warpout_effect = new WE_Default(objp, WD_WARP_OUT);
4861 			break;
4862 		case WT_IN_PLACE_ANIM:
4863 			shipp->warpout_effect = new WE_BSG(objp, WD_WARP_OUT);
4864 			break;
4865 		case WT_SWEEPER:
4866 			shipp->warpout_effect = new WE_Homeworld(objp, WD_WARP_OUT);
4867 			break;
4868 		case WT_HYPERSPACE:
4869 			shipp->warpout_effect = new WE_Hyperspace(objp, WD_WARP_OUT);
4870 			break;
4871 		default:
4872 			shipp->warpout_effect = new WarpEffect();
4873 	}
4874 }
4875 
4876 // Reset all ship values to empty/unused.
clear()4877 void ship::clear()
4878 {
4879 	int i, j;
4880 
4881 	objnum = -1;
4882 	ai_index = -1;
4883 	ship_info_index = -1;
4884 	hotkey = -1;
4885 	escort_priority = 0;
4886 	score = 0;
4887 	assist_score_pct = 0.0f;
4888 	respawn_priority = 0;
4889 
4890 	pre_death_explosion_happened = 0;
4891 	wash_killed = 0;	// serenity lies
4892 	cargo1 = 0;							// "Nothing"
4893 
4894 	wing_status_wing_index = -1;
4895 	wing_status_wing_pos = -1;
4896 
4897 	alt_type_index = -1;
4898 	callsign_index = -1;
4899 
4900 	targeting_laser_bank = -1;
4901 	targeting_laser_objnum = -1;
4902 
4903 	num_corkscrew_to_fire = 0;
4904 	corkscrew_missile_bank = -1;
4905 	next_corkscrew_fire = timestamp(0);
4906 
4907 	final_death_time = timestamp(-1);
4908 	death_time = timestamp(-1);
4909 	end_death_time = timestamp(-1);
4910 	really_final_death_time = timestamp(-1);
4911 	deathroll_rotvel = vmd_zero_vector;
4912 
4913 	if (warpin_effect != NULL)
4914 		delete warpin_effect;
4915 	if (warpout_effect != NULL)
4916 		delete warpout_effect;
4917 	warpin_effect = NULL;
4918 	warpout_effect = NULL;
4919 
4920 	next_fireball = timestamp(-1);
4921 	next_hit_spark = timestamp(-1);
4922 	num_hits = 0;
4923 	memset(sparks, 0, MAX_SHIP_HITS * sizeof(ship_spark));
4924 
4925 	use_special_explosion = false;
4926 	special_exp_damage = 0;
4927 	special_exp_blast = 0;
4928 	special_exp_inner = 0;
4929 	special_exp_outer = 0;
4930 	use_shockwave = false;
4931 	special_exp_shockwave_speed = 0;
4932 	special_exp_deathroll_time = 0;
4933 
4934 	special_hitpoints = 0;
4935 	special_shield = -1;
4936 
4937 	shield_points.clear();
4938 
4939 	ship_max_shield_strength = 0.0f;
4940 	ship_max_hull_strength = 0.0f;
4941 
4942 	ship_guardian_threshold = 0;
4943 
4944 	ship_name[0] = 0;
4945 	team = 0;
4946 
4947 	time_cargo_revealed = 0;
4948 
4949 	arrival_location = 0;
4950 	arrival_distance = 0;
4951 	arrival_anchor = -1;
4952 	arrival_path_mask = 0;
4953 	arrival_cue = -1;
4954 	arrival_delay = 0;
4955 
4956 	departure_location = 0;
4957 	departure_anchor = -1;
4958 	departure_path_mask = 0;
4959 	departure_cue = -1;
4960 	departure_delay = 0;
4961 
4962 	wingnum = -1;
4963 	orders_accepted = 0;
4964 
4965 	subsys_list.clear();
4966 	// since these aren't cleared by clear()
4967 	subsys_list.next = NULL;
4968 	subsys_list.prev = NULL;
4969 
4970 	memset(&subsys_info, 0, SUBSYSTEM_MAX * sizeof(ship_subsys_info));
4971 
4972 	memset(last_targeted_subobject, 0, MAX_PLAYERS * sizeof(ship_subsys *));
4973 
4974 	shield_integrity = NULL;
4975 
4976 	shield_recharge_index = INTIAL_SHIELD_RECHARGE_INDEX;
4977 	weapon_recharge_index = INTIAL_WEAPON_RECHARGE_INDEX;
4978 	engine_recharge_index = INTIAL_ENGINE_RECHARGE_INDEX;
4979 	weapon_energy = 0;
4980 	current_max_speed = 0.0f;
4981 	next_manage_ets = timestamp(0);
4982 
4983 	flags = 0;
4984 	flags2 = 0;
4985 	reinforcement_index = -1;
4986 
4987 	afterburner_fuel = 0.0f;
4988 
4989 	cmeasure_count = 0;
4990 	current_cmeasure = -1;
4991 
4992 	cmeasure_fire_stamp = timestamp(0);
4993 
4994 	target_shields_delta = 0.0f;
4995 	target_weapon_energy_delta = 0.0f;
4996 
4997 	memset(&weapons, 0, sizeof(ship_weapon));
4998 
4999 	// ---------- special weapons init that isn't setting things to 0
5000 	for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++)
5001 	{
5002 		weapons.primary_bank_weapons[i] = -1;
5003 
5004 		weapons.next_primary_fire_stamp[i] = timestamp(0);
5005 		weapons.last_primary_fire_stamp[i] = timestamp(-1);
5006 		weapons.last_primary_fire_sound_stamp[i] = timestamp(0);
5007 
5008 		weapons.primary_bank_slot_count[i] = 1;
5009 
5010 		weapons.primary_bank_rearm_time[i] = timestamp(0);
5011 		weapons.primary_animation_done_time[i] = timestamp(0);
5012 
5013 		// not part of weapons!
5014 		primary_rotate_rate[i] = 0.0f;
5015 		primary_rotate_ang[i] = 0.0f;
5016 		last_fired_point[i] = 0;
5017 		// for fighter beams
5018 		was_firing_last_frame[i] = 0;
5019 	}
5020 
5021 	for (i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++)
5022 	{
5023 		weapons.secondary_bank_weapons[i] = -1;
5024 
5025 		weapons.next_secondary_fire_stamp[i] = timestamp(0);
5026 		weapons.last_secondary_fire_stamp[i] = timestamp(-1);
5027 
5028 		weapons.secondary_bank_rearm_time[i] = timestamp(0);
5029 		weapons.secondary_animation_done_time[i] = timestamp(0);
5030 
5031 		for (j = 0; j < MAX_SLOTS; j++)
5032 			secondary_point_reload_pct[i][j] = 1.0f;
5033 	}
5034 
5035 	weapons.next_tertiary_fire_stamp = timestamp(0);
5036 
5037 	weapons.last_fired_weapon_index = -1;
5038 	weapons.last_fired_weapon_signature = -1;
5039 	// ---------- done with weapons init
5040 
5041 	shield_hits = 0;
5042 
5043 	wash_intensity = 0.0f;
5044 	wash_rot_axis = vmd_zero_vector;
5045 	wash_timestamp = timestamp(0);
5046 
5047 	num_swarm_missiles_to_fire = 0;
5048 	next_swarm_fire = timestamp(0);
5049 	next_swarm_path = 0;
5050 	num_turret_swarm_info = 0;
5051 	swarm_missile_bank = -1;
5052 
5053 	group = -1;
5054 	death_roll_snd = -1;
5055 	ship_list_index = -1;
5056 
5057 	thruster_bitmap = -1;
5058 	thruster_frame = 0.0f;
5059 
5060 	thruster_glow_bitmap = -1;
5061 	thruster_glow_frame = 0.0f;
5062 	thruster_glow_noise = 1.0f;
5063 
5064 	thruster_secondary_glow_bitmap = -1;
5065 	thruster_tertiary_glow_bitmap = -1;
5066 	thruster_distortion_bitmap = -1;
5067 
5068 	next_engine_stutter = timestamp(0);
5069 
5070 	base_texture_anim_frametime = 0;
5071 
5072 	total_damage_received = 0.0f;
5073 	memset(&damage_ship, 0, MAX_DAMAGE_SLOTS * sizeof(float));
5074 	for(i = 0; i < MAX_DAMAGE_SLOTS; i++)
5075 		damage_ship_id[i] = -1;
5076 
5077 	persona_index = -1;
5078 
5079 	subsys_disrupted_flags = 0;
5080 	subsys_disrupted_check_timestamp = timestamp(0);
5081 
5082 	create_time = 0;
5083 
5084 	ts_index = -1;
5085 
5086 	large_ship_blowup_index = -1;
5087 	for (i = 0; i < NUM_SUB_EXPL_HANDLES; i++)
5088 		sub_expl_sound_handle[i] = -1;
5089 
5090 	memset(&arc_pts, 0, MAX_SHIP_ARCS * 2 * sizeof(vec3d));
5091 	for (i = 0; i < MAX_SHIP_ARCS; i++)
5092 		arc_timestamp[i] = timestamp(-1);
5093 	memset(&arc_type, 0, MAX_SHIP_ARCS * sizeof(ubyte));
5094 	arc_next_time = timestamp(-1);
5095 
5096 	emp_intensity = -1.0f;
5097 	emp_decr = 0.0f;
5098 
5099 	memset(trail_ptr, 0, MAX_SHIP_CONTRAILS * sizeof(trail *));
5100 
5101 	tag_total = 0.0f;
5102 	tag_left = -1.0f;
5103 	time_first_tagged = 0;
5104 	level2_tag_total = 0.0f;
5105 	level2_tag_left = -1.0f;
5106 
5107 	for (i = 0; i < MAX_PLAYERS; i++ )
5108 	{
5109 		np_updates[i].seq = 0;
5110 		np_updates[i].update_stamp = -1;
5111 		np_updates[i].status_update_stamp = -1;
5112 		np_updates[i].subsys_update_stamp = -1;
5113 		np_updates[i].pos_chksum = 0;
5114 		np_updates[i].orient_chksum = 0;
5115 	}
5116 
5117 	lightning_stamp = timestamp(-1);
5118 
5119 	// set awacs warning flags so awacs ship only asks for help once at each level
5120 	awacs_warning_flag = AWACS_WARN_NONE;
5121 
5122 	special_warpin_objnum = -1;
5123 	special_warpout_objnum = -1;
5124 
5125 	fighter_beam_turret_data.clear();
5126 	memset(&beam_sys_info, 0, sizeof(model_subsystem));
5127 
5128 	primitive_sensor_range = DEFAULT_SHIP_PRIMITIVE_SENSOR_RANGE;
5129 
5130 	ship_replacement_textures = NULL;
5131 
5132 	current_viewpoint = -1;
5133 
5134 	for (i = 0; i < MAX_SHIP_CONTRAILS; i++)
5135 		ABtrail_ptr[i] = NULL;
5136 	memset(&ab_info, 0, MAX_SHIP_CONTRAILS * sizeof(trail_info));
5137 	ab_count = 0;
5138 
5139 	glow_point_bank_active.clear();
5140 
5141 	shader_effect_num = 0;
5142 	shader_effect_duration = 0;
5143 	shader_effect_start_time = 0;
5144 	shader_effect_active = false;
5145 
5146 	last_fired_turret = NULL;
5147 
5148 	bay_doors_anim_done_time = 0;
5149 	bay_doors_status = MA_POS_NOT_SET;
5150 	bay_doors_wanting_open = 0;
5151 	bay_doors_launched_from = 0;
5152 	bay_doors_need_open = false;
5153 	bay_doors_parent_shipnum = -1;
5154 
5155 	for(i = 0; i < MAX_MAN_THRUSTERS; i++)
5156 	{
5157 		thrusters_start[i] = timestamp(-1);
5158 		thrusters_sounds[i] = -1;
5159 	}
5160 
5161 	s_alt_classes.clear();
5162 
5163 	for(i=0;i<MAX_IFFS;i++)
5164 		for(j=0;j<MAX_IFFS;j++)
5165 			ship_iff_color[i][j] = -1;
5166 
5167 	ammo_low_complaint_count = 0;
5168 
5169 	armor_type_idx = -1;
5170 	shield_armor_type_idx = -1;
5171 	collision_damage_type_idx = -1;
5172 	debris_damage_type_idx = -1;
5173 	debris_net_sig = 0;
5174 
5175 	model_instance_num = -1;
5176 
5177 	time_created = 0;
5178 
5179 	radar_visible_since = -1;
5180 	radar_last_contact = -1;
5181 
5182 	radar_last_status = VISIBLE;
5183 	radar_current_status = VISIBLE;
5184 
5185 	team_name = "";
5186 	secondary_team_name = "";
5187 	team_change_timestamp = timestamp(-1);
5188 	team_change_time = 0;
5189 
5190 	autoaim_fov = 0.0f;
5191 }
5192 
5193 // NOTE: Now that the clear() member function exists, this function only sets the stuff associated with the object and ship class.
ship_set(int ship_index,int objnum,int ship_type)5194 void ship_set(int ship_index, int objnum, int ship_type)
5195 {
5196 	int i;
5197 	ship		*shipp = &Ships[ship_index];
5198 	object		*objp = &Objects[objnum];
5199 	ship_info	*sip = &(Ship_info[ship_type]);
5200 	ship_weapon	*swp = &shipp->weapons;
5201 	polymodel *pm = model_get(sip->model_num);
5202 
5203 	extern int oo_arrive_time_count[MAX_SHIPS];
5204 	extern int oo_interp_count[MAX_SHIPS];
5205 	oo_arrive_time_count[shipp - Ships] = 0;
5206 	oo_interp_count[shipp - Ships] = 0;
5207 
5208 	Assert(strlen(shipp->ship_name) <= NAME_LENGTH - 1);
5209 	shipp->ship_info_index = ship_type;
5210 	shipp->objnum = objnum;
5211 	shipp->score = sip->score;
5212 
5213 	ai_object_init(objp, shipp->ai_index);
5214 	physics_ship_init(objp);
5215 
5216 	if ( !Fred_running ) {
5217 		ship_set_warp_effects(objp, sip);
5218 	}
5219 
5220 	if (Fred_running){
5221 		shipp->ship_max_hull_strength = 100.0f;
5222 	} else {
5223 		shipp->ship_max_hull_strength = sip->max_hull_strength;
5224 	}
5225 	objp->hull_strength = shipp->ship_max_hull_strength;
5226 
5227 	if (Fred_running) {
5228 		shipp->ship_max_shield_strength = 100.0f;
5229 		objp->shield_quadrant[0] = 100.0f;
5230 	} else {
5231 		shipp->ship_max_shield_strength = sip->max_shield_strength;
5232 		shield_set_strength(objp, shipp->ship_max_shield_strength);
5233 	}
5234 
5235 	if (sip->flags2 & SIF2_MODEL_POINT_SHIELDS) {
5236 		objp->n_quadrants = pm->shield_points.size();
5237 		shipp->shield_points = pm->shield_points;
5238 		objp->shield_quadrant.resize(objp->n_quadrants);
5239 	}
5240 
5241 	shipp->orders_accepted = ship_get_default_orders_accepted( sip );
5242 
5243 	if (!subsys_set(objnum))
5244 	{
5245 		char err_msg[512];
5246 		sprintf (err_msg, "Unable to allocate ship subsystems. Maximum is %d. No subsystems have been assigned to %s.", (NUM_SHIP_SUBSYSTEM_SETS* NUM_SHIP_SUBSYSTEMS_PER_SET), shipp->ship_name);
5247 
5248 		if (Fred_running)
5249 			MessageBox(NULL, err_msg, "Error", MB_OK);
5250 		else
5251 			Error(LOCATION, err_msg);
5252 	}
5253 
5254 	ets_init_ship(objp);	// init ship fields that are used for the ETS
5255 
5256 	shipp->current_max_speed = Ship_info[ship_type].max_speed;
5257 
5258 	shipp->flags |= SF_ENGINES_ON;
5259 
5260 	// set certain flags that used to be in ship_info - Goober5000
5261 	if (sip->flags & SIF_STEALTH)
5262 		shipp->flags2 |= SF2_STEALTH;
5263 	if (sip->flags & SIF_SHIP_CLASS_DONT_COLLIDE_INVIS)
5264 		shipp->flags2 |= SF2_DONT_COLLIDE_INVIS;
5265 
5266 	if (sip->flags2 & SIF2_NO_ETS)
5267 		shipp->flags2 |= SF2_NO_ETS;
5268 
5269 	shipp->afterburner_fuel = sip->afterburner_fuel_capacity;
5270 
5271 	shipp->cmeasure_count = sip->cmeasure_max;
5272 	shipp->current_cmeasure = sip->cmeasure_type;
5273 
5274 	if (sip->num_primary_banks == 0 || swp->primary_bank_weapons[0] < 0) {
5275 		swp->current_primary_bank = -1;
5276 		swp->previous_primary_bank = -1;
5277 	}
5278 	if (sip->num_secondary_banks == 0 || swp->secondary_bank_weapons[0] < 0) {
5279 		swp->current_secondary_bank = -1;
5280 		swp->previous_secondary_bank = -1;
5281 	}
5282 	swp->current_tertiary_bank = -1;
5283 
5284 	swp->ai_class = Ai_info[shipp->ai_index].ai_class;
5285 
5286 	// handle ballistic primaries - kinda hackish; is this actually necessary?
5287 	// because I think it's not needed - when I accidentally left this unreachable
5288 	// it didn't cause any problems - Goober5000
5289 	for ( i = 0; i < sip->num_primary_banks; i++ )
5290 	{
5291 		float weapon_size = Weapon_info[sip->primary_bank_weapons[i]].cargo_size;
5292 
5293 		if ( weapon_size > 0.0f )
5294 		{
5295 			if (Fred_running)
5296 				swp->primary_bank_ammo[i] = 100;
5297 			else
5298 				swp->primary_bank_ammo[i] = fl2i(sip->primary_bank_ammo_capacity[i] / weapon_size + 0.5f );
5299 		}
5300 	}
5301 
5302 	for ( i = 0; i < sip->num_secondary_banks; i++ )
5303 	{
5304 		float weapon_size = Weapon_info[sip->secondary_bank_weapons[i]].cargo_size;
5305 		Assertion( weapon_size > 0.0f, "Cargo size for secondary weapon %s is invalid, must be greater than 0.\n", Weapon_info[sip->secondary_bank_weapons[i]].name );
5306 
5307 		if (Fred_running)
5308 			swp->secondary_bank_ammo[i] = 100;
5309 		else
5310 			swp->secondary_bank_ammo[i] = fl2i(sip->secondary_bank_ammo_capacity[i] / weapon_size + 0.5f );
5311 	}
5312 
5313 	shipp->armor_type_idx = sip->armor_type_idx;
5314 	shipp->shield_armor_type_idx = sip->shield_armor_type_idx;
5315 	shipp->collision_damage_type_idx =  sip->collision_damage_type_idx;
5316 	shipp->debris_damage_type_idx = sip->debris_damage_type_idx;
5317 
5318 	if(pm != NULL && pm->n_view_positions > 0)
5319 		ship_set_eye(objp, 0);
5320 	else
5321 		ship_set_eye(objp, -1);
5322 
5323 	sip->shockwave.damage_type_idx = sip->shockwave.damage_type_idx_sav;
5324 
5325 	// Team colors
5326 	shipp->team_name.assign( sip->default_team_name);
5327 	shipp->secondary_team_name = "<none>";
5328 
5329 	shipp->autoaim_fov = sip->autoaim_fov;
5330 }
5331 
5332 /**
5333  * Recalculates the overall strength of subsystems.
5334  *
5335  * Needed because several places in FreeSpace change subsystem strength and all
5336  * this data needs to be kept up to date.
5337  */
ship_recalc_subsys_strength(ship * shipp)5338 void ship_recalc_subsys_strength( ship *shipp )
5339 {
5340 	int i;
5341 	ship_subsys *ship_system;
5342 
5343 	// fill in the subsys_info fields for all particular types of subsystems
5344 	// make the current strength be 1.0.  If there are initial conditions on the ship, then
5345 	// the mission parse code should take care of setting that.
5346 	for (i = 0; i < SUBSYSTEM_MAX; i++) {
5347 		shipp->subsys_info[i].type_count = 0;
5348 		shipp->subsys_info[i].aggregate_max_hits = 0.0f;
5349 		shipp->subsys_info[i].aggregate_current_hits = 0.0f;
5350 	}
5351 
5352 	// count all of the subsystems of a particular type.  For each generic type of subsystem, we store the
5353 	// total count of hits.  (i.e. for 3 engines, we store the sum of the max_hits for each engine)
5354 	for ( ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system) ) {
5355 
5356 		if (!(ship_system->flags & SSF_NO_AGGREGATE)) {
5357 			int type = ship_system->system_info->type;
5358 			Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
5359 
5360 			shipp->subsys_info[type].type_count++;
5361 			shipp->subsys_info[type].aggregate_max_hits += ship_system->max_hits;
5362 			shipp->subsys_info[type].aggregate_current_hits += ship_system->current_hits;
5363 		}
5364 
5365 		//Get rid of any persistent sounds on the subsystem
5366 		//This is inefficient + sloppy but there's not really an easy way to handle things
5367 		//if a subsystem is brought back from the dead, other than this
5368 		if(ship_system->current_hits > 0.0f)
5369 		{
5370 			if(ship_system->subsys_snd_flags & SSSF_DEAD)
5371 			{
5372 				obj_snd_delete_type(shipp->objnum, ship_system->system_info->dead_snd, ship_system);
5373 				ship_system->subsys_snd_flags &= ~SSSF_DEAD;
5374 			}
5375 			if((ship_system->system_info->alive_snd != -1) && !(ship_system->subsys_snd_flags & SSSF_ALIVE))
5376 			{
5377 				obj_snd_assign(shipp->objnum, ship_system->system_info->alive_snd, &ship_system->system_info->pnt, 0, OS_SUBSYS_ALIVE, ship_system);
5378 				ship_system->subsys_snd_flags |= SSSF_ALIVE;
5379 			}
5380 			if(!(ship_system->subsys_snd_flags & SSSF_TURRET_ROTATION))
5381 			{
5382 				if(ship_system->system_info->turret_base_rotation_snd != -1)
5383 				{
5384 					obj_snd_assign(shipp->objnum, ship_system->system_info->turret_base_rotation_snd, &ship_system->system_info->pnt, 0, OS_TURRET_BASE_ROTATION, ship_system);
5385 					ship_system->subsys_snd_flags |= SSSF_TURRET_ROTATION;
5386 				}
5387 				if(ship_system->system_info->turret_gun_rotation_snd != -1)
5388 				{
5389 					obj_snd_assign(shipp->objnum, ship_system->system_info->turret_gun_rotation_snd, &ship_system->system_info->pnt, 0, OS_TURRET_GUN_ROTATION, ship_system);
5390 					ship_system->subsys_snd_flags |= SSSF_TURRET_ROTATION;
5391 				}
5392 			}
5393 			if((ship_system->flags & SSF_ROTATES) && (ship_system->system_info->rotation_snd != -1) && !(ship_system->subsys_snd_flags & SSSF_ROTATE))
5394 			{
5395 				obj_snd_assign(shipp->objnum, ship_system->system_info->rotation_snd, &ship_system->system_info->pnt, 0, OS_SUBSYS_ROTATION, ship_system);
5396 				ship_system->subsys_snd_flags |= SSSF_ROTATE;
5397 			}
5398 		}
5399 		else
5400 		{
5401 			if(ship_system->subsys_snd_flags & SSSF_ALIVE)
5402 			{
5403 				obj_snd_delete_type(shipp->objnum, ship_system->system_info->alive_snd, ship_system);
5404 				ship_system->subsys_snd_flags &= ~SSSF_ALIVE;
5405 			}
5406 			if(ship_system->subsys_snd_flags & SSSF_TURRET_ROTATION)
5407 			{
5408 				obj_snd_delete_type(shipp->objnum, ship_system->system_info->turret_base_rotation_snd, ship_system);
5409 				obj_snd_delete_type(shipp->objnum, ship_system->system_info->turret_gun_rotation_snd, ship_system);
5410 				ship_system->subsys_snd_flags &= ~SSSF_TURRET_ROTATION;
5411 			}
5412 			if(ship_system->subsys_snd_flags & SSSF_ROTATE)
5413 			{
5414 				obj_snd_delete_type(shipp->objnum, ship_system->system_info->rotation_snd, ship_system);
5415 				ship_system->subsys_snd_flags &= ~SSSF_ROTATE;
5416 			}
5417 			if((ship_system->system_info->dead_snd != -1) && !(ship_system->subsys_snd_flags & SSSF_DEAD))
5418 			{
5419 				obj_snd_assign(shipp->objnum, ship_system->system_info->dead_snd, &ship_system->system_info->pnt, 0, OS_SUBSYS_DEAD, ship_system);
5420 				ship_system->subsys_snd_flags |= SSSF_DEAD;
5421 			}
5422 		}
5423 	}
5424 
5425 	// set any ship flags which should be set.  unset the flags since we might be repairing a subsystem
5426 	// through sexpressions.
5427 	if ( (shipp->subsys_info[SUBSYSTEM_ENGINE].type_count > 0) && (shipp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f) ) {
5428 		shipp->flags |= SF_DISABLED;
5429 	} else {
5430 		shipp->flags &= ~SF_DISABLED;
5431 		ship_reset_disabled_physics( &Objects[shipp->objnum], shipp->ship_info_index );
5432 	}
5433 }
5434 
5435 /**
5436  * Fixup the model subsystem information for this ship pointer.
5437  * Needed when ships share the same model.
5438  */
ship_copy_subsystem_fixup(ship_info * sip)5439 void ship_copy_subsystem_fixup(ship_info *sip)
5440 {
5441 	int i, model_num;
5442 
5443 	model_num = sip->model_num;
5444 
5445 	// no point copying the subsystem data if the ship in question has none...
5446 	// mark that the ship (cargo container) has the path fixup done.
5447 	if (sip->n_subsystems == 0) {
5448 		sip->flags |= SIF_PATH_FIXUP;
5449 		return;
5450 	}
5451 
5452 	// if we need to get information for all our subsystems, we need to find another ship with the same model
5453 	// number as our own and that has the model information
5454 	for ( i = 0; i < Num_ship_classes; i++ ) {
5455 		model_subsystem *source_msp, *dest_msp;
5456 
5457 		if ( (Ship_info[i].model_num != model_num) || (&Ship_info[i] == sip) ){
5458 			continue;
5459 		}
5460 
5461 		// see if this ship has subsystems and a model for the subsystems.  We only need check the first
5462 		// subsystem since previous error checking would have trapped its loading as an error.
5463 		Assert( Ship_info[i].n_subsystems == sip->n_subsystems );
5464 
5465 		source_msp = &Ship_info[i].subsystems[0];
5466 		dest_msp = &(sip->subsystems[0]);
5467 		if (source_msp->model_num != -1) {
5468 			model_copy_subsystems( sip->n_subsystems, dest_msp, source_msp );
5469 		} else if (dest_msp->model_num != -1) {
5470 			model_copy_subsystems( sip->n_subsystems, source_msp, dest_msp );
5471 		} else {
5472 			// if none were found try finding a another ship to copy the data from
5473 			continue;
5474 		}
5475 		sip->flags |= SIF_PATH_FIXUP;
5476 		break;
5477 	}
5478 
5479 }
5480 
5481 // as with object, don't set next and prev to NULL because they keep the object on the free and used lists
clear()5482 void ship_subsys::clear()
5483 {
5484 	int i;
5485 
5486 	system_info = NULL;
5487 
5488 	parent_objnum = -1;
5489 
5490 	sub_name[0] = 0;
5491 	current_hits = max_hits = 0.0f;
5492 
5493 	flags = 0;
5494 
5495 	subsys_guardian_threshold = 0;
5496 	armor_type_idx = -1;
5497 
5498 	turret_best_weapon = -1;
5499 	turret_last_fire_direction = vmd_zero_vector;
5500 	turret_next_enemy_check_stamp = timestamp(0);
5501 	turret_next_fire_stamp = timestamp(0);
5502 	turret_enemy_objnum = -1;
5503 	turret_enemy_sig = 0;
5504 	turret_next_fire_pos = 0;
5505 	turret_time_enemy_in_range = 0.0f;
5506 
5507 	for (i = 0; i < NUM_TURRET_ORDER_TYPES; i++)
5508 		turret_targeting_order[i] = -1;
5509 	optimum_range = 0.0f;
5510 	favor_current_facing = 0.0f;
5511 	targeted_subsys = NULL;
5512 	scripting_target_override = false;
5513 	last_fired_weapon_info_index = -1;
5514 
5515 	turret_pick_big_attack_point_timestamp = timestamp(0);
5516 	turret_big_attack_point = vmd_zero_vector;
5517 
5518 	turret_animation_position = MA_POS_NOT_SET;
5519 	turret_animation_done_time = 0;
5520 
5521 	for (i = 0; i < MAX_TFP; i++)
5522 		turret_swarm_info_index[i] = -1;
5523 	turret_swarm_num = 0;
5524 
5525 	awacs_intensity = 0.0f;
5526 	awacs_radius = 0.0f;
5527 
5528 	memset(&weapons, 0, sizeof(ship_weapon));
5529 
5530 	memset(&submodel_info_1, 0, sizeof(submodel_instance_info));
5531 	memset(&submodel_info_2, 0, sizeof(submodel_instance_info));
5532 
5533 	disruption_timestamp = timestamp(0);
5534 
5535 	subsys_cargo_name = 0;
5536 	time_subsys_cargo_revealed = 0;
5537 
5538 	triggered_rotation_index = -1;
5539 
5540 	points_to_target = 0.0f;
5541 	base_rotation_rate_pct = 0.0f;
5542 	gun_rotation_rate_pct = 0.0f;
5543 
5544 	subsys_snd_flags = 0;
5545 
5546 	rotation_timestamp = timestamp(0);
5547 
5548 	world_to_turret_matrix = vmd_identity_matrix;
5549 
5550 	for (i = 0; i < 32; i++)
5551 		target_priority[i] = -1;
5552 	num_target_priorities = 0;
5553 
5554 	next_aim_pos_time = 0;
5555 	last_aim_enemy_pos = vmd_zero_vector;
5556 	last_aim_enemy_vel = vmd_zero_vector;
5557 
5558 	rof_scaler = 1.0f;
5559 	turn_rate = 0.0f;
5560 
5561 	turret_max_bomb_ownage = -1;
5562 	turret_max_target_ownage = -1;
5563 }
5564 
5565 /**
5566  * Set subsystem
5567  *
5568  * @param objnum				Object number (used as index into Objects[])
5569  * @param ignore_subsys_info	Default parameter with value of 0.  This is only set to 1 by the save/restore code
5570  */
subsys_set(int objnum,int ignore_subsys_info)5571 int subsys_set(int objnum, int ignore_subsys_info)
5572 {
5573 	ship	*shipp = &Ships[Objects[objnum].instance];
5574 	ship_info	*sinfo = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
5575 	model_subsystem *model_system;
5576 	ship_subsys *ship_system;
5577 	int i, j, k;
5578 
5579 	// set up the subsystems for this ship.  walk through list of subsystems in the ship-info array.
5580 	// for each subsystem, get a new ship_subsys instance and set up the pointers and other values
5581 	list_init ( &shipp->subsys_list );								// initialize the ship's list of subsystems
5582 
5583 	// make sure to have allocated the number of subsystems we require
5584 	if (!ship_allocate_subsystems( sinfo->n_subsystems )) {
5585 		return 0;
5586 	}
5587 
5588 	for ( i = 0; i < sinfo->n_subsystems; i++ )
5589 	{
5590 		model_system = &(sinfo->subsystems[i]);
5591 		if (model_system->model_num < 0) {
5592 			Warning (LOCATION, "Invalid subobj_num or model_num in subsystem '%s' on ship type '%s'.\nNot linking into ship!\n\n(This warning means that a subsystem was present in the table entry and not present in the model\nit should probably be removed from the table or added to the model.)\n", model_system->subobj_name, sinfo->name );
5593 			continue;
5594 		}
5595 
5596 		// set up the linked list
5597 		ship_system = GET_FIRST( &ship_subsys_free_list );		// get a new element from the ship_subsystem array
5598 		Assert ( ship_system != &ship_subsys_free_list );		// shouldn't have the dummy element
5599 		list_remove( ship_subsys_free_list, ship_system );		// remove the element from the array
5600 		list_append( &shipp->subsys_list, ship_system );		// link the element into the ship
5601 		ship_system->clear();									// initialize it to a known blank slate
5602 
5603 		ship_system->system_info = model_system;				// set the system_info pointer to point to the data read in from the model
5604 
5605 		ship_system->parent_objnum = objnum;
5606 
5607 		// if the table has set an name copy it
5608 		if (ship_system->system_info->alt_sub_name[0] != '\0') {
5609 			strcpy_s(ship_system->sub_name, ship_system->system_info->alt_sub_name);
5610 		}
5611 		else {
5612 			memset(ship_system->sub_name, '\0', sizeof(ship_system->sub_name));
5613 		}
5614 
5615 		// copy subsystem target priorities stuff
5616 		ship_system->num_target_priorities = ship_system->system_info->num_target_priorities;
5617 		for (j = 0; j < 32; j++) {
5618 			ship_system->target_priority[j] = ship_system->system_info->target_priority[j];
5619 		}
5620 
5621 		ship_system->rof_scaler = ship_system->system_info->turret_rof_scaler;
5622 
5623 		// zero flags
5624 		ship_system->flags = 0;
5625 		ship_system->weapons.flags = 0;
5626 		ship_system->subsys_snd_flags = 0;
5627 
5628 		// Goober5000
5629 		if (model_system->flags & MSS_FLAG_UNTARGETABLE)
5630 			ship_system->flags |= SSF_UNTARGETABLE;
5631 		// Wanderer
5632 		if (model_system->flags & MSS_FLAG_NO_SS_TARGETING)
5633 			ship_system->flags |= SSF_NO_SS_TARGETING;
5634 		if ((The_mission.ai_profile->flags2 & AIPF2_ADVANCED_TURRET_FOV_EDGE_CHECKS) || (model_system->flags & MSS_FLAG_FOV_EDGE_CHECK))
5635 			ship_system->flags |= SSF_FOV_EDGE_CHECK;
5636 		if ((The_mission.ai_profile->flags2 & AIPF2_REQUIRE_TURRET_TO_HAVE_TARGET_IN_FOV) || (model_system->flags & MSS_FLAG_FOV_REQUIRED))
5637 			ship_system->flags |= SSF_FOV_REQUIRED;
5638 
5639 		if (model_system->flags & MSS_FLAG_NO_REPLACE)
5640 			ship_system->flags |= SSF_NO_REPLACE;
5641 		if (model_system->flags & MSS_FLAG_NO_LIVE_DEBRIS)
5642 			ship_system->flags |= SSF_NO_LIVE_DEBRIS;
5643 		if (model_system->flags & MSS_FLAG_IGNORE_IF_DEAD)
5644 			ship_system->flags |= SSF_MISSILES_IGNORE_IF_DEAD;
5645 		if (model_system->flags & MSS_FLAG_ALLOW_VANISHING)
5646 			ship_system->flags |= SSF_VANISHED;
5647 		if (model_system->flags & MSS_FLAG_DAMAGE_AS_HULL)
5648 			ship_system->flags |= SSF_DAMAGE_AS_HULL;
5649 		if (model_system->flags & MSS_FLAG_NO_AGGREGATE)
5650 			ship_system->flags |= SSF_NO_AGGREGATE;
5651 		if (model_system->flags & MSS_FLAG_ROTATES)
5652 			ship_system->flags |= SSF_ROTATES;
5653 		if (model_system->flags2 & MSS_FLAG2_PLAYER_TURRET_SOUND)
5654 			ship_system->flags |= SSF_PLAY_SOUND_FOR_PLAYER;
5655 		if (model_system->flags2 & MSS_FLAG2_NO_DISAPPEAR)
5656 			ship_system->flags |= SSF_NO_DISAPPEAR;
5657 
5658 		ship_system->turn_rate = model_system->turn_rate;
5659 
5660 		// Goober5000 - this has to be moved outside back to parse_create_object, because
5661 		// a lot of the ship creation code is duplicated in several points and overwrites
5662 		// previous things... ugh.
5663 		ship_system->max_hits = model_system->max_subsys_strength;	// * shipp->ship_max_hull_strength / sinfo->max_hull_strength;
5664 
5665 		if ( !Fred_running ){
5666 			ship_system->current_hits = ship_system->max_hits;		// set the current hits
5667 		} else {
5668 			ship_system->current_hits = 0.0f;				// Jason wants this to be 0 in Fred.
5669 		}
5670 
5671 		ship_system->subsys_guardian_threshold = 0;
5672 		ship_system->armor_type_idx = model_system->armor_type_idx;
5673 		ship_system->turret_next_fire_stamp = timestamp(0);
5674 		ship_system->turret_next_enemy_check_stamp = timestamp(0);
5675 		ship_system->turret_enemy_objnum = -1;
5676 		ship_system->turret_next_fire_stamp = timestamp((int) frand_range(1.0f, 500.0f));	// next time this turret can fire
5677 		ship_system->turret_last_fire_direction = model_system->turret_norm;
5678 		ship_system->turret_next_fire_pos = 0;
5679 		ship_system->turret_time_enemy_in_range = 0.0f;
5680 		ship_system->disruption_timestamp=timestamp(0);
5681 		ship_system->turret_pick_big_attack_point_timestamp = timestamp(0);
5682 		ship_system->scripting_target_override = false;
5683 		vm_vec_zero(&ship_system->turret_big_attack_point);
5684 		for(j = 0; j < NUM_TURRET_ORDER_TYPES; j++)
5685 		{
5686 			//WMC - Set targeting order to default.
5687 			ship_system->turret_targeting_order[j] = j;
5688 		}
5689 		ship_system->optimum_range = model_system->optimum_range;
5690 		ship_system->favor_current_facing = model_system->favor_current_facing;
5691 		ship_system->subsys_cargo_name = 0;
5692 		ship_system->time_subsys_cargo_revealed = 0;
5693 
5694 		j = 0;
5695 		int number_of_weapons = 0;
5696 
5697 		for (k=0; k<MAX_SHIP_PRIMARY_BANKS; k++){
5698 			if (model_system->primary_banks[k] != -1) {
5699 				ship_system->weapons.primary_bank_weapons[j] = model_system->primary_banks[k];
5700 				ship_system->weapons.primary_bank_capacity[j] = model_system->primary_bank_capacity[k];	// added by Goober5000
5701 				ship_system->weapons.next_primary_fire_stamp[j] = timestamp(0);
5702 				ship_system->weapons.last_primary_fire_stamp[j++] = -1;
5703 			}
5704 			ship_system->weapons.burst_counter[k] = 0;
5705 		}
5706 
5707 		ship_system->weapons.num_primary_banks = j;
5708 		number_of_weapons += j;
5709 
5710 		j = 0;
5711 		for (k=0; k<MAX_SHIP_SECONDARY_BANKS; k++){
5712 			if (model_system->secondary_banks[k] != -1) {
5713 				ship_system->weapons.secondary_bank_weapons[j] = model_system->secondary_banks[k];
5714 				ship_system->weapons.secondary_bank_capacity[j] = model_system->secondary_bank_capacity[k];
5715 				ship_system->weapons.next_secondary_fire_stamp[j] = timestamp(0);
5716 				ship_system->weapons.last_secondary_fire_stamp[j++] = -1;
5717 			}
5718 			ship_system->weapons.burst_counter[k + MAX_SHIP_PRIMARY_BANKS] = 0;
5719 		}
5720 
5721 		ship_system->weapons.num_secondary_banks = j;
5722 		number_of_weapons += j;
5723 		ship_system->weapons.current_primary_bank = -1;
5724 		ship_system->weapons.current_secondary_bank = -1;
5725 
5726 		ship_system->next_aim_pos_time = 0;
5727 
5728 		ship_system->turret_max_bomb_ownage = model_system->turret_max_bomb_ownage;
5729 		ship_system->turret_max_target_ownage = model_system->turret_max_target_ownage;
5730 
5731 		// Make turret flag checks and warnings
5732 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_SALVO) && (ship_system->system_info->flags & MSS_FLAG_TURRET_FIXED_FP))
5733 		{
5734 			Warning (LOCATION, "\"salvo mode\" flag used with \"fixed firingpoints\" flag\nsubsystem '%s' on ship type '%s'.\n\"salvo mode\" flag is ignored\n", model_system->subobj_name, sinfo->name );
5735 			ship_system->system_info->flags &= (~MSS_FLAG_TURRET_SALVO);
5736 		}
5737 
5738 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_SALVO) && (model_system->turret_num_firing_points < 2))
5739 		{
5740 			Warning (LOCATION, "\"salvo mode\" flag used with turret which has less than two firingpoints\nsubsystem '%s' on ship type '%s'.\n\"salvo mode\" flag is ignored\n", model_system->subobj_name, sinfo->name );
5741 			ship_system->system_info->flags &= (~MSS_FLAG_TURRET_SALVO);
5742 		}
5743 
5744 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_FIXED_FP) && (model_system->turret_num_firing_points < 2))
5745 		{
5746 			Warning (LOCATION, "\"fixed firingpoints\" flag used with turret which has less than two firingpoints\nsubsystem '%s' on ship type '%s'.\n\"fixed firingpoints\" flag is ignored\n", model_system->subobj_name, sinfo->name );
5747 			ship_system->system_info->flags &= (~MSS_FLAG_TURRET_FIXED_FP);
5748 		}
5749 
5750 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_SALVO) && (ship_system->system_info->flags & MSS_FLAG_USE_MULTIPLE_GUNS))
5751 		{
5752 			Warning (LOCATION, "\"salvo mode\" flag used with \"use multiple guns\" flag\nsubsystem '%s' on ship type '%s'.\n\"use multiple guns\" flag is ignored\n", model_system->subobj_name, sinfo->name );
5753 			ship_system->system_info->flags &= (~MSS_FLAG_USE_MULTIPLE_GUNS);
5754 		}
5755 
5756 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_FIXED_FP) && !(ship_system->system_info->flags & MSS_FLAG_USE_MULTIPLE_GUNS))
5757 		{
5758 			Warning (LOCATION, "\"fixed firingpoints\" flag used without \"use multiple guns\" flag\nsubsystem '%s' on ship type '%s'.\n\"use multiple guns\" guns added by default\n", model_system->subobj_name, sinfo->name );
5759 			ship_system->system_info->flags |= MSS_FLAG_USE_MULTIPLE_GUNS;
5760 		}
5761 
5762 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_SALVO) && (number_of_weapons > 1))
5763 		{
5764 			Warning (LOCATION, "\"salvo mode\" flag used with turret which has more than one weapon defined for it\nsubsystem '%s' on ship type '%s'.\nonly single weapon will be used\n", model_system->subobj_name, sinfo->name );
5765 		}
5766 
5767 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_FIXED_FP) && (number_of_weapons > model_system->turret_num_firing_points))
5768 		{
5769 			Warning (LOCATION, "\"fixed firingpoint\" flag used with turret which has more weapons defined for it than it has firingpoints\nsubsystem '%s' on ship type '%s'.\nweapons will share firingpoints\n", model_system->subobj_name, sinfo->name );
5770 		}
5771 
5772 		if ((ship_system->system_info->flags & MSS_FLAG_TURRET_FIXED_FP) && (number_of_weapons < model_system->turret_num_firing_points))
5773 		{
5774 			Warning (LOCATION, "\"fixed firingpoint\" flag used with turret which has less weapons defined for it than it has firingpoints\nsubsystem '%s' on ship type '%s'.\nsome of the firingpoints will be left unused\n", model_system->subobj_name, sinfo->name );
5775 		}
5776 
5777 
5778 		for (k=0; k<MAX_SHIP_SECONDARY_BANKS; k++) {
5779 			ship_system->weapons.secondary_bank_ammo[k] = (Fred_running ? 100 : ship_system->weapons.secondary_bank_capacity[k]);
5780 
5781 			ship_system->weapons.secondary_next_slot[k] = 0;
5782 		}
5783 
5784 		// Goober5000
5785 		for (k=0; k<MAX_SHIP_PRIMARY_BANKS; k++)
5786 		{
5787 			ship_system->weapons.primary_bank_ammo[k] = (Fred_running ? 100 : ship_system->weapons.primary_bank_capacity[k]);
5788 		}
5789 
5790 		ship_system->weapons.last_fired_weapon_index = -1;
5791 		ship_system->weapons.last_fired_weapon_signature = -1;
5792 		ship_system->weapons.detonate_weapon_time = -1;
5793 		ship_system->weapons.ai_class = sinfo->ai_class;  // assume ai class of ship for turret
5794 
5795 		// rapid fire (swarm) stuff
5796 		for (k = 0; k < MAX_TFP; k++)
5797 			ship_system->turret_swarm_info_index[k] = -1;
5798 
5799 		ship_system->turret_swarm_num = 0;
5800 
5801 		// AWACS stuff
5802 		ship_system->awacs_intensity = model_system->awacs_intensity;
5803 		ship_system->awacs_radius = model_system->awacs_radius;
5804 		if (ship_system->awacs_intensity > 0) {
5805 			ship_system->system_info->flags |= MSS_FLAG_AWACS;
5806 		}
5807 
5808 		// turn_rate, turn_accel
5809 		float turn_accel = 0.5f;
5810 		model_set_instance_info(&ship_system->submodel_info_1, model_system->turn_rate, turn_accel);
5811 
5812 		model_clear_instance_info( &ship_system->submodel_info_2 );
5813 
5814 		// Clear this flag here so we correctly rebuild the turret matrix on mission load
5815 		model_system->flags &= ~MSS_FLAG_TURRET_MATRIX;
5816 
5817 		// Allocate a triggered rotation instance if we need it
5818 		if (model_system->flags & MSS_FLAG_TRIGGERED) {
5819 			ship_system->triggered_rotation_index = Triggered_rotations.size();
5820 			triggered_rotation tr;
5821 			Triggered_rotations.push_back(tr);
5822 		}
5823 	}
5824 
5825 	if ( !ignore_subsys_info ) {
5826 		ship_recalc_subsys_strength( shipp );
5827 	}
5828 
5829 	// Fix up animation code references
5830 	for (i = 0; i < sinfo->n_subsystems; i++) {
5831 		for (j = 0; j < sinfo->subsystems[i].n_triggers; j++) {
5832 			if (subsystem_stricmp(sinfo->subsystems[i].triggers[j].sub_name, "<none>")) {
5833 				int idx = ship_get_subobj_model_num(sinfo, sinfo->subsystems[i].triggers[j].sub_name);
5834 				if (idx != -1) {
5835 					sinfo->subsystems[i].triggers[j].subtype = idx;
5836 				} else {
5837 					WarningEx(LOCATION, "Could not find subobject %s in ship class %s. Animation triggers will not work correctly.\n", sinfo->subsystems[i].triggers[j].sub_name, sinfo->name);
5838 				}
5839 			}
5840 		}
5841 	}
5842 
5843 	return 1;
5844 }
5845 
5846 /**
5847  * Modify the matrix orient by the slew angles a.
5848  */
compute_slew_matrix(matrix * orient,angles * a)5849 void compute_slew_matrix(matrix *orient, angles *a)
5850 {
5851 	matrix	tmp, tmp2;
5852 	angles	t1, t2;
5853 
5854 	t1 = t2 = *a;
5855 	t1.h = 0.0f;	t1.b = 0.0f;
5856 	t2.p = 0.0f;	t2.b = 0.0f;
5857 
5858 	// put in p & b like normal
5859 	vm_angles_2_matrix(&tmp, &t2 ); // Changed the order of axis rotations. First pitch, then yaw (Swifty)
5860 	vm_matrix_x_matrix( &tmp2, orient, &tmp);
5861 
5862 	// Put in heading separately
5863 	vm_angles_2_matrix(&tmp, &t1 );
5864 	vm_matrix_x_matrix( orient, &tmp2, &tmp );
5865 
5866 	vm_orthogonalize_matrix(orient);
5867 }
5868 
5869 
5870 #ifndef NDEBUG
5871 /**
5872  * Render docking information, NOT while in object's reference frame.
5873  */
render_dock_bays(object * objp)5874 void render_dock_bays(object *objp)
5875 {
5876 	polymodel	*pm;
5877 	dock_bay		*db;
5878 
5879 	pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
5880 
5881 	if (pm->docking_bays == NULL)
5882 		return;
5883 
5884 	if (pm->docking_bays[0].num_slots != 2)
5885 		return;
5886 
5887 	db = &pm->docking_bays[0];
5888 
5889 	vertex	v0, v1;
5890 	vec3d	p0, p1, p2, p3, nr;
5891 
5892 	vm_vec_unrotate(&p0, &db->pnt[0], &objp->orient);
5893 	vm_vec_add2(&p0, &objp->pos);
5894 	g3_rotate_vertex(&v0, &p0);
5895 
5896 	vm_vec_unrotate(&p1, &db->pnt[1], &objp->orient);
5897 	vm_vec_add2(&p1, &objp->pos);
5898 	g3_rotate_vertex(&v1, &p1);
5899 
5900 	gr_set_color(255, 0, 0);
5901 	g3_draw_line(&v0, &v1);
5902 
5903 	vm_vec_avg(&p2, &p0, &p1);
5904 
5905 	vm_vec_unrotate(&nr, &db->norm[0], &objp->orient);
5906 	vm_vec_scale_add(&p3, &p2, &nr, 10.0f);
5907 
5908 	g3_rotate_vertex(&v0, &p2);
5909 	g3_rotate_vertex(&v1, &p3);
5910 	gr_set_color(255, 255, 0);
5911 	g3_draw_line(&v0, &v1);
5912 	g3_draw_sphere(&v1, 1.25f);
5913 
5914 }
5915 #endif
5916 
5917 int Ship_shadows = 0;
5918 
5919 DCF_BOOL( ship_shadows, Ship_shadows )
5920 
5921 MONITOR( NumShipsRend )
5922 
5923 int Show_shield_hits = 0;
5924 DCF_BOOL( show_shield_hits, Show_shield_hits )
5925 
5926 int Show_tnorms = 0;
5927 DCF_BOOL( show_tnorms, Show_tnorms )
5928 
5929 int Show_paths = 0;
5930 DCF_BOOL( show_paths, Show_paths )
5931 
5932 int Show_fpaths = 0;
DCF_BOOL(show_fpaths,Show_fpaths)5933 DCF_BOOL( show_fpaths, Show_fpaths )
5934 
5935 void ship_find_warping_ship_helper(object *objp, dock_function_info *infop)
5936 {
5937 	// only check ships
5938 	if (objp->type != OBJ_SHIP)
5939 		return;
5940 
5941 	// am I arriving or departing by warp?
5942 	if ( Ships[objp->instance].flags & (SF_ARRIVING|SF_DEPART_WARP) )
5943 	{
5944 #ifndef NDEBUG
5945 		// in debug builds, make sure only one of the docked objects has these flags set
5946 		if (infop->maintained_variables.bool_value)
5947 		{
5948 			//WMC - This is annoying and triggered in sm2-10
5949 			//Warning(LOCATION, "Ship %s and its docked ship %s are arriving or departing at the same time.\n",
5950 			//Ships[infop->maintained_variables.objp_value->instance].ship_name, Ships[objp->instance].ship_name);
5951 		}
5952 #endif
5953 		// we found someone
5954 		infop->maintained_variables.bool_value = true;
5955 		infop->maintained_variables.objp_value = objp;
5956 
5957 #ifdef NDEBUG
5958 		// return early in release builds
5959 		infop->early_return_condition = true;
5960 #endif
5961 	}
5962 }
5963 
5964 SCP_vector<man_thruster_renderer> Man_thrusters;
5965 
5966 /**
5967  * Batch renders all maneuvering thrusters in the array.
5968  *
5969  * It also clears the array every 10 seconds to keep mem usage down.
5970  */
batch_render_man_thrusters()5971 void batch_render_man_thrusters()
5972 {
5973 	man_thruster_renderer *mtr;
5974 	size_t mant_size = Man_thrusters.size();
5975 
5976 	if (mant_size == 0)
5977 		return;
5978 
5979 	for(size_t i = 0; i < mant_size; i++)
5980 	{
5981 		mtr = &Man_thrusters[i];
5982 		gr_set_bitmap(mtr->bmap_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f);
5983 
5984 		mtr->man_batcher.render(TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT | TMAP_HTL_3D_UNLIT);
5985 		mtr->bmap_id = -1;	//Mark as free
5986 	}
5987 
5988 	//WMC - clear maneuvering thruster render queue every 10 seconds
5989 	if(timestamp() - Man_thruster_reset_timestamp > 10000)
5990 	{
5991 		Man_thrusters.clear();
5992 		Man_thruster_reset_timestamp = timestamp();
5993 	}
5994 }
5995 
5996 /**
5997  * Looks for a free slot in the man_thruster batch
5998  * rendering array. Or, it returns a slot with the same bitmap
5999  * ID as the maneuvering thruster.
6000  *
6001  * You could actually batch render anything that uses a simple bitmap
6002  * on a single poly with this system...just plug the bitmap into bmap_frame
6003  * and use as a normal batcher.
6004  *
6005  * Once calling this function, use man_batcher.allocate_add() to allocate or it will crash later.
6006  * Then call man_batcher.draw*()
6007  */
man_thruster_get_slot(int bmap_frame)6008 man_thruster_renderer *man_thruster_get_slot(int bmap_frame)
6009 {
6010 	man_thruster_renderer *mtr;
6011 	size_t mant_size = Man_thrusters.size();
6012 
6013 	for(size_t mi = 0; mi < mant_size; mi++)
6014 	{
6015 		mtr = &Man_thrusters[mi];
6016 		if(mtr->bmap_id == bmap_frame)
6017 			return mtr;
6018 	}
6019 	for(size_t mj = 0; mj < mant_size; mj++)
6020 	{
6021 		mtr = &Man_thrusters[mj];
6022 		if(mtr->bmap_id == -1)
6023 		{
6024 			mtr->bmap_id = bmap_frame;
6025 			return mtr;
6026 		}
6027 	}
6028 
6029 	Man_thrusters.push_back(man_thruster_renderer(bmap_frame));
6030 	return &Man_thrusters[Man_thrusters.size()-1];
6031 }
6032 
6033 //WMC - used for FTL and maneuvering thrusters
6034 geometry_batcher fx_batcher;
ship_render(object * obj)6035 void ship_render(object * obj)
6036 {
6037 	int num = obj->instance;
6038 	Assert( num >= 0);
6039 	ship *shipp = &Ships[num];
6040 	ship *warp_shipp = NULL;
6041 	ship_info *sip = &Ship_info[Ships[num].ship_info_index];
6042 	bool is_first_stage_arrival = false;
6043 	bool show_thrusters = (shipp->flags2 & SF2_NO_THRUSTERS) == 0;
6044 	dock_function_info dfi;
6045 
6046 
6047 #if 0
6048 	// show target when attacking big ship
6049 	vec3d temp, target;
6050 	ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
6051 	if ( (aip->target_objnum >= 0)  && (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_SUPERCAP|SIF_CAPITAL|SIF_CRUISER)) ) {
6052 		vm_vec_unrotate(&temp, &aip->big_attack_point, &Objects[aip->target_objnum].orient);
6053 		vm_vec_add(&target, &temp, &Objects[aip->target_objnum].pos);
6054 
6055 		vertex v0, v1;
6056 		gr_set_color(128,0,0);
6057 		g3_rotate_vertex( &v0, &obj->pos );
6058 		g3_rotate_vertex( &v1, &target );
6059 
6060 		g3_draw_line(&v0, &v1);
6061 
6062 		g3_draw_sphere(&v1, 5.0f);
6063 	}
6064 #endif
6065 
6066 
6067 	if ( obj == Viewer_obj)
6068 	{
6069 		if (ship_show_velocity_dot && (obj==Player_obj) )
6070 		{
6071 			vec3d p0,v;
6072 			vertex v0;
6073 
6074 			vm_vec_scale_add( &v, &obj->phys_info.vel, &obj->orient.vec.fvec, 3.0f );
6075 			vm_vec_normalize( &v );
6076 
6077 
6078 			vm_vec_scale_add( &p0, &obj->pos, &v, 20.0f);
6079 
6080 			g3_rotate_vertex( &v0, &p0 );
6081 
6082 			gr_set_color(0,128,0);
6083 			g3_draw_sphere( &v0, 0.1f );
6084 		}
6085 
6086 		// Show the shield hit effect for the viewer.
6087 		if ( Show_shield_hits )
6088 		{
6089 			shipp = &Ships[num];
6090 			if (shipp->shield_hits)
6091 			{
6092 				create_shield_explosion_all(obj);
6093 				shipp->shield_hits = 0;
6094 			}
6095 		}
6096 
6097 		if (!(Viewer_mode & VM_TOPDOWN))
6098 		{
6099 			return;
6100 		}
6101 	}
6102 
6103 	MONITOR_INC( NumShipsRend, 1 );
6104 
6105 	// look for a warping ship, whether for me or for anybody I'm docked with
6106 	dock_evaluate_all_docked_objects(obj, &dfi, ship_find_warping_ship_helper);
6107 
6108 	// if any docked objects are set to stage 1 arrival then set bool
6109 	if (dfi.maintained_variables.bool_value) {
6110 		warp_shipp = &Ships[dfi.maintained_variables.objp_value->instance];
6111 
6112 		is_first_stage_arrival = ((warp_shipp->flags & SF_ARRIVING_STAGE_1) > 0);
6113 
6114 		// This is a hack to make ships using the hyperspace warpin type to
6115 		// render even in stage 1, which is used for collision detection
6116 		// purposes -zookeeper
6117 		if (Ship_info[warp_shipp->ship_info_index].warpin_type == WT_HYPERSPACE) {
6118 			warp_shipp = NULL;
6119 			is_first_stage_arrival = false;
6120 		}
6121 	}
6122 
6123 
6124 	// Make ships that are warping in not render during stage 1
6125 	if ( !(is_first_stage_arrival) )
6126 	{
6127 		if ( Ship_shadows && shipfx_in_shadow( obj ) )	{
6128 			light_set_shadow(1);
6129 		} else {
6130 			light_set_shadow(0);
6131 		}
6132 
6133 		ship_model_start(obj);
6134 
6135 		uint render_flags = MR_NORMAL;
6136 	#ifndef NDEBUG
6137 		if(Show_paths || Show_fpaths){
6138 			render_flags |= MR_BAY_PATHS;
6139 		}
6140 	#endif
6141 
6142 		// Only render electrical arcs if within 500m of the eye (for a 10m piece)
6143 		if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f )	{
6144 			int i;
6145 			for (i=0; i<MAX_SHIP_ARCS; i++ )	{
6146 				if ( timestamp_valid( shipp->arc_timestamp[i] ) )	{
6147 					model_add_arc(sip->model_num, -1, &shipp->arc_pts[i][0], &shipp->arc_pts[i][1], shipp->arc_type[i]);
6148 				}
6149 			}
6150 		}
6151 
6152 		if ( shipp->large_ship_blowup_index >= 0 )	{
6153 			shipfx_large_blowup_render(shipp);
6154 		} else {
6155 			//WMC - I suppose this is a bit hackish.
6156 			physics_info *pi = &Objects[shipp->objnum].phys_info;
6157 			float render_amount;
6158 			fx_batcher.allocate(sip->num_maneuvering);	//Act as if all thrusters are going.
6159 
6160 			for(int i = 0; i < sip->num_maneuvering; i++)
6161 			{
6162 				man_thruster *mtp = &sip->maneuvering[i];
6163 
6164 				render_amount = 0.0f;
6165 
6166 				//WMC - get us a steady value
6167 				vec3d des_vel;
6168 				vm_vec_rotate(&des_vel, &pi->desired_vel, &obj->orient);
6169 
6170 				if(pi->desired_rotvel.xyz.x < 0 && (mtp->use_flags & MT_PITCH_UP)) {
6171 					render_amount = fl_abs(pi->desired_rotvel.xyz.x) / pi->max_rotvel.xyz.x;
6172 				} else if(pi->desired_rotvel.xyz.x > 0 && (mtp->use_flags & MT_PITCH_DOWN)) {
6173 					render_amount = fl_abs(pi->desired_rotvel.xyz.x) / pi->max_rotvel.xyz.x;
6174 				} else if(pi->desired_rotvel.xyz.y < 0 && (mtp->use_flags & MT_ROLL_RIGHT)) {
6175 					render_amount = fl_abs(pi->desired_rotvel.xyz.y) / pi->max_rotvel.xyz.y;
6176 				} else if(pi->desired_rotvel.xyz.y > 0 && (mtp->use_flags & MT_ROLL_LEFT)) {
6177 					render_amount = fl_abs(pi->desired_rotvel.xyz.y) / pi->max_rotvel.xyz.y;
6178 				} else if(pi->desired_rotvel.xyz.z < 0 && (mtp->use_flags & MT_BANK_RIGHT)) {
6179 					render_amount = fl_abs(pi->desired_rotvel.xyz.z) / pi->max_rotvel.xyz.z;
6180 				} else if(pi->desired_rotvel.xyz.z > 0 && (mtp->use_flags & MT_BANK_LEFT)) {
6181 					render_amount = fl_abs(pi->desired_rotvel.xyz.z) / pi->max_rotvel.xyz.z;
6182 				}
6183 
6184 				//Backslash - show thrusters according to thrust amount, not speed
6185 				if(pi->side_thrust > 0 && (mtp->use_flags & MT_SLIDE_RIGHT)) {
6186 					render_amount = pi->side_thrust;
6187 				} else if(pi->side_thrust < 0 && (mtp->use_flags & MT_SLIDE_LEFT)) {
6188 					render_amount = -pi->side_thrust;
6189 				} else if(pi->vert_thrust > 0 && (mtp->use_flags & MT_SLIDE_UP)) {
6190 					render_amount = pi->vert_thrust;
6191 				} else if(pi->vert_thrust < 0 && (mtp->use_flags & MT_SLIDE_DOWN)) {
6192 					render_amount = -pi->vert_thrust;
6193 				} else if(pi->forward_thrust > 0 && (mtp->use_flags & MT_FORWARD)) {
6194 					render_amount = pi->forward_thrust;
6195 				} else if(pi->forward_thrust < 0 && (mtp->use_flags & MT_REVERSE)) {
6196 					render_amount = -pi->forward_thrust;
6197 				}
6198 
6199 				//Don't render small faraway thrusters (more than 10k * radius away)
6200 				if (vm_vec_dist(&Eye_position, &obj->pos) > (10000.0f * mtp->radius))
6201 					render_amount = 0.0f;
6202 
6203 				if(render_amount > 0.0f)
6204 				{
6205 					//Handle sounds and stuff
6206 					if(shipp->thrusters_start[i] <= 0)
6207 					{
6208 						shipp->thrusters_start[i] = timestamp();
6209 						if(mtp->start_snd >= 0)
6210 							snd_play_3d( &Snds[mtp->start_snd], &mtp->pos, &Eye_position, 0.0f, &obj->phys_info.vel );
6211 					}
6212 
6213 					//Only assign looping sound if
6214 					//it is specified
6215 					//it isn't assigned already
6216 					//start sound doesn't exist or has finished
6217 					if (!Cmdline_freespace_no_sound)
6218 						if(mtp->loop_snd >= 0
6219 							&& shipp->thrusters_sounds[i] < 0
6220 							&& (mtp->start_snd < 0 || (snd_get_duration(mtp->start_snd) < timestamp() - shipp->thrusters_start[i]))
6221 							)
6222 						{
6223 							shipp->thrusters_sounds[i] = obj_snd_assign(OBJ_INDEX(obj), mtp->loop_snd, &mtp->pos, 1);
6224 						}
6225 
6226 					//Draw graphics
6227 					//Skip invalid ones
6228 					if(mtp->tex_id >= 0)
6229 					{
6230 						float rad = mtp->radius;
6231 						if(rad <= 0.0f)
6232 							rad = 1.0f;
6233 
6234 						float len = mtp->length;
6235 						if(len == 0.0f)
6236 							len = rad;
6237 
6238 						vec3d start, tmpend, end;
6239 						//Start
6240 						vm_vec_unrotate(&start, &mtp->pos, &obj->orient);
6241 						vm_vec_add2(&start, &obj->pos);
6242 
6243 						//End
6244 						vm_vec_scale_add(&tmpend, &mtp->pos, &mtp->norm, len * render_amount);
6245 						vm_vec_unrotate(&end, &tmpend, &obj->orient);
6246 						vm_vec_add2(&end, &obj->pos);
6247 
6248 						//Draw
6249 						fx_batcher.draw_beam(&start, &end, rad, 1.0f);
6250 
6251 						int bmap_frame = mtp->tex_id;
6252 						if(mtp->tex_nframes > 0)
6253 							bmap_frame += (int)(((float)(timestamp() - shipp->thrusters_start[i]) / 1000.0f) * (float)mtp->tex_fps) % mtp->tex_nframes;
6254 
6255 						man_thruster_renderer *mtr = man_thruster_get_slot(bmap_frame);
6256 						mtr->man_batcher.add_allocate(1);
6257 						mtr->man_batcher.draw_beam(&start, &end, rad, 1.0f);
6258 					}
6259 
6260 				}
6261 				//We've stopped firing a thruster
6262 				else if(shipp->thrusters_start[i] > 0)
6263 				{
6264 					shipp->thrusters_start[i] = 0;
6265 					if(shipp->thrusters_sounds[i] >= 0)
6266 					{
6267 						obj_snd_delete(OBJ_INDEX(obj), shipp->thrusters_sounds[i]);
6268 						shipp->thrusters_sounds[i] = -1;
6269 					}
6270 
6271 					if(mtp->stop_snd >= 0)
6272 					{
6273 						//Get world pos
6274 						vec3d start;
6275 						vm_vec_unrotate(&start, &mtp->pos, &obj->orient);
6276 						vm_vec_add2(&start, &obj->pos);
6277 
6278 						snd_play_3d( &Snds[mtp->stop_snd], &mtp->pos, &Eye_position, 0.0f, &obj->phys_info.vel );
6279 					}
6280 				}
6281 			}
6282 
6283 			if ( !(shipp->flags & SF_DISABLED) && !ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) && show_thrusters) {
6284 				mst_info mst;
6285 
6286 				mst.length.xyz.z = obj->phys_info.forward_thrust;
6287 				mst.length.xyz.x = obj->phys_info.side_thrust;
6288 				mst.length.xyz.y = obj->phys_info.vert_thrust;
6289 
6290 				//	Maybe add noise to thruster geometry.
6291 				if (!(sip->flags2 & SIF2_NO_THRUSTER_GEO_NOISE)) {
6292 					mst.length.xyz.z *= (1.0f + frand()/5.0f - 0.1f);
6293 					mst.length.xyz.y *= (1.0f + frand()/5.0f - 0.1f);
6294 					mst.length.xyz.x *= (1.0f + frand()/5.0f - 0.1f);
6295 				}
6296 
6297 				CLAMP(mst.length.xyz.z, -1.0f, 1.0f);
6298 				CLAMP(mst.length.xyz.y, -1.0f, 1.0f);
6299 				CLAMP(mst.length.xyz.x, -1.0f, 1.0f);
6300 
6301 				mst.primary_bitmap = shipp->thruster_bitmap;
6302 				mst.primary_glow_bitmap = shipp->thruster_glow_bitmap;
6303 				mst.secondary_glow_bitmap = shipp->thruster_secondary_glow_bitmap;
6304 				mst.tertiary_glow_bitmap = shipp->thruster_tertiary_glow_bitmap;
6305 				mst.distortion_bitmap = shipp->thruster_distortion_bitmap;
6306 
6307 				mst.use_ab = (obj->phys_info.flags & PF_AFTERBURNER_ON) || (obj->phys_info.flags & PF_BOOSTER_ON);
6308 				mst.glow_noise = shipp->thruster_glow_noise;
6309 				mst.rotvel = &Objects[shipp->objnum].phys_info.rotvel;
6310 
6311 				mst.glow_rad_factor = sip->thruster01_glow_rad_factor;
6312 				mst.secondary_glow_rad_factor = sip->thruster02_glow_rad_factor;
6313 				mst.tertiary_glow_rad_factor = sip->thruster03_glow_rad_factor;
6314 				mst.glow_length_factor = sip->thruster02_glow_len_factor;
6315 				mst.distortion_length_factor = sip->thruster_dist_len_factor;
6316 				mst.distortion_rad_factor = sip->thruster_dist_rad_factor;
6317 
6318 				mst.draw_distortion = sip->draw_distortion;
6319 
6320 				model_set_thrust(sip->model_num, &mst);
6321 
6322 				render_flags |= MR_SHOW_THRUSTERS;
6323 			}
6324 
6325 			// fill the model flash lighting values in
6326 			shipfx_flash_light_model( obj, sip->model_num );
6327 
6328 
6329 			// If the ship is going "through" the warp effect, then
6330 			// set up the model renderer to only draw the polygons in front
6331 			// of the warp in effect
6332 			int clip_started = 0;
6333 
6334 			// Warp_shipp points to the ship that is going through a
6335 			// warp... either this ship or the ship it is docked with.
6336 			if ( warp_shipp != NULL )
6337 			{
6338 				if(warp_shipp->flags & SF_ARRIVING)
6339 					clip_started = warp_shipp->warpin_effect->warpShipClip();
6340 				else if(warp_shipp->flags & SF_DEPART_WARP)
6341 					clip_started = warp_shipp->warpout_effect->warpShipClip();
6342 			}
6343 
6344 			// maybe set squad logo bitmap
6345 			model_set_insignia_bitmap(-1);
6346 
6347 			if(Game_mode & GM_MULTIPLAYER){
6348 				// if its any player's object
6349 				int np_index = multi_find_player_by_object( obj );
6350 				if((np_index >= 0) && (np_index < MAX_PLAYERS) && MULTI_CONNECTED(Net_players[np_index]) && (Net_players[np_index].m_player != NULL)){
6351 					model_set_insignia_bitmap(Net_players[np_index].m_player->insignia_texture);
6352 				}
6353 			}
6354 			// in single player, we want to render model insignias on all ships in alpha beta and gamma
6355 			// Goober5000 - and also on wings that have their logos set
6356 			else {
6357 				// if its an object in my squadron
6358 				if(ship_in_my_squadron(shipp)) {
6359 					model_set_insignia_bitmap(Player->insignia_texture);
6360 				}
6361 
6362 				// maybe it has a wing squad logo - Goober5000
6363 				if (shipp->wingnum >= 0)
6364 				{
6365 					// don't override the player's wing
6366 					if (shipp->wingnum != Player_ship->wingnum)
6367 					{
6368 						// if we have a logo texture
6369 						if (Wings[shipp->wingnum].wing_insignia_texture >= 0)
6370 						{
6371 							model_set_insignia_bitmap(Wings[shipp->wingnum].wing_insignia_texture);
6372 						}
6373 					}
6374 				}
6375 			}
6376 
6377 			// nebula
6378 			if(The_mission.flags & MISSION_FLAG_FULLNEB){
6379 				extern void model_set_fog_level(float l);
6380 				model_set_fog_level(neb2_get_fog_intensity(obj));
6381 			}
6382 
6383 			// Valathil - maybe do a scripting hook here to do some scriptable effects?
6384 			if(shipp->shader_effect_active && Use_GLSL > 1)
6385 			{
6386 				float timer;
6387 				render_flags |= (MR_ANIMATED_SHADER);
6388 
6389 				ship_effect* sep = &Ship_effects[shipp->shader_effect_num];
6390 				opengl_shader_set_animated_effect(sep->shader_effect);
6391 				if (sep->invert_timer) {
6392 					timer = 1.0f - ((timer_get_milliseconds() - shipp->shader_effect_start_time) / (float)shipp->shader_effect_duration);
6393 					timer = MAX(timer,0.0f);
6394 				} else {
6395 					timer = ((timer_get_milliseconds() - shipp->shader_effect_start_time) / (float)shipp->shader_effect_duration);
6396 				}
6397 
6398 				opengl_shader_set_animated_timer(timer);
6399 
6400 				if (sep->disables_rendering && (timer_get_milliseconds() > shipp->shader_effect_start_time + shipp->shader_effect_duration) ) {
6401 					shipp->flags2 |= SF2_CLOAKED;
6402 					shipp->shader_effect_active = false;
6403 				} else {
6404 					shipp->flags2 &= ~SF2_CLOAKED;
6405 					if (timer_get_milliseconds() > shipp->shader_effect_start_time + shipp->shader_effect_duration)
6406 						shipp->shader_effect_active = false;
6407 				}
6408 			}
6409 
6410 			if (sip->uses_team_colors) {
6411 				gr_set_team_color(shipp->team_name, shipp->secondary_team_name, shipp->team_change_timestamp, shipp->team_change_time);
6412 			}
6413 
6414 			if(sip->flags2 & SIF2_NO_LIGHTING)
6415 				render_flags |= MR_NO_LIGHTING;
6416 
6417 			//draw weapon models
6418 			if ((sip->flags2 & SIF2_DRAW_WEAPON_MODELS) && !(shipp->flags2 & SF2_CLOAKED)) {
6419 				int i,k;
6420 				ship_weapon *swp = &shipp->weapons;
6421 				g3_start_instance_matrix(&obj->pos, &obj->orient, true);
6422 
6423 				int save_flags = render_flags;
6424 
6425 				render_flags &= ~MR_SHOW_THRUSTERS;
6426 
6427 				//primary weapons
6428 				for (i = 0; i < swp->num_primary_banks; i++) {
6429 					if (Weapon_info[swp->primary_bank_weapons[i]].external_model_num == -1 || !sip->draw_primary_models[i])
6430 						continue;
6431 
6432 					w_bank *bank = &model_get(sip->model_num)->gun_banks[i];
6433 					for(k = 0; k < bank->num_slots; k++) {
6434 						polymodel* pm = model_get(Weapon_info[swp->primary_bank_weapons[i]].external_model_num);
6435 						pm->gun_submodel_rotation = shipp->primary_rotate_ang[i];
6436 						model_render(Weapon_info[swp->primary_bank_weapons[i]].external_model_num, &vmd_identity_matrix, &bank->pnt[k], render_flags);
6437 						pm->gun_submodel_rotation = 0.0f;
6438 					}
6439 				}
6440 
6441 				//secondary weapons
6442 		        int num_secondaries_rendered = 0;
6443                 vec3d secondary_weapon_pos;
6444                 w_bank* bank;
6445 
6446 				for (i = 0; i < swp->num_secondary_banks; i++) {
6447 					if (Weapon_info[swp->secondary_bank_weapons[i]].external_model_num == -1 || !sip->draw_secondary_models[i])
6448 						continue;
6449 
6450 					bank = &(model_get(sip->model_num))->missile_banks[i];
6451 
6452 					if (Weapon_info[swp->secondary_bank_weapons[i]].wi_flags2 & WIF2_EXTERNAL_WEAPON_LNCH) {
6453 						for(k = 0; k < bank->num_slots; k++) {
6454 							model_render(Weapon_info[swp->secondary_bank_weapons[i]].external_model_num, &vmd_identity_matrix, &bank->pnt[k], render_flags);
6455 						}
6456 					} else {
6457 						num_secondaries_rendered = 0;
6458 
6459 						for(k = 0; k < bank->num_slots; k++)
6460 						{
6461 							secondary_weapon_pos = bank->pnt[k];
6462 
6463 							if (num_secondaries_rendered >= shipp->weapons.secondary_bank_ammo[i])
6464 								break;
6465 
6466 							if(shipp->secondary_point_reload_pct[i][k] <= 0.0)
6467 								continue;
6468 
6469 							num_secondaries_rendered++;
6470 
6471 							vm_vec_scale_add2(&secondary_weapon_pos, &vmd_z_vector, -(1.0f-shipp->secondary_point_reload_pct[i][k]) * model_get(Weapon_info[swp->secondary_bank_weapons[i]].external_model_num)->rad);
6472 
6473 							model_render(Weapon_info[swp->secondary_bank_weapons[i]].external_model_num, &vmd_identity_matrix, &secondary_weapon_pos, render_flags);
6474 						}
6475 					}
6476 				}
6477 				g3_done_instance(true);
6478 				render_flags = save_flags;
6479 			}
6480 
6481 			// small ships
6482 			if (!(shipp->flags2 & SF2_CLOAKED)) {
6483 				if ((The_mission.flags & MISSION_FLAG_FULLNEB) && (sip->flags & SIF_SMALL_SHIP)) {
6484 					// force detail levels
6485  					float fog_val = neb2_get_fog_intensity(obj);
6486 					if(fog_val >= 0.6f){
6487 						model_set_detail_level(2);
6488 						model_render( sip->model_num, &obj->orient, &obj->pos, render_flags | MR_LOCK_DETAIL, OBJ_INDEX(obj), -1, shipp->ship_replacement_textures );
6489 					} else {
6490 						model_render( sip->model_num, &obj->orient, &obj->pos, render_flags, OBJ_INDEX(obj), -1, shipp->ship_replacement_textures );
6491 					}
6492 				} else {
6493 					model_render( sip->model_num, &obj->orient, &obj->pos, render_flags, OBJ_INDEX(obj), -1, shipp->ship_replacement_textures );
6494 				}
6495 			}
6496 
6497 			// always turn off fog after rendering a ship
6498 			gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
6499 			light_set_shadow(0);
6500 
6501 			#ifndef NDEBUG
6502 			if (Show_shield_mesh)
6503 				ship_draw_shield( obj);		//	Render the shield.
6504 			#endif
6505 
6506 			if ( clip_started )	{
6507 				g3_stop_user_clip_plane();
6508 			}
6509 		}
6510 
6511 		ship_model_stop(obj);
6512 
6513 		if (shipp->shield_hits) {
6514 			create_shield_explosion_all(obj);
6515 			shipp->shield_hits = 0;
6516 		}
6517 
6518 	#ifndef NDEBUG
6519 		if (Ai_render_debug_flag || Show_paths) {
6520 			if ( shipp->ai_index != -1 ){
6521 				render_path_points(obj);
6522 			}
6523 
6524 			render_dock_bays(obj);
6525 		}
6526 	#endif
6527 
6528 	#ifndef NDEBUG
6529 		if(Show_tnorms){
6530 			ship_subsys *systemp;
6531 			vec3d tpos, tnorm, temp;
6532 			vec3d v1, v2;
6533 			vertex l1, l2;
6534 
6535 			gr_set_color(0, 0, 255);
6536 			systemp = GET_FIRST( &shipp->subsys_list );
6537 			while ( systemp != END_OF_LIST(&shipp->subsys_list) ) {
6538 				ship_get_global_turret_gun_info(obj, systemp, &tpos, &tnorm, 1, &temp);
6539 
6540 				v1 = tpos;
6541 				vm_vec_scale_add(&v2, &v1, &tnorm, 20.0f);
6542 
6543 				g3_rotate_vertex(&l1, &v1);
6544 				g3_rotate_vertex(&l2, &v2);
6545 
6546 				g3_draw_sphere(&l1, 2.0f);
6547 				g3_draw_line(&l1, &l2);
6548 
6549 				systemp = GET_NEXT(systemp);
6550 			}
6551 		}
6552 	#endif
6553 	}
6554 
6555 	//WMC - Draw animated warp effect (ie BSG thingy)
6556 	//WMC - based on Bobb's secondary thruster stuff
6557 	//which was in turn based on the beam code.
6558 	//I'm gonna need some serious acid to neutralize this base.
6559 	if(shipp->flags & SF_ARRIVING)
6560 		shipp->warpin_effect->warpShipRender();
6561 	else if(shipp->flags & SF_DEPART_WARP)
6562 		shipp->warpout_effect->warpShipRender();
6563 
6564 	gr_disable_team_color();
6565 }
6566 
ship_render_cockpit(object * objp)6567 void ship_render_cockpit(object *objp)
6568 {
6569 	if(objp->type != OBJ_SHIP || objp->instance < 0)
6570 		return;
6571 
6572 	ship *shipp = &Ships[objp->instance];
6573 	ship_info *sip = &Ship_info[shipp->ship_info_index];
6574 
6575 	if(sip->cockpit_model_num < 0)
6576 		return;
6577 
6578 	polymodel *pm = model_get(sip->cockpit_model_num);
6579 	Assert(pm != NULL);
6580 
6581 	//Setup
6582 	gr_reset_clip();
6583 	hud_save_restore_camera_data(1);
6584 
6585 	matrix eye_ori = vmd_identity_matrix;
6586 	vec3d eye_pos = vmd_zero_vector;
6587 	ship_get_eye(&eye_pos, &eye_ori, objp, false, true);
6588 
6589 	vec3d pos = vmd_zero_vector;
6590 
6591 	vm_vec_unrotate(&pos, &sip->cockpit_offset, &eye_ori);
6592 	if (!Cmdline_nohtl)
6593 	{
6594 		gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, 0.02f, 10.0f*pm->rad);
6595 		gr_set_view_matrix(&vmd_zero_vector, &Eye_matrix);
6596 	}
6597 
6598 	//Zbuffer
6599 	int saved_zbuffer_mode = gr_zbuffer_get();
6600 	gr_zbuffer_set(GR_ZBUFF_NONE);
6601 
6602 	//Deal with the model
6603 	model_set_detail_level(0);
6604 	model_clear_instance(sip->cockpit_model_num);
6605 	model_render(sip->cockpit_model_num, &eye_ori, &pos, MR_LOCK_DETAIL | MR_NO_FOGGING, -1, -1, Player_cockpit_textures);
6606 
6607 	//Zbuffer
6608 	gr_zbuffer_set(saved_zbuffer_mode);
6609 
6610 	if (!Cmdline_nohtl)
6611 	{
6612 		gr_end_view_matrix();
6613 		gr_end_proj_matrix();
6614 	}
6615 
6616 	hud_save_restore_camera_data(0);
6617 }
6618 
ship_render_show_ship_cockpit(object * objp)6619 void ship_render_show_ship_cockpit(object *objp)
6620 {
6621 	vec3d cockpit_eye_pos;
6622 	matrix dummy;
6623 	ship_get_eye(&cockpit_eye_pos, &dummy, objp, true, true); //Get cockpit eye position
6624 
6625 	gr_end_view_matrix();
6626 	gr_end_proj_matrix();
6627 	gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, 0.05f, Max_draw_distance);
6628 	gr_set_view_matrix(&cockpit_eye_pos, &Eye_matrix); // Set Camera to cockpit eye position
6629 
6630 	Glowpoint_override = true; // Turn off glowpoints so they dont get rendered fixed at origin
6631 	model_set_detail_level(0);
6632 	model_render(Ship_info[Ships[objp->instance].ship_info_index].model_num, &objp->orient, &vmd_zero_vector, MR_NORMAL | MR_LOCK_DETAIL, OBJ_INDEX(objp)); // Render ship model with fixed detail level 0 so its not switching LOD when moving away from origin
6633 	Glowpoint_override = false;
6634 
6635 	gr_end_view_matrix();
6636 	gr_end_proj_matrix();
6637 	gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
6638 	gr_set_view_matrix(&Eye_position, &Eye_matrix); // Reset Camera to normal
6639 }
6640 
ship_init_cockpit_displays(ship * shipp)6641 void ship_init_cockpit_displays(ship *shipp)
6642 {
6643 	ship_info *sip = &Ship_info[shipp->ship_info_index];
6644 
6645 	int cockpit_model_num = sip->cockpit_model_num;
6646 
6647 	// don't bother creating cockpit texture replacements if this ship has no cockpit
6648 	if ( cockpit_model_num < 0 ) {
6649 		return;
6650 	}
6651 
6652 	// check if we even have cockpit displays
6653 	if ( sip->displays.size() <= 0 ) {
6654 		return;
6655 	}
6656 
6657 	if ( Player_cockpit_textures != NULL) {
6658 		return;
6659 	}
6660 
6661 	// ship's cockpit texture replacements haven't been setup yet, so do it.
6662 	Player_cockpit_textures = (int *) vm_malloc(MAX_REPLACEMENT_TEXTURES * sizeof(int));
6663 
6664 	int i;
6665 
6666 	for ( i = 0; i < MAX_REPLACEMENT_TEXTURES; i++ ) {
6667 		Player_cockpit_textures[i] = -1;
6668 	}
6669 
6670 	for ( i = 0; i < (int)sip->displays.size(); i++ ) {
6671 		ship_add_cockpit_display(&sip->displays[i], cockpit_model_num);
6672 	}
6673 
6674 	ship_set_hud_cockpit_targets();
6675 }
6676 
ship_clear_cockpit_displays()6677 void ship_clear_cockpit_displays()
6678 {
6679 	for ( int i = 0; i < (int)Player_displays.size(); i++ ) {
6680 		if ( Player_displays[i].background >= 0 ) {
6681 			bm_release(Player_displays[i].background);
6682 		}
6683 
6684 		if ( Player_displays[i].foreground >= 0 ) {
6685 			bm_release(Player_displays[i].foreground);
6686 		}
6687 
6688 		if ( Player_displays[i].target >= 0 ) {
6689 			bm_release(Player_displays[i].target);
6690 		}
6691 	}
6692 
6693 	Player_displays.clear();
6694 
6695 	if ( Player_cockpit_textures != NULL ) {
6696 		vm_free(Player_cockpit_textures);
6697 		Player_cockpit_textures = NULL;
6698 	}
6699 }
6700 
ship_add_cockpit_display(cockpit_display_info * display,int cockpit_model_num)6701 void ship_add_cockpit_display(cockpit_display_info *display, int cockpit_model_num)
6702 {
6703 	if ( strlen(display->filename) <= 0 ) {
6704 		return;
6705 	}
6706 
6707 	if( cockpit_model_num < 0 ) {
6708 		return;
6709 	}
6710 
6711 	int i, tm_num, diffuse_target = -1, glow_target = -1, glow_handle = -1, diffuse_handle = -1;
6712 	int w, h;
6713 	cockpit_display new_display;
6714 
6715 	// if no texture target has been found yet, find one.
6716 	polymodel *pm = model_get(cockpit_model_num);
6717 
6718 	for ( i = 0; i < pm->n_textures; i++ )
6719 	{
6720 		tm_num = pm->maps[i].FindTexture(display->filename);
6721 		if ( tm_num >= 0 ) {
6722 			diffuse_target = i*TM_NUM_TYPES;
6723 			glow_target = i*TM_NUM_TYPES+TM_GLOW_TYPE;
6724 
6725 			diffuse_handle = pm->maps[i].textures[TM_BASE_TYPE].GetTexture();
6726 			glow_handle = pm->maps[i].textures[TM_GLOW_TYPE].GetTexture();
6727 			break;
6728 		}
6729 	}
6730 
6731 	// create a render target for this cockpit texture
6732 	if ( Player_cockpit_textures[glow_target] < 0) {
6733 
6734 		bm_get_info(diffuse_handle, &w, &h);
6735 		Player_cockpit_textures[glow_target] = bm_make_render_target(w, h, BMP_FLAG_RENDER_TARGET_DYNAMIC);
6736 
6737 		// if no render target was made, bail
6738 		if ( Player_cockpit_textures[glow_target] < 0 ) {
6739 			return;
6740 		}
6741 	}
6742 
6743 	new_display.background = -1;
6744 	if ( display->bg_filename[0] != '\0' ) {
6745 		new_display.background = bm_load(display->bg_filename);
6746 
6747 		if ( new_display.background < 0 ) {
6748 			Warning(LOCATION, "Unable to load background %s for cockpit display %s", display->bg_filename, display->name);
6749 		}
6750 	}
6751 
6752 	new_display.foreground = -1;
6753 	if ( display->fg_filename[0] != '\0' ) {
6754 		new_display.foreground = bm_load(display->fg_filename);
6755 
6756 		if ( new_display.foreground < 0 ) {
6757 			Warning(LOCATION, "Unable to load background %s for cockpit display %s", display->fg_filename, display->name);
6758 		}
6759 	}
6760 
6761 	strcpy_s(new_display.name, display->name);
6762 	new_display.offset[0] = display->offset[0];
6763 	new_display.offset[1] = display->offset[1];
6764 	new_display.size[0] = display->size[0];
6765 	new_display.size[1] = display->size[1];
6766 	new_display.source = glow_handle;
6767 	new_display.target = Player_cockpit_textures[glow_target];
6768 
6769 	Player_displays.push_back(new_display);
6770 }
6771 
ship_set_hud_cockpit_targets()6772 void ship_set_hud_cockpit_targets()
6773 {
6774 	if ( !Ship_info[Player_ship->ship_info_index].hud_enabled ) {
6775 		return;
6776 	}
6777 
6778 	SCP_vector<HudGauge*> &hud = Ship_info[Player_ship->ship_info_index].hud_gauges;
6779 
6780 	for ( int i = 0; i < (int)hud.size(); i++ ) {
6781 		for ( int j = 0; j < (int)Player_displays.size(); j++ ) {
6782 			hud[i]->setCockpitTarget(&Player_displays[j]);
6783 		}
6784 	}
6785 }
6786 
ship_start_render_cockpit_display(int cockpit_display_num)6787 int ship_start_render_cockpit_display(int cockpit_display_num)
6788 {
6789 	// make sure this thing even has a cockpit
6790 	if ( Ship_info[Player_ship->ship_info_index].cockpit_model_num < 0 ) {
6791 		return -1;
6792 	}
6793 
6794 	if ( Player_cockpit_textures == NULL ) {
6795 		return -1;
6796 	}
6797 
6798 	// check sanity of the cockpit display handle
6799 	if ( cockpit_display_num >= (int)Player_displays.size() || cockpit_display_num < 0 ) {
6800 		return -1;
6801 	}
6802 
6803 	cockpit_display* display = &Player_displays[cockpit_display_num];
6804 
6805 	if ( display->target < 0 ) {
6806 		return -1;
6807 	}
6808 
6809 	if ( !bm_set_render_target(display->target) ) {
6810 		return -1;
6811 	}
6812 
6813 	int cull = gr_set_cull(0);
6814 
6815 	gr_clear();
6816 
6817 	if ( display->source >= 0 ) {
6818 		gr_set_bitmap(display->source);
6819 		gr_bitmap(0, 0, GR_RESIZE_NONE);
6820 	}
6821 
6822 	if ( display->background >= 0 ) {
6823 		gr_set_bitmap(display->background);
6824 		gr_bitmap_ex(display->offset[0], display->offset[1], display->size[0], display->size[1], 0, 0, GR_RESIZE_NONE);
6825 	}
6826 
6827 	gr_set_cull(cull);
6828 
6829 	return display->target;
6830 }
6831 
ship_end_render_cockpit_display(int cockpit_display_num)6832 void ship_end_render_cockpit_display(int cockpit_display_num)
6833 {
6834 	// make sure this thing even has a cockpit
6835 	if ( Ship_info[Player_ship->ship_info_index].cockpit_model_num < 0 ) {
6836 		return;
6837 	}
6838 
6839 	if ( Player_cockpit_textures == NULL ) {
6840 		return;
6841 	}
6842 
6843 	// check sanity of the cockpit display handle
6844 	if ( cockpit_display_num >= (int)Player_displays.size() || cockpit_display_num < 0 ) {
6845 		return;
6846 	}
6847 
6848 	cockpit_display* display = &Player_displays[cockpit_display_num];
6849 
6850 	int cull = gr_set_cull(0);
6851 	if ( display->foreground >= 0 ) {
6852 		gr_reset_clip();
6853 		gr_set_bitmap(display->foreground);
6854 		gr_bitmap_ex(display->offset[0], display->offset[1], display->size[0], display->size[1], 0, 0, GR_RESIZE_NONE);
6855 	}
6856 
6857 	gr_set_cull(cull);
6858 	bm_set_render_target(-1);
6859 }
6860 
ship_subsystems_delete(ship * shipp)6861 void ship_subsystems_delete(ship *shipp)
6862 {
6863 	if ( NOT_EMPTY(&shipp->subsys_list) )
6864 	{
6865 		ship_subsys *systemp, *temp;
6866 
6867 		systemp = GET_FIRST( &shipp->subsys_list );
6868 		while ( systemp != END_OF_LIST(&shipp->subsys_list) ) {
6869 			temp = GET_NEXT( systemp );								// use temporary since pointers will get screwed with next operation
6870 			list_remove( shipp->subsys_list, systemp );			// remove the element
6871 			list_append( &ship_subsys_free_list, systemp );		// and place back onto free list
6872 			Num_ship_subsystems--;								// subtract from our in-use total
6873 			systemp = temp;												// use the temp variable to move right along
6874 		}
6875 	}
6876 }
6877 
ship_delete(object * obj)6878 void ship_delete( object * obj )
6879 {
6880 	ship	*shipp;
6881 	int	num, objnum;
6882 
6883 	num = obj->instance;
6884 	Assert( num >= 0);
6885 
6886 	objnum = OBJ_INDEX(obj);
6887 	Assert( Ships[num].objnum == objnum );
6888 
6889 	shipp = &Ships[num];
6890 
6891 	if (shipp->ai_index != -1){
6892 		ai_free_slot(shipp->ai_index);
6893 	}
6894 
6895 	// free up the list of subsystems of this ship.  walk through list and move remaining subsystems
6896 	// on ship back to the free list for other ships to use.
6897 	ship_subsystems_delete(&Ships[num]);
6898 	shipp->objnum = -1;
6899 
6900 	if (shipp->shield_integrity != NULL) {
6901 		vm_free(shipp->shield_integrity);
6902 		shipp->shield_integrity = NULL;
6903 	}
6904 
6905 	if (shipp->ship_replacement_textures != NULL) {
6906 		vm_free(shipp->ship_replacement_textures);
6907 		shipp->ship_replacement_textures = NULL;
6908 	}
6909 
6910 	// glow point banks
6911 	shipp->glow_point_bank_active.clear();
6912 
6913 	if ( shipp->ship_list_index != -1 ) {
6914 		ship_obj_list_remove(shipp->ship_list_index);
6915 		shipp->ship_list_index = -1;
6916 	}
6917 
6918 	free_sexp2(shipp->arrival_cue);
6919 	free_sexp2(shipp->departure_cue);
6920 
6921 	// call the contrail system
6922 	ct_ship_delete(shipp);
6923 
6924 	model_delete_instance(shipp->model_instance_num);
6925 }
6926 
6927 /**
6928  * Used by ::ship_cleanup which is called if the ship is in a wing.
6929  *
6930  * This function updates the ship_index list (i.e. removes its entry in the list)
6931  * and packs the array accordingly.
6932  */
ship_wing_cleanup(int shipnum,wing * wingp)6933 void ship_wing_cleanup( int shipnum, wing *wingp )
6934 {
6935 	int i, index = -1, team = Ships[shipnum].team;
6936 
6937 	// find this ship's position within its wing
6938 	for (i = 0; i < wingp->current_count; i++)
6939 	{
6940 		if (wingp->ship_index[i] == shipnum)
6941 		{
6942 			index = i;
6943 			break;
6944 		}
6945 	}
6946 
6947 	// this can happen in multiplayer (dogfight, ingame join specifically)
6948 	if (index == -1)
6949 		return;
6950 
6951 	// compress the ship_index array and mark the last entry with a -1
6952 	for (i = index; i < wingp->current_count - 1; i++)
6953 		wingp->ship_index[i] = wingp->ship_index[i+1];
6954 
6955 	wingp->current_count--;
6956 	Assert ( wingp->current_count >= 0 );
6957 	wingp->ship_index[wingp->current_count] = -1;
6958 
6959 	// if the current count is 0, check to see if the wing departed or was destroyed.
6960 	if (wingp->current_count == 0)
6961 	{
6962 		// if this wing was ordered to depart by the player, set the current_wave equal to the total
6963 		// waves so we can mark the wing as gone and no other ships arrive
6964 		// Goober5000 - also if it's departing... this is sort of, but not exactly, what :V: did;
6965 		// but it seems to be consistent with how it should behave
6966 		if (wingp->flags & (WF_WING_DEPARTING | WF_DEPARTURE_ORDERED))
6967 			wingp->current_wave = wingp->num_waves;
6968 
6969 		// Goober5000 - some changes for clarity and closing holes
6970 		// make sure to flag the wing as gone if all of its member ships are gone and no more can arrive
6971 		if ((wingp->current_wave == wingp->num_waves) && (wingp->total_destroyed + wingp->total_departed + wingp->total_vanished == wingp->total_arrived_count))
6972 		{
6973 			// mark the wing as gone
6974 			wingp->flags |= WF_WING_GONE;
6975 			wingp->time_gone = Missiontime;
6976 
6977 			// if all ships were destroyed, log it as destroyed
6978 			if (wingp->total_destroyed == wingp->total_arrived_count)
6979 			{
6980 				// first, be sure to mark a wing destroyed event if all members of wing were destroyed and on
6981 				// the last wave.  This circumvents a problem where the wing could be marked as departed and
6982 				// destroyed if the last ships were destroyed after the wing's departure cue became true.
6983 				mission_log_add_entry(LOG_WING_DESTROYED, wingp->name, NULL, team);
6984 			}
6985 			// if some ships escaped, log it as departed
6986 			else if (wingp->total_vanished != wingp->total_arrived_count)
6987 			{
6988 				// if the wing wasn't destroyed, and it is departing, then mark it as departed -- in this
6989 				// case, there had better be ships in this wing with departure entries in the log file.  The
6990 				// logfile code checks for this case.
6991 				mission_log_add_entry(LOG_WING_DEPARTED, wingp->name, NULL, team);
6992 			}
6993 
6994 #ifndef NDEBUG
6995 			//WMC - Ships can depart too, besides being destroyed :P
6996             if ((wingp->total_destroyed + wingp->total_departed + wingp->total_vanished) != wingp->total_arrived_count)
6997 			{
6998 				// apparently, there have been reports of ships still present in the mission when this log
6999 				// entry if written.  Do a sanity check here to find out for sure.
7000 				for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
7001 				{
7002 					// skip the player -- stupid special case.
7003 					if (&Objects[so->objnum] == Player_obj)
7004 						continue;
7005 
7006 					if ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN))
7007 						continue;
7008 
7009 					if ((Ships[Objects[so->objnum].instance].wingnum == WING_INDEX(wingp)) && !(Ships[Objects[so->objnum].instance].flags & (SF_DEPARTING|SF_DYING)))
7010 					{
7011 						// TODO: I think this Int3() is triggered when a wing whose ships are all docked to ships of another
7012 						// wing departs.  It can be reliably seen in TVWP chapter 1 mission 7, when Torino and Iota wing depart.
7013 						// Not sure how to fix this. -- Goober5000
7014 						Int3();
7015 					}
7016 				}
7017 			}
7018 #endif
7019 		}
7020 	}
7021 }
7022 
7023 // functions to do management, like log entries and wing cleanup after a ship has been destroyed
7024 
7025 // Goober5000
ship_actually_depart_helper(object * objp,dock_function_info * infop)7026 void ship_actually_depart_helper(object *objp, dock_function_info *infop)
7027 {
7028 	// do standard departure stuff first
7029 	objp->flags |= OF_SHOULD_BE_DEAD;
7030 	if (objp->type == OBJ_SHIP)
7031 		ship_cleanup(objp->instance, infop->parameter_variables.bool_value ? SHIP_VANISHED : SHIP_DEPARTED);
7032 
7033 	// do the end-mission stuff if it's the player ship
7034 	if (objp == Player_obj)
7035 		gameseq_post_event(GS_EVENT_PLAYER_WARPOUT_DONE);
7036 }
7037 
7038 /**
7039  * Used to actually remove a ship, plus all the ships it's docked to, from the mission
7040  */
ship_actually_depart(int shipnum,int method)7041 void ship_actually_depart(int shipnum, int method)
7042 {
7043 	dock_function_info dfi;
7044 	dfi.parameter_variables.bool_value = (method == SHIP_VANISHED ? true:false);
7045 	dock_evaluate_all_docked_objects(&Objects[Ships[shipnum].objnum], &dfi, ship_actually_depart_helper);
7046 
7047 	// in a couple of cases we'll need to send a packet to update clients
7048 	if (MULTIPLAYER_MASTER && ((method == SHIP_DEPARTED_BAY) || (method == SHIP_VANISHED)) ) {
7049 		send_ship_depart_packet(&Objects[Ships[shipnum].objnum], method);
7050 	}
7051 }
7052 
7053 // no destruction effects, not for player destruction and multiplayer, only self-destruction
ship_destroy_instantly(object * ship_obj,int shipnum)7054 void ship_destroy_instantly(object *ship_obj, int shipnum)
7055 {
7056 	Assert(ship_obj->type == OBJ_SHIP);
7057 	Assert(!(ship_obj == Player_obj));
7058 	Assert(!(Game_mode & GM_MULTIPLAYER));
7059 
7060 	// undocking and death preparation
7061 	ship_stop_fire_primary(ship_obj);
7062 	ai_deathroll_start(ship_obj);
7063 
7064 	mission_log_add_entry(LOG_SELF_DESTRUCTED, Ships[ship_obj->instance].ship_name, NULL );
7065 
7066 	// scripting stuff
7067 	Script_system.SetHookObject("Self", ship_obj);
7068 	Script_system.RunCondition(CHA_DEATH, 0, NULL, ship_obj);
7069 	Script_system.RemHookVars(2, "Self", "Killer");
7070 
7071 	ship_obj->flags |= OF_SHOULD_BE_DEAD;
7072 	ship_cleanup(shipnum,SHIP_DESTROYED);
7073 }
7074 
7075 
7076 /**
7077  * Merge ship_destroyed and ship_departed and ship_vanished
7078  */
ship_cleanup(int shipnum,int cleanup_mode)7079 void ship_cleanup(int shipnum, int cleanup_mode)
7080 {
7081 	Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
7082 	Assert(cleanup_mode == SHIP_DESTROYED || cleanup_mode == SHIP_DEPARTED || cleanup_mode == SHIP_VANISHED);
7083 	Assert(Objects[Ships[shipnum].objnum].type == OBJ_SHIP);
7084 	Assert(Objects[Ships[shipnum].objnum].flags & OF_SHOULD_BE_DEAD);
7085 
7086 	ship *shipp = &Ships[shipnum];
7087 	object *objp = &Objects[shipp->objnum];
7088 
7089 	// add the information to the exited ship list
7090 	if (cleanup_mode == SHIP_DESTROYED) {
7091 		ship_add_exited_ship(shipp, SEF_DESTROYED);
7092 	} else {
7093 		ship_add_exited_ship(shipp, SEF_DEPARTED);
7094 	}
7095 
7096 	// record kill?
7097 	if (cleanup_mode == SHIP_DESTROYED) {
7098 		// determine if we need to count this ship as a kill in counting number of kills per ship type
7099 		// look at the ignore flag for the ship (if not in a wing), or the ignore flag for the wing
7100 		// (if the ship is in a wing), and add to the kill count if the flags are not set
7101 		if ( !(shipp->flags & SF_IGNORE_COUNT) || ((shipp->wingnum != -1) && !(Wings[shipp->wingnum].flags & WF_IGNORE_COUNT)) )
7102 			ship_add_ship_type_kill_count( shipp->ship_info_index );
7103 
7104 		// let the event music system know an enemy was destroyed (important for deciding when to transition from battle to normal music)
7105 		if (Player_ship != NULL && iff_x_attacks_y(Player_ship->team, shipp->team))
7106 			event_music_hostile_ship_destroyed();
7107 	}
7108 
7109 	// add mission log entry?
7110 	// (vanished ships have no log, and destroyed ships are logged in ship_hit_kill)
7111 	if (cleanup_mode == SHIP_DEPARTED) {
7112 		// see if this ship departed within the radius of a jump node -- if so, put the node name into
7113 		// the secondary mission log field
7114 		CJumpNode *jnp = jumpnode_get_which_in(objp);
7115 		if(jnp==NULL)
7116 			mission_log_add_entry(LOG_SHIP_DEPARTED, shipp->ship_name, NULL, shipp->wingnum);
7117 		else
7118 			mission_log_add_entry(LOG_SHIP_DEPARTED, shipp->ship_name, jnp->GetName(), shipp->wingnum);
7119 	}
7120 
7121 #ifndef NDEBUG
7122 	// add a debug log entry
7123 	if (cleanup_mode == SHIP_DESTROYED) {
7124 		nprintf(("Alan", "SHIP DESTROYED: %s\n", shipp->ship_name));
7125 	} else if (cleanup_mode == SHIP_DEPARTED) {
7126 		nprintf(("Alan", "SHIP DEPARTED: %s\n", shipp->ship_name));
7127 	} else {
7128 		nprintf(("Alan", "SHIP VANISHED: %s\n", shipp->ship_name));
7129 	}
7130 #endif
7131 
7132 	// update wingman status gauge
7133 	if ( (shipp->wing_status_wing_index >= 0) && (shipp->wing_status_wing_pos >= 0) ) {
7134 		if (cleanup_mode == SHIP_DESTROYED) {
7135 			hud_set_wingman_status_dead(shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
7136 		} else if (cleanup_mode == SHIP_DEPARTED) {
7137 			hud_set_wingman_status_departed(shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
7138 		} else {
7139 			hud_set_wingman_status_none(shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
7140 		}
7141 	}
7142 
7143 	// if ship belongs to a wing, do the wing cleanup
7144 	if ( shipp->wingnum != -1 ) {
7145 		wing *wingp = &Wings[shipp->wingnum];
7146 
7147 		if (cleanup_mode == SHIP_DESTROYED) {
7148 			wingp->total_destroyed++;
7149 		} else if (cleanup_mode == SHIP_DEPARTED) {
7150 			wingp->total_departed++;
7151 		} else {
7152 			wingp->total_vanished++;
7153 		}
7154 
7155 		ship_wing_cleanup(shipnum, wingp);
7156 	}
7157 
7158 	// Note, this call to ai_ship_destroy must come after ship_wing_cleanup for guarded wings to
7159 	// properly note the destruction of a ship in their wing.
7160 	if (cleanup_mode == SHIP_DESTROYED) {
7161 		ai_ship_destroy(shipnum, SEF_DESTROYED);	// Do AI stuff for destruction of ship.
7162 	} else {
7163 		ai_ship_destroy(shipnum, SEF_DEPARTED);		// should still do AI cleanup after ship has departed
7164 	}
7165 
7166 	// Goober5000 - lastly, clear out the dead-docked list, per Mantis #2294
7167 	// (for exploding ships, this list should have already been cleared by now, via
7168 	// do_dying_undock_physics, except in the case of the destroy-instantly sexp)
7169 	while (object_is_dead_docked(objp))
7170 	{
7171 		object *docked_objp = dock_get_first_dead_docked_object(objp);
7172 		dock_dead_undock_objects(objp, docked_objp);
7173 	}
7174 }
7175 
7176 /**
7177  * Calculates the blast and damage applied to a ship from another ship blowing up.
7178  *
7179  * @param pos1			ship explosion position
7180  * @param pos2			other ship position
7181  * @param inner_rad		distance from ship center for which full damage is applied
7182  * @param outer_rad		distance from ship center for which no damage is applied
7183  * @param max_damage	maximum damage applied
7184  * @param max_blast		maximum impulse applied from blast
7185  * @param damage		damage applied
7186  * @param blast			impulse applied from blast
7187  */
ship_explode_area_calc_damage(vec3d * pos1,vec3d * pos2,float inner_rad,float outer_rad,float max_damage,float max_blast,float * damage,float * blast)7188 int ship_explode_area_calc_damage( vec3d *pos1, vec3d *pos2, float inner_rad, float outer_rad, float max_damage, float max_blast, float *damage, float *blast )
7189 {
7190 	float dist;
7191 
7192 	dist = vm_vec_dist_quick( pos1, pos2 );
7193 
7194 	// check outside outer radius
7195 	if ( dist > outer_rad )
7196 		return -1;
7197 
7198 	if ( dist < inner_rad ) {
7199 	// check insider inner radius
7200 		*damage = max_damage;
7201 		*blast = max_blast;
7202 	} else {
7203 	// between inner and outer
7204 		float fraction = 1.0f - (dist - inner_rad) / (outer_rad - inner_rad);
7205 		*damage  = fraction * max_damage;
7206 		*blast   = fraction * max_blast;
7207 	}
7208 
7209 	return 1;
7210 }
7211 
7212 /**
7213  * Applies damage to ship close to others when a ship dies and blows up
7214  *
7215  * @param exp_objp			ship object pointers
7216  */
ship_blow_up_area_apply_blast(object * exp_objp)7217 void ship_blow_up_area_apply_blast( object *exp_objp)
7218 {
7219 	ship *shipp;
7220 	ship_info *sip;
7221 	float	inner_rad, outer_rad, max_damage, max_blast, shockwave_speed;
7222 
7223 	//	No area explosion in training missions.
7224 	if (The_mission.game_type & MISSION_TYPE_TRAINING){
7225 		return;
7226 	}
7227 
7228 	Assert( exp_objp != NULL );
7229 	Assert( exp_objp->type == OBJ_SHIP );
7230 	Assert( exp_objp->instance >= 0 );
7231 
7232 	shipp = &Ships[exp_objp->instance];
7233 	sip = &Ship_info[shipp->ship_info_index];
7234 
7235 	Assert( (shipp != NULL) && (sip != NULL) );
7236 
7237 	if ((exp_objp->hull_strength <= KAMIKAZE_HULL_ON_DEATH) && (Ai_info[Ships[exp_objp->instance].ai_index].ai_flags & AIF_KAMIKAZE) && (shipp->special_exp_damage == -1)) {
7238 		int override = Ai_info[shipp->ai_index].kamikaze_damage;
7239 
7240 		inner_rad = exp_objp->radius*2.0f;
7241 		outer_rad = exp_objp->radius*4.0f; // + (override * 0.3f);
7242 		max_damage = i2fl(override);
7243 		max_blast = override * 5.0f;
7244 		shockwave_speed = 100.0f;
7245 	} else {
7246 		if (shipp->use_special_explosion) {
7247 			inner_rad = i2fl(shipp->special_exp_inner);
7248 			outer_rad = i2fl(shipp->special_exp_outer);
7249 			max_damage = i2fl(shipp->special_exp_damage);
7250 			max_blast = i2fl(shipp->special_exp_blast);
7251 			shockwave_speed = i2fl(shipp->special_exp_shockwave_speed);
7252 		} else {
7253 			inner_rad = sip->shockwave.inner_rad;
7254 			outer_rad = sip->shockwave.outer_rad;
7255 			max_damage = sip->shockwave.damage;
7256 			max_blast  = sip->shockwave.blast;
7257 			shockwave_speed = sip->shockwave.speed;
7258 		}
7259 	}
7260 
7261 	// account for ships that give no damage when they blow up.
7262 	if ( (max_damage < 0.1f) && (max_blast < 0.1f) ){
7263 		return;
7264 	}
7265 
7266 	if ( shockwave_speed > 0 ) {
7267 		shockwave_create_info sci;
7268 		shockwave_create_info_init(&sci);
7269 
7270 		strcpy_s(sci.name, sip->shockwave.name);
7271 		strcpy_s(sci.pof_name, sip->shockwave.pof_name);
7272 		sci.inner_rad = inner_rad;
7273 		sci.outer_rad = outer_rad;
7274 		sci.blast = max_blast;
7275 		sci.damage = max_damage;
7276 		sci.speed = shockwave_speed;
7277 		sci.rot_angles.p = frand_range(0.0f, 1.99f*PI);
7278 		sci.rot_angles.b = frand_range(0.0f, 1.99f*PI);
7279 		sci.rot_angles.h = frand_range(0.0f, 1.99f*PI);
7280 		shipfx_do_shockwave_stuff(shipp, &sci);
7281 	} else {
7282 		object *objp;
7283 		float blast = 0.0f;
7284 		float damage = 0.0f;
7285 		for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7286 			if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) ) {
7287 				continue;
7288 			}
7289 
7290 			if ( objp == exp_objp ){
7291 				continue;
7292 			}
7293 
7294 			// don't blast navbuoys
7295 			if ( objp->type == OBJ_SHIP ) {
7296 				if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) {
7297 					continue;
7298 				}
7299 			}
7300 
7301 			if ( ship_explode_area_calc_damage( &exp_objp->pos, &objp->pos, inner_rad, outer_rad, max_damage, max_blast, &damage, &blast ) == -1 ){
7302 				continue;
7303 			}
7304 
7305 			switch ( objp->type ) {
7306 			case OBJ_SHIP:
7307 				ship_apply_global_damage( objp, exp_objp, &exp_objp->pos, damage );
7308 				vec3d force, vec_ship_to_impact;
7309 				vm_vec_sub( &vec_ship_to_impact, &objp->pos, &exp_objp->pos );
7310 				vm_vec_copy_normalize( &force, &vec_ship_to_impact );
7311 				vm_vec_scale( &force, blast );
7312 				ship_apply_whack( &force, &vec_ship_to_impact, objp );
7313 				break;
7314 			case OBJ_ASTEROID:
7315 				asteroid_hit(objp, NULL, NULL, damage);
7316 				break;
7317 			default:
7318 				Int3();
7319 				break;
7320 			}
7321 		}	// end for
7322 	}
7323 }
7324 
7325 /**
7326  * Only ever called once for any ship that dies
7327  *
7328  * This function relies on the "dead dock" list, which replaces the dock_objnum_when_dead
7329  * used in retail.
7330  */
do_dying_undock_physics(object * dying_objp,ship * dying_shipp)7331 void do_dying_undock_physics(object *dying_objp, ship *dying_shipp)
7332 {
7333 	// this function should only be called for an object that was docked...
7334 	// no harm in calling it if it wasn't, but we want to enforce this
7335 	Assert(object_is_dead_docked(dying_objp));
7336 
7337 	object *docked_objp;
7338 
7339 	float damage;
7340 	float impulse_mag;
7341 
7342 	vec3d impulse_norm, impulse_vec, pos;
7343 
7344 	// damage applied to each docked object
7345 	damage = 0.2f * dying_shipp->ship_max_hull_strength;
7346 
7347 	// Goober5000 - as with ai_deathroll_start, we can't simply iterate through the dock list while we're
7348 	// unlinking things.  So just repeatedly unlink the first object.
7349 	while (object_is_dead_docked(dying_objp))
7350 	{
7351 		docked_objp = dock_get_first_dead_docked_object(dying_objp);
7352 		ship *docked_shipp = &Ships[docked_objp->instance];
7353 		int dockee_index = dock_find_dead_dockpoint_used_by_object(docked_objp, dying_objp);
7354 
7355 		// undo all the docking animations for the docked ship only
7356 		model_anim_start_type(docked_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
7357 		model_anim_start_type(docked_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
7358 		model_anim_start_type(docked_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
7359 		model_anim_start_type(docked_shipp, TRIGGER_TYPE_DOCKING_STAGE_1, dockee_index, -1);
7360 
7361 		// only consider the mass of these two objects, not the whole assembly
7362 		// (this is inaccurate, but the alternative is a huge mess of extra code for a very small gain in realism)
7363 		float docked_mass = dying_objp->phys_info.mass + docked_objp->phys_info.mass;
7364 
7365 		// damage this docked object
7366 		ship_apply_global_damage(docked_objp, dying_objp, &dying_objp->pos, damage);
7367 
7368 		// do physics
7369 		vm_vec_sub(&impulse_norm, &docked_objp->pos, &dying_objp->pos);
7370 		vm_vec_normalize(&impulse_norm);
7371 		// set for relative separation velocity of ~30
7372 		impulse_mag = 50.f * docked_objp->phys_info.mass * dying_objp->phys_info.mass / docked_mass;
7373 		vm_vec_copy_scale(&impulse_vec, &impulse_norm, impulse_mag);
7374 		vm_vec_rand_vec_quick(&pos);
7375 		vm_vec_scale(&pos, docked_objp->radius);
7376 		// apply whack to docked object
7377 		physics_apply_whack(&impulse_vec, &pos, &docked_objp->phys_info, &docked_objp->orient, docked_objp->phys_info.mass);
7378 		// enhance rotation of the docked object
7379 		vm_vec_scale(&docked_objp->phys_info.rotvel, 2.0f);
7380 
7381 		// apply whack to dying object
7382 		vm_vec_negate(&impulse_vec);
7383 		vm_vec_rand_vec_quick(&pos);
7384 		vm_vec_scale(&pos, dying_objp->radius);
7385 		physics_apply_whack(&impulse_vec, &pos, &dying_objp->phys_info, &dying_objp->orient, dying_objp->phys_info.mass);
7386 
7387 		// unlink the two objects, since dying_objp has blown up
7388 		dock_dead_undock_objects(dying_objp, docked_objp);
7389 	}
7390 }
7391 
7392 /**
7393  * Do the stuff we do in a frame for a ship that's in its death throes.
7394  */
ship_dying_frame(object * objp,int ship_num)7395 void ship_dying_frame(object *objp, int ship_num)
7396 {
7397 	ship *shipp = &Ships[ship_num];
7398 
7399 	if ( shipp->flags & SF_DYING )	{
7400 		ship_info *sip = &Ship_info[shipp->ship_info_index];
7401 		int knossos_ship = (sip->flags & SIF_KNOSSOS_DEVICE);
7402 
7403 		// bash hull value toward 0 (from self destruct)
7404 		if (objp->hull_strength > 0) {
7405 			int time_left = timestamp_until(shipp->final_death_time);
7406 			float hits_left = objp->hull_strength;
7407 
7408 			objp->hull_strength -= hits_left * (1000.0f * flFrametime) / time_left;
7409 		}
7410 
7411 		// special case of VAPORIZE
7412 		if (shipp->flags & SF_VAPORIZE) {
7413 			if (timestamp_elapsed(shipp->final_death_time)) {
7414 				// play death sound
7415 				snd_play_3d( &Snds[SND_VAPORIZED], &objp->pos, &View_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY  );
7416 
7417 				// do joystick effect
7418 				if (objp == Player_obj) {
7419 					joy_ff_explode();
7420 				}
7421 
7422 				// if dying ship is docked, do damage to docked and physics
7423 				if (object_is_dead_docked(objp))  {
7424 					do_dying_undock_physics(objp, shipp);
7425 				}
7426 
7427 				// do all accounting for respawning client and server side here.
7428 				if (objp == Player_obj) {
7429 					gameseq_post_event(GS_EVENT_DEATH_BLEW_UP);
7430 				}
7431 
7432 				// mark object as dead
7433 				objp->flags |= OF_SHOULD_BE_DEAD;
7434 
7435 				// Don't blow up model.  Only use debris shards.
7436 				// call ship function to clean up after the ship is destroyed.
7437 				ship_cleanup(ship_num, SHIP_DESTROYED);
7438 				return;
7439 			} else {
7440 				return;
7441 			}
7442 		}
7443 
7444 		// bash the desired rotvel
7445 		objp->phys_info.desired_rotvel = shipp->deathroll_rotvel;
7446 
7447 		// Do fireballs for Big ship with propagating explostion, but not Kamikaze
7448 		if (!(Ai_info[shipp->ai_index].ai_flags & AIF_KAMIKAZE) && ship_get_exp_propagates(shipp) && (sip->death_roll_r_mult > 0.0f)) {
7449 			if ( timestamp_elapsed(shipp->next_fireball))	{
7450 				vec3d outpnt, pnt1, pnt2;
7451 				polymodel *pm = model_get(sip->model_num);
7452 
7453 				// Gets two random points on the surface of a submodel
7454 				if ( Cmdline_old_collision_sys ) {
7455 					submodel_get_two_random_points(pm->id, pm->detail[0], &pnt1, &pnt2 );
7456 				} else {
7457 					submodel_get_two_random_points_better(pm->id, pm->detail[0], &pnt1, &pnt2 );
7458 				}
7459 
7460 				model_find_world_point(&outpnt, &pnt1, sip->model_num, pm->detail[0], &objp->orient, &objp->pos );
7461 
7462 				float rad = objp->radius*0.1f;
7463 
7464 				if (sip->death_roll_r_mult != 1.0f)
7465 					rad *= sip->death_roll_r_mult;
7466 
7467 				int fireball_type = fireball_ship_explosion_type(sip);
7468 				if(fireball_type < 0) {
7469 					fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
7470 				}
7471 				fireball_create( &outpnt, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(objp), rad, 0, &objp->phys_info.vel );
7472 				// start the next fireball up in the next 50 - 200 ms (2-3 per frame)
7473 				int min_time = 333;
7474 				int max_time = 500;
7475 
7476 				if (sip->death_roll_time_mult != 1.0f) {
7477 					min_time = (int) (min_time / sip->death_roll_time_mult);
7478 					max_time = (int) (max_time / sip->death_roll_time_mult);
7479 				}
7480 
7481 				shipp->next_fireball = timestamp_rand(min_time,max_time);
7482 
7483 				// do sound - maybe start a random sound, if it has played far enough.
7484 				do_sub_expl_sound(objp->radius, &outpnt, shipp->sub_expl_sound_handle);
7485 			}
7486 		}
7487 
7488 		// create little fireballs for knossos as it dies
7489 		if (knossos_ship) {
7490 			if ( timestamp_elapsed(shipp->next_fireball)) {
7491 				vec3d rand_vec, outpnt; // [0-.7 rad] in plane
7492 				vm_vec_rand_vec_quick(&rand_vec);
7493 				float scale = -vm_vec_dotprod(&objp->orient.vec.fvec, &rand_vec) * (0.9f + 0.2f * frand());
7494 				vm_vec_scale_add2(&rand_vec, &objp->orient.vec.fvec, scale);
7495 				vm_vec_normalize_quick(&rand_vec);
7496 				scale = objp->radius * frand() * 0.717f;
7497 				vm_vec_scale(&rand_vec, scale);
7498 				vm_vec_add(&outpnt, &objp->pos, &rand_vec);
7499 
7500 				float rad = objp->radius*0.2f;
7501 
7502 				int fireball_type = fireball_ship_explosion_type(sip);
7503 				if(fireball_type < 0) {
7504 					fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
7505 				}
7506 				fireball_create( &outpnt, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(objp), rad, 0, &objp->phys_info.vel );
7507 				// start the next fireball up in the next 50 - 200 ms (2-3 per frame)
7508 				shipp->next_fireball = timestamp_rand(333,500);
7509 
7510 				// emit particles
7511 				particle_emitter	pe;
7512 				particle_effect		pef = sip->knossos_end_particles;
7513 
7514 				pe.num_low = pef.n_low;					// Lowest number of particles to create
7515 				pe.num_high = pef.n_high;				// Highest number of particles to create
7516 				pe.pos = outpnt;				// Where the particles emit from
7517 				pe.vel = objp->phys_info.vel;	// Initial velocity of all the particles
7518 				pe.min_life = pef.min_life;	// How long the particles live
7519 				pe.max_life = pef.max_life;	// How long the particles live
7520 				pe.normal = objp->orient.vec.uvec;	// What normal the particle emit around
7521 				pe.normal_variance = pef.variance;		//	How close they stick to that normal 0=on normal, 1=180, 2=360 degree
7522 				pe.min_vel = pef.min_vel;
7523 				pe.max_vel = pef.max_vel;
7524 				pe.min_rad = pef.min_rad;	// * objp->radius;
7525 				pe.max_rad = pef.max_rad; // * objp->radius;
7526 
7527 				if (pe.num_high > 0) {
7528 					particle_emit( &pe, PARTICLE_SMOKE2, 0, 50 );
7529 				}
7530 
7531 				// do sound - maybe start a random sound, if it has played far enough.
7532 				do_sub_expl_sound(objp->radius, &outpnt, shipp->sub_expl_sound_handle);
7533 			}
7534 		}
7535 
7536 		int time_until_minor_explosions = timestamp_until(shipp->final_death_time);
7537 
7538 		// Wait until just before death and set off some explosions
7539 		// If it is less than 1/2 second until large explosion, but there is
7540 		// at least 1/10th of a second left, then create 5 small explosions
7541 		if ( (time_until_minor_explosions < 500) && (time_until_minor_explosions > 100) && (!shipp->pre_death_explosion_happened) ) {
7542 			shipp->next_fireball = timestamp(-1);	// never time out again
7543 			shipp->pre_death_explosion_happened=1;		// Mark this event as having occurred
7544 
7545 			polymodel *pm = model_get(sip->model_num);
7546 
7547 			// Start shockwave for ship with propagating explosion, do now for timing
7548 			if ( ship_get_exp_propagates(shipp) ) {
7549 				ship_blow_up_area_apply_blast( objp );
7550 			}
7551 
7552 			int zz_max = sip->death_fx_count;
7553 
7554 			for (int zz=0; zz<zz_max; zz++ ) {
7555 				// don't make sequence of fireballs for knossos
7556 				if (knossos_ship) {
7557 					break;
7558 				}
7559 
7560 				if (sip->death_fx_r_mult <= 0.0f) {
7561 					break;
7562 				}
7563 				// Find two random vertices on the model, then average them
7564 				// and make the piece start there.
7565 				vec3d tmp, outpnt, pnt1, pnt2;
7566 
7567 				// Gets two random points on the surface of a submodel [KNOSSOS]
7568 				if ( Cmdline_old_collision_sys ) {
7569 					submodel_get_two_random_points(pm->id, pm->detail[0], &pnt1, &pnt2 );
7570 				} else {
7571 					submodel_get_two_random_points_better(pm->id, pm->detail[0], &pnt1, &pnt2 );
7572 				}
7573 
7574 				vm_vec_avg( &tmp, &pnt1, &pnt2 );
7575 				model_find_world_point(&outpnt, &tmp, pm->id, pm->detail[0], &objp->orient, &objp->pos );
7576 
7577 				float rad = objp->radius*0.40f;
7578 
7579 				rad *= sip->death_fx_r_mult;
7580 
7581 				int fireball_type = fireball_ship_explosion_type(sip);
7582 				if(fireball_type < 0) {
7583 					fireball_type = FIREBALL_EXPLOSION_MEDIUM;
7584 				}
7585 				fireball_create( &outpnt, fireball_type, FIREBALL_MEDIUM_EXPLOSION, OBJ_INDEX(objp), rad, 0, &objp->phys_info.vel );
7586 			}
7587 		}
7588 
7589 		if ( timestamp_elapsed(shipp->final_death_time))	{
7590 			shipp->death_time = shipp->final_death_time;
7591 			shipp->final_death_time = timestamp(-1);	// never time out again
7592 
7593 			// play ship explosion sound effect, pick appropriate explosion sound
7594 			int sound_index;
7595 
7596 			if (ship_has_sound(objp, SND_SHIP_EXPLODE_1))
7597 			{
7598 				sound_index = ship_get_sound(objp, SND_SHIP_EXPLODE_1);
7599 			}
7600 			else
7601 			{
7602 				if ( sip->flags & (SIF_CAPITAL | SIF_KNOSSOS_DEVICE) ) {
7603 					sound_index=SND_CAPSHIP_EXPLODE;
7604 				} else {
7605 					 if ( OBJ_INDEX(objp) & 1 ) {
7606 						sound_index=SND_SHIP_EXPLODE_1;
7607 					} else {
7608 						sound_index=SND_SHIP_EXPLODE_2;
7609 					}
7610 				}
7611 			}
7612 
7613 			snd_play_3d( &Snds[sound_index], &objp->pos, &View_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY  );
7614 			if (objp == Player_obj)
7615 				joy_ff_explode();
7616 
7617 			if ( shipp->death_roll_snd != -1 ) {
7618 				snd_stop(shipp->death_roll_snd);
7619 				shipp->death_roll_snd = -1;
7620 			}
7621 
7622 			// if dying ship is docked, do damage to docked and physics
7623 			if (object_is_dead_docked(objp))  {
7624 				do_dying_undock_physics(objp, shipp);
7625 			}
7626 
7627 			// play a random explosion
7628 			particle_emitter	pe;
7629 			particle_effect		pef = sip->regular_end_particles;
7630 
7631 			pe.num_low = pef.n_low;					// Lowest number of particles to create
7632 			pe.num_high = pef.n_high;				// Highest number of particles to create
7633 			pe.pos = objp->pos;				// Where the particles emit from
7634 			pe.vel = objp->phys_info.vel;	// Initial velocity of all the particles
7635 			pe.min_life = pef.min_life;				// How long the particles live
7636 			pe.max_life = pef.max_life;				// How long the particles live
7637 			pe.normal = objp->orient.vec.uvec;	// What normal the particle emit around
7638 			pe.normal_variance = pef.variance;		//	How close they stick to that normal 0=on normal, 1=180, 2=360 degree
7639 			pe.min_vel = pef.min_vel;				// How fast the slowest particle can move
7640 			pe.max_vel = pef.max_vel;				// How fast the fastest particle can move
7641 			pe.min_rad = pef.min_rad;				// Min radius
7642 			pe.max_rad = pef.max_rad;				// Max radius
7643 
7644 			if ((!knossos_ship) && (pe.num_high > 0)) {
7645 				particle_emit( &pe, PARTICLE_SMOKE2, 0 );
7646 			}
7647 
7648 			// If this is a large ship with a propagating explosion, set it to blow up.
7649 			if ( ship_get_exp_propagates(shipp) )	{
7650 				if (Ai_info[shipp->ai_index].ai_flags & AIF_KAMIKAZE) {
7651 					ship_blow_up_area_apply_blast( objp );
7652 				}
7653 				shipfx_large_blowup_init(shipp);
7654 				// need to timeout immediately to keep physics in sync
7655 				shipp->really_final_death_time = timestamp(0);
7656 				polymodel *pm = model_get(sip->model_num);
7657 				shipp->end_death_time = timestamp((int) pm->core_radius);
7658 			} else {
7659 				// only do big fireball if not big ship
7660 				float big_rad;
7661 				int fireball_objnum, fireball_type, default_fireball_type;
7662 				float explosion_life;
7663 				big_rad = objp->radius*1.75f;
7664 
7665 				default_fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
7666 				if (knossos_ship) {
7667 					big_rad = objp->radius * 1.2f;
7668 					default_fireball_type = FIREBALL_EXPLOSION_LARGE1;
7669 				}
7670 				//SUSHI: Option to override radius of big fireball
7671 				if (Ship_info[shipp->ship_info_index].big_exp_visual_rad >= 0)
7672 					big_rad = Ship_info[shipp->ship_info_index].big_exp_visual_rad;
7673 
7674 				fireball_type = fireball_ship_explosion_type(sip);
7675 				if(fireball_type < 0) {
7676 					fireball_type = default_fireball_type;
7677 				}
7678 				fireball_objnum = fireball_create( &objp->pos, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(objp), big_rad, 0, &objp->phys_info.vel );
7679 				if ( fireball_objnum >= 0 )	{
7680 					explosion_life = fireball_lifeleft(&Objects[fireball_objnum]);
7681 				} else {
7682 					explosion_life = 0.0f;
7683 				}
7684 
7685 				// JAS:  I put in all this code because of an item on my todo list that
7686 				// said that the ship destroyed debris shouldn't pop in until the
7687 				// big explosion is 30% done.  I did this on Oct24 and me & Adam
7688 				// thought it looked dumb since the explosion didn't move with the
7689 				// ship, so instead of just taking this code out, since we might need
7690 				// it in the future, I disabled it.   You can reenable it by changing
7691 				// the commenting on the following two lines.
7692 				shipp->end_death_time = shipp->really_final_death_time = timestamp( fl2i(explosion_life*1000.0f)/5 );	// Wait till 30% of vclip time before breaking the ship up.
7693 			}
7694 
7695 			shipp->flags |= SF_EXPLODED;
7696 
7697 			if ( !(ship_get_exp_propagates(shipp)) ) {
7698 				// apply area of effect blast damage from ship explosion
7699 				ship_blow_up_area_apply_blast( objp );
7700 			}
7701 		}
7702 
7703 		if ( timestamp_elapsed(shipp->really_final_death_time))	{
7704 			// Copied from lock all turrets sexp
7705 			// Locks all turrets on ship that is about to split.
7706 			ship_subsys *subsys;
7707 			subsys = GET_FIRST(&shipp->subsys_list);
7708 			while ( subsys != END_OF_LIST(&shipp->subsys_list) )
7709 			{
7710 			// just mark all turrets as locked
7711 				if (subsys->system_info->type == SUBSYSTEM_TURRET) {
7712 					subsys->weapons.flags |= SW_FLAG_TURRET_LOCK;
7713 				}
7714 				subsys = GET_NEXT(subsys);
7715 			}
7716 
7717 			// do large_ship_split and explosion
7718 			if ( shipp->large_ship_blowup_index >= 0 ) {
7719 				if ( shipfx_large_blowup_do_frame(shipp, flFrametime) )	{
7720 					// do all accounting for respawning client and server side here.
7721 					if(objp == Player_obj) {
7722 						gameseq_post_event(GS_EVENT_DEATH_BLEW_UP);
7723 					}
7724 
7725 					objp->flags |= OF_SHOULD_BE_DEAD;
7726 
7727 					ship_cleanup(ship_num, SHIP_DESTROYED);		// call ship function to clean up after the ship is destroyed.
7728 				}
7729 				return;
7730 			}
7731 
7732 			shipfx_blow_up_model(objp, sip->model_num, 0, 20, &objp->pos );
7733 
7734 			// do all accounting for respawning client and server side here.
7735 			if(objp == Player_obj) {
7736 				gameseq_post_event(GS_EVENT_DEATH_BLEW_UP);
7737 			}
7738 
7739 			objp->flags |= OF_SHOULD_BE_DEAD;
7740 
7741 			ship_cleanup(ship_num, SHIP_DESTROYED);		// call ship function to clean up after the ship is destroyed.
7742 			shipp->really_final_death_time = timestamp( -1 );	// Never time out again!
7743 		}
7744 
7745 		// If a ship is dying (and not a capital or big ship) then stutter the engine sound
7746 		if ( timestamp_elapsed(shipp->next_engine_stutter) ) {
7747 			if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
7748 				shipp->flags ^= SF_ENGINES_ON;			// toggle state of engines
7749 				shipp->next_engine_stutter = timestamp_rand(50, 250);
7750 			}
7751 		}
7752 	}
7753 }
7754 
ship_chase_shield_energy_targets(ship * shipp,object * obj,float frametime)7755 void ship_chase_shield_energy_targets(ship *shipp, object *obj, float frametime)
7756 {
7757 	float delta;
7758 	ship_info *sip;
7759 
7760 	if (shipp->flags & SF_DYING)
7761 		return;
7762 
7763 	sip = &Ship_info[shipp->ship_info_index];
7764 
7765 	delta = frametime * ETS_RECHARGE_RATE * shipp->ship_max_shield_strength / 100.0f;
7766 
7767 	//	Chase target_shields and target_weapon_energy
7768 	if (shipp->target_shields_delta > 0.0f) {
7769 		if (delta > shipp->target_shields_delta)
7770 			delta = shipp->target_shields_delta;
7771 
7772 		shield_add_strength(obj, delta);
7773 		shipp->target_shields_delta -= delta;
7774 	} else if (shipp->target_shields_delta < 0.0f) {
7775 		if (delta < -shipp->target_shields_delta)
7776 			delta = -shipp->target_shields_delta;
7777 
7778 		shield_add_strength(obj, -delta);
7779 		shipp->target_shields_delta += delta;
7780 	}
7781 
7782 	delta = frametime * ETS_RECHARGE_RATE * sip->max_weapon_reserve / 100.0f;
7783 
7784 	if (shipp->target_weapon_energy_delta > 0.0f) {
7785 		if (delta > shipp->target_weapon_energy_delta)
7786 			delta = shipp->target_weapon_energy_delta;
7787 
7788 		shipp->weapon_energy += delta;
7789 		shipp->target_weapon_energy_delta -= delta;
7790 	} else if (shipp->target_weapon_energy_delta < 0.0f) {
7791 		if (delta < -shipp->target_weapon_energy_delta)
7792 			delta = -shipp->target_weapon_energy_delta;
7793 
7794 		shipp->weapon_energy -= delta;
7795 		shipp->target_weapon_energy_delta += delta;
7796 	}
7797 
7798 }
7799 
thruster_glow_anim_load(generic_anim * ga)7800 int thruster_glow_anim_load(generic_anim *ga)
7801 {
7802 	if ( !VALID_FNAME(ga->filename) )
7803 		return -1;
7804 
7805 	int fps = 15;
7806 
7807 	ga->first_frame = bm_load(ga->filename);
7808 	if (ga->first_frame < 0)
7809 	{
7810 		Warning(LOCATION, "Couldn't load thruster glow animation '%s'\nPrimary glow type effect does not accept .EFF or .ANI effects", ga->filename);
7811 		return -1;
7812 	}
7813 	ga->num_frames = NOISE_NUM_FRAMES;
7814 
7815 	Assert(fps != 0);
7816 	ga->total_time = i2fl(ga->num_frames)/fps;
7817 
7818 	return 0;
7819 }
7820 
7821 /**
7822  * Loads the animations for ship's afterburners
7823  */
ship_init_thrusters()7824 void ship_init_thrusters()
7825 {
7826 	if ( Thrust_anim_inited == 1 )
7827 		return;
7828 
7829 	for (size_t i = 0; i < Species_info.size(); i++)
7830 	{
7831 		species_info *species = &Species_info[i];
7832 
7833 		generic_anim_load(&species->thruster_info.flames.normal);
7834 		generic_anim_load(&species->thruster_info.flames.afterburn);
7835 
7836 		// Bobboau's extra thruster stuff
7837 		{
7838 			generic_bitmap_load(&species->thruster_secondary_glow_info.normal);
7839 			generic_bitmap_load(&species->thruster_secondary_glow_info.afterburn);
7840 			generic_bitmap_load(&species->thruster_tertiary_glow_info.normal);
7841 			generic_bitmap_load(&species->thruster_tertiary_glow_info.afterburn);
7842 			generic_bitmap_load(&species->thruster_distortion_info.normal);
7843 			generic_bitmap_load(&species->thruster_distortion_info.afterburn);
7844 		}
7845 
7846 		// glows are handled a bit strangely
7847 		thruster_glow_anim_load(&species->thruster_info.glow.normal);
7848 		thruster_glow_anim_load(&species->thruster_info.glow.afterburn);
7849 	}
7850 
7851 	Thrust_anim_inited = 1;
7852 }
7853 
7854 
7855 /**
7856  * Figure out which thruster bitmap will get rendered next time around.
7857  *
7858  * ::ship_render() needs to have shipp->thruster_bitmap set to
7859  * a valid bitmap number, or -1 if we shouldn't render thrusters.
7860  */
ship_do_thruster_frame(ship * shipp,object * objp,float frametime)7861 void ship_do_thruster_frame( ship *shipp, object *objp, float frametime )
7862 {
7863 	float rate;
7864 	int framenum;
7865 	int secondary_glow_bitmap, tertiary_glow_bitmap, distortion_bitmap;
7866 	generic_anim *flame_anim, *glow_anim;
7867 	ship_info	*sinfo = &Ship_info[shipp->ship_info_index];
7868 
7869 	if ( !Thrust_anim_inited ) {
7870 		ship_init_thrusters();
7871 	}
7872 
7873 	if (objp->phys_info.flags & PF_AFTERBURNER_ON) {
7874 		flame_anim = &sinfo->thruster_flame_info.afterburn;		// select afterburner flame
7875 		glow_anim = &sinfo->thruster_glow_info.afterburn;			// select afterburner glow
7876 		secondary_glow_bitmap = sinfo->thruster_secondary_glow_info.afterburn.bitmap_id;
7877 		tertiary_glow_bitmap = sinfo->thruster_tertiary_glow_info.afterburn.bitmap_id;
7878 		distortion_bitmap = sinfo->thruster_distortion_info.afterburn.bitmap_id;
7879 
7880 		rate = 1.5f;		// go at 1.5x faster when afterburners on
7881 	} else if (objp->phys_info.flags & PF_BOOSTER_ON) {
7882 		flame_anim = &sinfo->thruster_flame_info.afterburn;		// select afterburner flame
7883 		glow_anim = &sinfo->thruster_glow_info.afterburn;			// select afterburner glow
7884 		secondary_glow_bitmap = sinfo->thruster_secondary_glow_info.afterburn.bitmap_id;
7885 		tertiary_glow_bitmap = sinfo->thruster_tertiary_glow_info.afterburn.bitmap_id;
7886 		distortion_bitmap = sinfo->thruster_distortion_info.afterburn.bitmap_id;
7887 
7888 		rate = 2.5f;		// go at 2.5x faster when boosters on
7889 	} else {
7890 		flame_anim = &sinfo->thruster_flame_info.normal;			// select normal flame
7891 		glow_anim = &sinfo->thruster_glow_info.normal;				// select normal glow
7892 		secondary_glow_bitmap = sinfo->thruster_secondary_glow_info.normal.bitmap_id;
7893 		tertiary_glow_bitmap = sinfo->thruster_tertiary_glow_info.normal.bitmap_id;
7894 		distortion_bitmap = sinfo->thruster_distortion_info.normal.bitmap_id;
7895 
7896 		// If thrust at 0, go at half as fast, full thrust; full framerate
7897 		// so set rate from 0.67 to 1.67, depending on thrust from 0 to 1
7898 		rate = 0.67f * (1.0f + objp->phys_info.forward_thrust);
7899 	}
7900 
7901 	Assert( frametime > 0.0f );
7902 
7903 	// add primary thruster effects ...
7904 
7905 	if (flame_anim->first_frame >= 0) {
7906 		shipp->thruster_frame += frametime * rate;
7907 
7908 		// Sanity checks
7909 		if (shipp->thruster_frame < 0.0f) {
7910 			shipp->thruster_frame = 0.0f;
7911 		} else if (shipp->thruster_frame > 100.0f) {
7912 			shipp->thruster_frame = 0.0f;
7913 		}
7914 
7915 		while (shipp->thruster_frame > flame_anim->total_time) {
7916 			shipp->thruster_frame -= flame_anim->total_time;
7917 		}
7918 
7919 		framenum = fl2i( (shipp->thruster_frame * flame_anim->num_frames) / flame_anim->total_time );
7920 		CLAMP(framenum, 0, (flame_anim->num_frames - 1));
7921 
7922 		// Get the bitmap for this frame
7923 		shipp->thruster_bitmap = flame_anim->first_frame + framenum;
7924 	} else {
7925 		shipp->thruster_frame = 0.0f;
7926 		shipp->thruster_bitmap = -1;
7927 	}
7928 
7929 	// primary glows ...
7930 	if (glow_anim->first_frame >= 0) {
7931 		shipp->thruster_glow_frame += frametime * rate;
7932 
7933 		// Sanity checks
7934 		if (shipp->thruster_glow_frame < 0.0f) {
7935 			shipp->thruster_glow_frame = 0.0f;
7936 		} else if (shipp->thruster_glow_frame > 100.0f) {
7937 			shipp->thruster_glow_frame = 0.0f;
7938 		}
7939 
7940 		while (shipp->thruster_glow_frame > glow_anim->total_time) {
7941 			shipp->thruster_glow_frame -= glow_anim->total_time;
7942 		}
7943 
7944 		framenum = fl2i( (shipp->thruster_glow_frame*glow_anim->num_frames) / glow_anim->total_time );
7945 		CLAMP(framenum, 0, (glow_anim->num_frames - 1));
7946 
7947 		// Get the bitmap for this frame
7948 		shipp->thruster_glow_bitmap = glow_anim->first_frame;	// + framenum;
7949 		shipp->thruster_glow_noise = Noise[framenum];
7950 	} else {
7951 		shipp->thruster_glow_frame = 0.0f;
7952 		shipp->thruster_glow_bitmap = -1;
7953 		shipp->thruster_glow_noise = 1.0f;
7954 	}
7955 
7956 	// add extra thruster effects
7957 	shipp->thruster_secondary_glow_bitmap = secondary_glow_bitmap;
7958 	shipp->thruster_tertiary_glow_bitmap = tertiary_glow_bitmap;
7959 	shipp->thruster_distortion_bitmap = distortion_bitmap;
7960 }
7961 
7962 
7963 /**
7964  * Figure out which thruster bitmap will get rendered next time around.
7965  *
7966  * ship_render needs to have shipp->thruster_bitmap set to
7967  * a valid bitmap number, or -1 if we shouldn't render thrusters.
7968  *
7969  * This does basically the same thing as ship_do_thruster_frame, except it
7970  * operates on a weapon. This is in the ship code because it needs
7971  * the same thruster animation info as the ship stuff, and I would
7972  * rather extern this one function than all the thruster animation stuff.
7973  */
ship_do_weapon_thruster_frame(weapon * weaponp,object * objp,float frametime)7974 void ship_do_weapon_thruster_frame( weapon *weaponp, object *objp, float frametime )
7975 {
7976 	float rate;
7977 	int framenum;
7978 	generic_anim *flame_anim, *glow_anim;
7979 
7980 	if (!Thrust_anim_inited)
7981 		ship_init_thrusters();
7982 
7983 	species_info *species = &Species_info[weaponp->species];
7984 	weapon_info *wip = &Weapon_info[weaponp->weapon_info_index];
7985 
7986 	// If thrust at 0, go at half as fast, full thrust; full framerate
7987 	// so set rate from 0.67 to 1.67, depending on thrust from 0 to 1
7988 	rate = 0.67f * (1.0f + objp->phys_info.forward_thrust);
7989 
7990 	if (wip->thruster_flame.first_frame >= 0)
7991 		flame_anim = &wip->thruster_flame;
7992 	else
7993 		flame_anim = &species->thruster_info.flames.normal;
7994 
7995 	if (wip->thruster_glow.first_frame >= 0)
7996 		glow_anim = &wip->thruster_glow;
7997 	else
7998 		glow_anim  = &species->thruster_info.glow.normal;
7999 
8000 	Assert( frametime > 0.0f );
8001 
8002 	if (flame_anim->first_frame >= 0) {
8003 		weaponp->thruster_frame += frametime * rate;
8004 
8005 		// Sanity checks
8006 		if ( weaponp->thruster_frame < 0.0f )	weaponp->thruster_frame = 0.0f;
8007 		if ( weaponp->thruster_frame > 100.0f ) weaponp->thruster_frame = 0.0f;
8008 
8009 		while ( weaponp->thruster_frame > flame_anim->total_time )	{
8010 			weaponp->thruster_frame -= flame_anim->total_time;
8011 		}
8012 		framenum = fl2i( (weaponp->thruster_frame*flame_anim->num_frames) / flame_anim->total_time );
8013 		if ( framenum < 0 ) framenum = 0;
8014 		if ( framenum >= flame_anim->num_frames ) framenum = flame_anim->num_frames-1;
8015 
8016 		// Get the bitmap for this frame
8017 		weaponp->thruster_bitmap = flame_anim->first_frame + framenum;
8018 	} else {
8019 		weaponp->thruster_frame = 0.0f;
8020 		weaponp->thruster_bitmap = -1;
8021 	}
8022 
8023 	// Do it for glow bitmaps
8024 	if (glow_anim->first_frame >= 0) {
8025 		weaponp->thruster_glow_frame += frametime * rate;
8026 
8027 		// Sanity checks
8028 		if ( weaponp->thruster_glow_frame < 0.0f )	weaponp->thruster_glow_frame = 0.0f;
8029 		if ( weaponp->thruster_glow_frame > 100.0f ) weaponp->thruster_glow_frame = 0.0f;
8030 
8031 		while ( weaponp->thruster_glow_frame > glow_anim->total_time )	{
8032 			weaponp->thruster_glow_frame -= glow_anim->total_time;
8033 		}
8034 		framenum = fl2i( (weaponp->thruster_glow_frame*glow_anim->num_frames) / glow_anim->total_time );
8035 		if ( framenum < 0 ) framenum = 0;
8036 		if ( framenum >= glow_anim->num_frames ) framenum = glow_anim->num_frames-1;
8037 
8038 		// Get the bitmap for this frame
8039 		weaponp->thruster_glow_bitmap = glow_anim->first_frame;	// + framenum;
8040 		weaponp->thruster_glow_noise = Noise[framenum];
8041 	} else {
8042 		weaponp->thruster_glow_frame = 0.0f;
8043 		weaponp->thruster_glow_bitmap = -1;
8044 		weaponp->thruster_glow_noise = 1.0f;
8045 	}
8046 }
8047 
8048 
8049 
8050 // Repair damaged subsystems for a ship, called for each ship once per frame.
8051 // TODO: optimize by only calling ever N seconds and keeping track of elapsed time
8052 //
8053 // NOTE: need to update current_hits in the sp->subsys_list element, and the sp->subsys_info[]
8054 // element.
8055 #define SHIP_REPAIR_SUBSYSTEM_RATE	0.01f	// percent repair per second for a subsystem
8056 #define SUBSYS_REPAIR_THRESHOLD		0.1	// only repair subsystems that have > 10% strength
ship_auto_repair_frame(int shipnum,float frametime)8057 void ship_auto_repair_frame(int shipnum, float frametime)
8058 {
8059 	ship_subsys		*ssp;
8060 	ship_subsys_info	*ssip;
8061 	ship			*sp;
8062 	ship_info		*sip;
8063 	object			*objp;
8064 	float			real_repair_rate;
8065 
8066 	#ifndef NDEBUG
8067 	if ( !Ship_auto_repair )	// only repair subsystems if Ship_auto_repair flag is set
8068 		return;
8069 	#endif
8070 
8071 	Assert( shipnum >= 0 && shipnum < MAX_SHIPS);
8072 	sp = &Ships[shipnum];
8073 	sip = &Ship_info[sp->ship_info_index];
8074 	objp = &Objects[sp->objnum];
8075 
8076 	//Repair the hull...or maybe unrepair?
8077 	if(sip->hull_repair_rate != 0.0f)
8078 	{
8079 		objp->hull_strength += sp->ship_max_hull_strength * sip->hull_repair_rate * frametime;
8080 
8081 		if(objp->hull_strength > sp->ship_max_hull_strength)
8082 		{
8083 			objp->hull_strength = sp->ship_max_hull_strength;
8084 		}
8085 	}
8086 
8087 	// only allow for the auto-repair of subsystems on small ships
8088 	//...NOT. Check if var has been changed from def -C
8089 	if ( !(sip->flags & SIF_SMALL_SHIP) && sip->subsys_repair_rate == -2.0f)
8090 		return;
8091 
8092 	if(sip->subsys_repair_rate == -2.0f)
8093 		real_repair_rate = SHIP_REPAIR_SUBSYSTEM_RATE;
8094 	else
8095 		real_repair_rate = sip->subsys_repair_rate;
8096 
8097 	// AL 3-14-98: only allow auto-repair if power output not zero
8098 	if (sip->power_output <= 0)
8099 		return;
8100 
8101 	// iterate through subsystems, repair as needed based on elapsed frametime
8102 	for ( ssp = GET_FIRST(&sp->subsys_list); ssp != END_OF_LIST(&sp->subsys_list); ssp = GET_NEXT(ssp) ) {
8103 		Assert(ssp->system_info->type >= 0 && ssp->system_info->type < SUBSYSTEM_MAX);
8104 		ssip = &sp->subsys_info[ssp->system_info->type];
8105 
8106 		if ( ssp->current_hits < ssp->max_hits ) {
8107 
8108 			// only repair those subsystems which are not destroyed
8109 			if ( ssp->max_hits <= 0 || ssp->current_hits <= 0 )
8110 				continue;
8111 
8112 			// do incremental repair on the subsystem
8113 			// check for overflow of current_hits
8114 			ssp->current_hits += ssp->max_hits * real_repair_rate * frametime;
8115 			if ( ssp->current_hits > ssp->max_hits ) {
8116 				ssp->current_hits = ssp->max_hits;
8117 			}
8118 
8119 			// aggregate repair
8120 			if (!(ssp->flags & SSF_NO_AGGREGATE)) {
8121 				ssip->aggregate_current_hits += ssip->aggregate_max_hits * real_repair_rate * frametime;
8122 				if ( ssip->aggregate_current_hits > ssip->aggregate_max_hits ) {
8123 					ssip->aggregate_current_hits = ssip->aggregate_max_hits;
8124 				}
8125 			}
8126 		}
8127 	}	// end for
8128 }
8129 
8130 // this function checks to see how far the player has strayed from his starting location (should be
8131 // single player only).  Issues a warning at some distance.  Makes mission end if he keeps flying away
8132 // 3 strikes and you're out or too far away
8133 #define PLAYER_MAX_DIST_WARNING			700000			// distance in KM at which player gets warning to return to battle
8134 #define PLAYER_DISTANCE_MAX_WARNINGS	3				// maximum number of warnings player can receive before mission ends
8135 #define PLAYER_MAX_DIST_END				750000			// distance from starting loc at which we end mission
8136 #define PLAYER_WARN_DELTA_TIME			10000			//ms
8137 #define PLAYER_DEATH_DELTA_TIME			5000			//ms
8138 
ship_check_player_distance_sub(player * p,int multi_target=-1)8139 void ship_check_player_distance_sub(player *p, int multi_target=-1)
8140 {
8141 	// only check distance for ships
8142 	if ( p->control_mode != PCM_NORMAL )	{
8143 		// already warping out... don't bother checking anymore
8144 		return;
8145 	}
8146 
8147 	float dist = vm_vec_dist_quick(&Objects[p->objnum].pos, &vmd_zero_vector);
8148 
8149 	int give_warning_to_player = 0;
8150 	if ( dist > PLAYER_MAX_DIST_WARNING ) {
8151 		if (p->distance_warning_count == 0) {
8152 			give_warning_to_player = 1;
8153 		} else {
8154 			if (timestamp_until(p->distance_warning_time) < 0) {
8155 				give_warning_to_player = 1;
8156 			}
8157 		}
8158 	}
8159 
8160 	if ( give_warning_to_player ) {
8161 		// increase warning count
8162 		p->distance_warning_count++;
8163 		// set timestamp unless player PLAYER_FLAGS_DIST_TO_BE_KILLED flag is set
8164 		if ( !(p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) ) {
8165 			p->distance_warning_time = timestamp(PLAYER_WARN_DELTA_TIME);
8166 		}
8167 		// issue up to max warnings
8168 		if (p->distance_warning_count <= PLAYER_DISTANCE_MAX_WARNINGS) {
8169 			message_send_builtin_to_player( MESSAGE_STRAY_WARNING, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, multi_target, -1 );
8170 		}
8171 
8172 		if (p->distance_warning_count > PLAYER_DISTANCE_MAX_WARNINGS) {
8173 			p->flags |= PLAYER_FLAGS_DIST_WARNING;
8174 		}
8175 	}
8176 
8177 	if ( !(p->flags & PLAYER_FLAGS_FORCE_MISSION_OVER) && ((p->distance_warning_count > PLAYER_DISTANCE_MAX_WARNINGS) || (dist > PLAYER_MAX_DIST_END)) ) {
8178 //		DKA 5/17/99 - DON'T force warpout.  Won't work multiplayer.  Blow up ship.
8179 		if ( !(p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) ) {
8180 			message_send_builtin_to_player( MESSAGE_STRAY_WARNING_FINAL, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, multi_target, -1 );
8181 			p->flags |= PLAYER_FLAGS_DIST_TO_BE_KILLED;
8182 			p->distance_warning_time = timestamp(PLAYER_DEATH_DELTA_TIME);
8183 		}
8184 
8185 		// get hull strength and blow up
8186 		if ( (p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) && (timestamp_until(p->distance_warning_time) < 0) ) {
8187 			p->flags |= PLAYER_FLAGS_FORCE_MISSION_OVER;
8188 			float damage = 10.0f * Objects[p->objnum].hull_strength;
8189 			ship_apply_global_damage(&Objects[p->objnum], &Objects[p->objnum], NULL, damage);
8190 		}
8191 	}
8192 
8193 	// see if player has moved back into "bounds"
8194 	if ( (dist < PLAYER_MAX_DIST_WARNING) && (p->flags & PLAYER_FLAGS_DIST_WARNING) && !(p->flags & PLAYER_FLAGS_DIST_TO_BE_KILLED) ) {
8195 		p->flags &= ~PLAYER_FLAGS_DIST_WARNING;
8196 		p->distance_warning_count = 1;
8197 	}
8198 }
8199 
ship_check_player_distance()8200 void ship_check_player_distance()
8201 {
8202 	int idx;
8203 
8204 	// multiplayer
8205 	if (Game_mode & GM_MULTIPLAYER) {
8206 		// if I'm the server, check all non-observer players including myself
8207 		if (MULTIPLAYER_MASTER) {
8208 			// warn all players
8209 			for (idx=0; idx<MAX_PLAYERS; idx++) {
8210 				if (MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Objects[Net_players[idx].m_player->objnum].type != OBJ_GHOST) ) {
8211 					// if bad, blow him up
8212 					ship_check_player_distance_sub(Net_players[idx].m_player, idx);
8213 				}
8214 			}
8215 		}
8216 	}
8217 	// single player
8218 	else {
8219 		// maybe blow him up
8220 		ship_check_player_distance_sub(Player);
8221 	}
8222 }
8223 
observer_process_post(object * objp)8224 void observer_process_post(object *objp)
8225 {
8226 	Assert(objp != NULL);
8227 
8228 	if (objp == NULL)
8229 		return;
8230 
8231 	Assert(objp->type == OBJ_OBSERVER);
8232 
8233 	if (Game_mode & GM_MULTIPLAYER) {
8234 		// if I'm just an observer
8235 		if (MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])) {
8236 			float dist = vm_vec_dist_quick(&Player_obj->pos, &vmd_zero_vector);
8237 			// if beyond max dist, reset to 0
8238 			if (dist > PLAYER_MAX_DIST_END) {
8239 				// set me to zero
8240 				if ((Player_obj != NULL) && (Player_obj->type != OBJ_GHOST)) {
8241 					Player_obj->pos = vmd_zero_vector;
8242 				}
8243 			}
8244 		}
8245 	}
8246 }
8247 
8248 /**
8249  * Reset some physics info when ship's engines goes from disabled->enabled
8250  */
ship_reset_disabled_physics(object * objp,int ship_class)8251 void ship_reset_disabled_physics(object *objp, int ship_class)
8252 {
8253 	Assert(objp != NULL);
8254 
8255 	if (objp == NULL)
8256 		return;
8257 
8258 	objp->phys_info.flags &= ~(PF_REDUCED_DAMP | PF_DEAD_DAMP);
8259 	objp->phys_info.side_slip_time_const = Ship_info[ship_class].damp;
8260 }
8261 
8262 /**
8263  * Clear/set the subsystem disrupted flags
8264  */
ship_subsys_disrupted_check(ship * sp)8265 void ship_subsys_disrupted_check(ship *sp)
8266 {
8267 	ship_subsys *ss;
8268 	int engines_disabled=0;
8269 
8270 	if ( sp->subsys_disrupted_flags & (1<<SUBSYSTEM_ENGINE) ) {
8271 		engines_disabled=1;
8272 	}
8273 
8274 	sp->subsys_disrupted_flags=0;
8275 
8276 	ss = GET_FIRST(&sp->subsys_list);
8277 	while ( ss != END_OF_LIST( &sp->subsys_list ) ) {
8278 		if ( !timestamp_elapsed(ss->disruption_timestamp) ) {
8279 			sp->subsys_disrupted_flags |= (1<<ss->system_info->type);
8280 		}
8281 		ss = GET_NEXT( ss );
8282 	}
8283 
8284 	if ( engines_disabled ) {
8285 		if ( !(sp->subsys_disrupted_flags & (1<<SUBSYSTEM_ENGINE)) ) {
8286 			if ( !(sp->flags & SF_DISABLED) ) {
8287 				ship_reset_disabled_physics(&Objects[sp->objnum], sp->ship_info_index);
8288 			}
8289 		}
8290 	}
8291 }
8292 
8293 /**
8294  * Maybe check ship subsystems for disruption, and set/clear flags
8295  */
ship_subsys_disrupted_maybe_check(ship * shipp)8296 void ship_subsys_disrupted_maybe_check(ship *shipp)
8297 {
8298 	if ( timestamp_elapsed(shipp->subsys_disrupted_check_timestamp) ) {
8299 		ship_subsys_disrupted_check(shipp);
8300 		shipp->subsys_disrupted_check_timestamp=timestamp(250);
8301 	}
8302 }
8303 
8304 /**
8305  * Determine if a given subsystem is disrupted (ie inoperable)
8306  *
8307  * @param ss	pointer to ship subsystem
8308  * @return		1 if subsystem is disrupted, 0 if subsystem is not disrupted
8309  */
ship_subsys_disrupted(ship_subsys * ss)8310 int ship_subsys_disrupted(ship_subsys *ss)
8311 {
8312 	if ( !ss ) {
8313 		Int3();		// should never happen, get Alan if it does.
8314 		return 0;
8315 	}
8316 
8317 	if ( timestamp_elapsed(ss->disruption_timestamp) ) {
8318 		return 0;
8319 	} else {
8320 		return 1;
8321 	}
8322 }
8323 
8324 /**
8325  * Disrupt a subsystem (ie make it inoperable for a time)
8326  *
8327  * @param ss	pointer to ship subsystem to be disrupted
8328  * @param time	time in ms that subsystem should be disrupted
8329  */
ship_subsys_set_disrupted(ship_subsys * ss,int time)8330 void ship_subsys_set_disrupted(ship_subsys *ss, int time)
8331 {
8332 	int time_left=0;
8333 
8334 	if ( !ss ) {
8335 		Int3();		// should never happen, get Alan if it does.
8336 		return;
8337 	}
8338 
8339 	time_left=timestamp_until(ss->disruption_timestamp);
8340 	if ( time_left < 0 ) {
8341 		time_left=0;
8342 	}
8343 
8344 	ss->disruption_timestamp = timestamp(time+time_left);
8345 }
8346 
8347 /**
8348  * Determine if a given type of subsystem is disrupted (i.e. inoperable)
8349  *
8350  * @param sp	pointer to ship containing subsystem
8351  * @param type	type of subsystem (SUBSYSTEM_*)
8352  * @return		1 if subsystem is disrupted, 0 if subsystem is not disrupted
8353  */
ship_subsys_disrupted(ship * sp,int type)8354 int ship_subsys_disrupted(ship *sp, int type)
8355 {
8356 	Assert ( sp != NULL );
8357 	Assert ( type >= 0 && type < SUBSYSTEM_MAX );
8358 
8359 	// Bogus pointer to ship to check for disrupted subsystem
8360 	if (sp == NULL)
8361 		return 0;
8362 
8363 	if ( sp->subsys_disrupted_flags & (1<<type) ) {
8364 		return 1;
8365 	} else {
8366 		return 0;
8367 	}
8368 }
8369 
8370 float Decay_rate = 1.0f / 120.0f;
8371 DCF(lethality_decay, "time in sec to return from 100 to 0")
8372 {
8373 	dc_get_arg(ARG_FLOAT);
8374 	Decay_rate = Dc_arg_float;
8375 }
8376 
8377 float min_lethality = 0.0f;
8378 
lethality_decay(ai_info * aip)8379 void lethality_decay(ai_info *aip)
8380 {
8381 	float decay_rate = Decay_rate;
8382 	aip->lethality -= 100.0f * decay_rate * flFrametime;
8383 	aip->lethality = MAX(-10.0f, aip->lethality);
8384 
8385 #ifndef NDEBUG
8386 	if (Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) {
8387 		if (Framecount % 10 == 0) {
8388 			int num_turrets = 0;
8389 			if ((aip->target_objnum != -1) && (Objects[aip->target_objnum].type == OBJ_SHIP)) {
8390 				//TODO: put this where it belongs, this would involve recompiling *everything* right now
8391 				//-WMC
8392 				int num_turrets_attacking(object *turret_parent, int target_objnum);
8393 				num_turrets = num_turrets_attacking(&Objects[aip->target_objnum], Ships[aip->shipnum].objnum);
8394 			}
8395 			nprintf(("lethality", "Player lethality: %.1f, num turrets targeting player: %d\n", aip->lethality, num_turrets));
8396 		}
8397 	}
8398 #endif
8399 }
8400 
ship_process_pre(object * objp,float frametime)8401 void ship_process_pre(object *objp, float frametime)
8402 {
8403 	if ( (objp == NULL) || !frametime )
8404 		return;
8405 }
8406 
MONITOR(NumShips)8407 MONITOR( NumShips )
8408 
8409 void ship_radar_process( object * obj, ship * shipp, ship_info * sip )
8410 {
8411 	Assert( obj != NULL);
8412 	Assert( shipp != NULL );
8413 	Assert( sip != NULL);
8414 
8415 	shipp->radar_last_status = shipp->radar_current_status;
8416 
8417 	RadarVisibility visibility = radar_is_visible(obj);
8418 
8419 	if (visibility == NOT_VISIBLE)
8420 	{
8421 		if (shipp->radar_last_contact < 0 && shipp->radar_visible_since < 0)
8422 		{
8423 			shipp->radar_visible_since = -1;
8424 			shipp->radar_last_contact = -1;
8425 		}
8426 		else
8427 		{
8428 			shipp->radar_visible_since = -1;
8429 			shipp->radar_last_contact = Missiontime;
8430 		}
8431 	}
8432 	else if (visibility == VISIBLE || visibility == DISTORTED)
8433 	{
8434 		if (shipp->radar_visible_since < 0)
8435 		{
8436 			shipp->radar_visible_since = Missiontime;
8437 		}
8438 
8439 		shipp->radar_last_contact = Missiontime;
8440 	}
8441 
8442 	shipp->radar_current_status = visibility;
8443 }
8444 
8445 
8446 /**
8447  * Player ship uses this code, but does a quick out after doing a few things.
8448  *
8449  * When adding code to this function, decide whether or not a client in a multiplayer game
8450  * needs to execute the code you are adding.  Code which moves things, creates things, etc
8451  * probably doesn't need to be called.  If you don't know -- find Allender!!!
8452  */
ship_process_post(object * obj,float frametime)8453 void ship_process_post(object * obj, float frametime)
8454 {
8455 	int	num;
8456 	ship	*shipp;
8457 	ship_info *sip;
8458 
8459 	if(obj->type != OBJ_SHIP){
8460 		nprintf(("Network","Ignoring non-ship object in ship_process_post()\n"));
8461 		return;
8462 	}
8463 
8464 	MONITOR_INC( NumShips, 1 );
8465 
8466 	num = obj->instance;
8467 	Assert( num >= 0 && num < MAX_SHIPS);
8468 	Assert( obj->type == OBJ_SHIP );
8469 	Assert( Ships[num].objnum == OBJ_INDEX(obj));
8470 
8471 	shipp = &Ships[num];
8472 
8473 	sip = &Ship_info[shipp->ship_info_index];
8474 
8475 	shipp->shield_hits = 0;
8476 
8477 	update_ets(obj, frametime);
8478 
8479 	afterburners_update(obj, frametime);
8480 
8481 	ship_subsys_disrupted_maybe_check(shipp);
8482 
8483 	ship_dying_frame(obj, num);
8484 
8485 	ship_chase_shield_energy_targets(shipp, obj, frametime);
8486 
8487 	// AL 1-6-98: record the initial ammo counts for ships, which is used as the max limit for rearming
8488 	// Goober5000 - added ballistics support
8489 	if ( !(shipp->flags & SF_AMMO_COUNT_RECORDED) )
8490 	{
8491 		int max_missiles;
8492 		for ( int i=0; i<MAX_SHIP_SECONDARY_BANKS; i++ ) {
8493 			if ( red_alert_mission() )
8494 			{
8495 				max_missiles = get_max_ammo_count_for_bank(shipp->ship_info_index, i, shipp->weapons.secondary_bank_weapons[i]);
8496 				shipp->weapons.secondary_bank_start_ammo[i] = max_missiles;
8497 			}
8498 			else
8499 			{
8500 				shipp->weapons.secondary_bank_start_ammo[i] = shipp->weapons.secondary_bank_ammo[i];
8501 			}
8502 		}
8503 
8504 		if ( sip->flags & SIF_BALLISTIC_PRIMARIES )
8505 		{
8506 			for ( int i=0; i<MAX_SHIP_PRIMARY_BANKS; i++ )
8507 			{
8508 				if ( red_alert_mission() )
8509 				{
8510 					max_missiles = get_max_ammo_count_for_primary_bank(shipp->ship_info_index, i, shipp->weapons.primary_bank_weapons[i]);
8511 					shipp->weapons.primary_bank_start_ammo[i] = max_missiles;
8512 				}
8513 				else
8514 				{
8515 					shipp->weapons.primary_bank_start_ammo[i] = shipp->weapons.primary_bank_ammo[i];
8516 				}
8517 			}
8518 		}
8519 
8520 		shipp->flags |= SF_AMMO_COUNT_RECORDED;
8521 	}
8522 
8523 	if(!(Game_mode & GM_STANDALONE_SERVER)) {
8524 		// Plot ship on the radar.  What about multiplayer ships?
8525 		if ( obj != Player_obj )			// don't plot myself.
8526 			radar_plot_object( obj );
8527 
8528 		// MWA -- move the spark code to before the check for multiplayer master
8529 		//	Do ship sparks.  Don't do sparks on my ship (since I cannot see it).  This
8530 		// code will do sparks on other ships in multiplayer though.
8531 		// JAS: Actually in external view, you can see sparks, so I don't do sparks
8532 		// on the Viewer_obj, not Player_obj.
8533 		if ( (obj != Viewer_obj) && timestamp_elapsed(Ships[num].next_hit_spark) )	{
8534 			shipfx_emit_spark(num,-1);	// -1 means choose random spark location
8535 		}
8536 
8537 		if ( obj != Viewer_obj )	{
8538 			shipfx_do_damaged_arcs_frame( shipp );
8539 		}
8540 
8541 		// JAS - flicker the thruster bitmaps
8542 		ship_do_thruster_frame(shipp,obj,frametime);
8543 	}
8544 
8545 	ship_auto_repair_frame(num, frametime);
8546 
8547 	shipfx_do_lightning_frame(shipp);
8548 
8549 	// if the ship has an EMP effect active, process it
8550 	emp_process_ship(shipp);
8551 
8552 	// call the contrail system
8553 	ct_ship_process(shipp);
8554 
8555 	// process engine wash
8556 	void engine_wash_ship_process(ship *shipp);
8557 	engine_wash_ship_process(shipp);
8558 
8559 	// update TAG info
8560 	if(shipp->tag_left > 0.0f){
8561 		shipp->tag_left -= flFrametime;
8562 		if(shipp->tag_left <= 0.000001f){
8563 			shipp->tag_left = -1.0f;
8564 
8565 			mprintf(("Killing TAG for %s\n", shipp->ship_name));
8566 		}
8567 	}
8568 
8569 	// update level 2 TAG info
8570 	if(shipp->level2_tag_left > 0.0f){
8571 		shipp->level2_tag_left -= flFrametime;
8572 		if(shipp->level2_tag_left <= 0.000001f){
8573 			shipp->level2_tag_left = -1.0f;
8574 
8575 			mprintf(("Killing level 2 TAG for %s\n", shipp->ship_name));
8576 		}
8577 	}
8578 
8579 	if ( shipp->flags & SF_ARRIVING && Ai_info[shipp->ai_index].mode != AIM_BAY_EMERGE )	{
8580 		// JAS -- if the ship is warping in, just move it forward at a speed
8581 		// fast enough to move 2x its radius in SHIP_WARP_TIME seconds.
8582 		shipfx_warpin_frame( obj, frametime );
8583 	} else if ( shipp->flags & SF_DEPART_WARP ) {
8584 		// JAS -- if the ship is warping out, just move it forward at a speed
8585 		// fast enough to move 2x its radius in SHIP_WARP_TIME seconds.
8586 		shipfx_warpout_frame( obj, frametime );
8587 	}
8588 
8589 	// update radar status of the ship
8590 	ship_radar_process(obj, shipp, sip);
8591 
8592 	if ( (!(shipp->flags & SF_ARRIVING) || (Ai_info[shipp->ai_index].mode == AIM_BAY_EMERGE)
8593 		|| ((sip->warpin_type == WT_IN_PLACE_ANIM) && (shipp->flags & SF_ARRIVING_STAGE_2)) )
8594 		&&	!(shipp->flags & SF_DEPART_WARP))
8595 	{
8596 		//	Do AI.
8597 
8598 		// for multiplayer people.  return here if in multiplay and not the host
8599 		if ( MULTIPLAYER_CLIENT ) {
8600 			model_anim_handle_multiplayer( &Ships[num] );
8601 			return;
8602 		}
8603 
8604 		// MWA -- moved the code to maybe fire swarm missiles to after the check for
8605 		// multiplayer master.  Only single player and multi server needs to do this code
8606 		// this code might call ship_fire_secondary which will send the fire packets
8607 		swarm_maybe_fire_missile(num);
8608 
8609 		// maybe fire turret swarm missiles
8610 		void turret_swarm_maybe_fire_missile(int num);
8611 		turret_swarm_maybe_fire_missile(num);
8612 
8613 		// maybe fire a corkscrew missile (just like swarmers)
8614 		cscrew_maybe_fire_missile(num);
8615 
8616 		//rotate player subobjects since its processed by the ai functions
8617 		// AL 2-19-98: Fire turret for player if it exists
8618 		//WMC - changed this to call process_subobjects
8619 		if ( (obj->flags & OF_PLAYER_SHIP) && !Player_use_ai )
8620 		{
8621 			ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
8622 			if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
8623 			{
8624 				if (aip->support_ship_objnum >= 0)
8625 				{
8626 					if (vm_vec_dist_quick(&obj->pos, &Objects[aip->support_ship_objnum].pos) < (obj->radius + Objects[aip->support_ship_objnum].radius) * 1.25f)
8627 						return;
8628 				}
8629 			}
8630 			process_subobjects(OBJ_INDEX(obj));
8631 		}
8632 
8633 		if (obj == Player_obj) {
8634 			ship_check_player_distance();
8635 		}
8636 
8637 		// update ship lethality
8638 		if ( Ships[num].ai_index >= 0 ){
8639 			if (!physics_paused && !ai_paused){
8640 				lethality_decay(&Ai_info[Ships[num].ai_index]);
8641 			}
8642 		}
8643 
8644 		// if the ship is an observer ship don't need to do AI
8645 		if ( obj->type == OBJ_OBSERVER)  {
8646 			return;
8647 		}
8648 
8649 		// Goober5000 - player may want to use AI
8650 		if ( (Ships[num].ai_index >= 0) && (!(obj->flags & OF_PLAYER_SHIP) || Player_use_ai) ){
8651 			if (!physics_paused && !ai_paused){
8652 				ai_process( obj, Ships[num].ai_index, frametime );
8653 			}
8654 		}
8655 	}
8656 }
8657 
8658 
8659 /**
8660  * Set the ship level weapons based on the information contained in the ship info.
8661  *
8662  * Weapon assignments are checked against the model to ensure the models
8663  * and the ship info weapon data are in synch.
8664  */
ship_set_default_weapons(ship * shipp,ship_info * sip)8665 void ship_set_default_weapons(ship *shipp, ship_info *sip)
8666 {
8667 	int			i, j;
8668 	polymodel	*pm;
8669 	ship_weapon *swp = &shipp->weapons;
8670 	weapon_info *wip;
8671 
8672 	//	Copy primary and secondary weapons from ship_info to ship.
8673 	//	Later, this will happen in the weapon loadout screen.
8674 	for (i=0; i < MAX_SHIP_PRIMARY_BANKS; i++){
8675 		swp->primary_bank_weapons[i] = sip->primary_bank_weapons[i];
8676 		swp->primary_bank_slot_count[i] = 1; // RSAXVC DYN LINK CODE
8677 	}
8678 
8679 	for (i=0; i < MAX_SHIP_SECONDARY_BANKS; i++){
8680 		swp->secondary_bank_weapons[i] = sip->secondary_bank_weapons[i];
8681 	}
8682 
8683 	// Copy the number of primary and secondary banks to ship, and verify that
8684 	// model is in synch
8685 	pm = model_get( sip->model_num );
8686 
8687 	// Primary banks
8688 	if ( pm->n_guns > sip->num_primary_banks ) {
8689 		Assert(pm->n_guns <= MAX_SHIP_PRIMARY_BANKS);
8690 		Error(LOCATION, "There are %d primary banks in the model file,\nbut only %d primary banks specified for %s.\nThis must be fixed, as it will cause crashes.\n", pm->n_guns, sip->num_primary_banks, sip->name);
8691 		for ( i = sip->num_primary_banks; i < pm->n_guns; i++ ) {
8692 			// Make unspecified weapon for bank be a laser
8693 			for ( j = 0; j < Num_player_weapon_precedence; j++ ) {
8694 				Assertion((Player_weapon_precedence[j] > 0), "Error reading player weapon precedence list. Check weapons.tbl for $Player Weapon Precedence entry, and correct as necessary.\n");
8695 				int weapon_id = Player_weapon_precedence[j];
8696 				if ( (Weapon_info[weapon_id].subtype == WP_LASER) || (Weapon_info[weapon_id].subtype == WP_BEAM) ) {
8697 					swp->primary_bank_weapons[i] = weapon_id;
8698 					break;
8699 				}
8700 			}
8701 			Assert(swp->primary_bank_weapons[i] >= 0);
8702 		}
8703 		sip->num_primary_banks = pm->n_guns;
8704 	}
8705 	else if ( pm->n_guns < sip->num_primary_banks ) {
8706 		Warning(LOCATION, "There are %d primary banks specified for %s\nbut only %d primary banks in the model\n", sip->num_primary_banks, sip->name, pm->n_guns);
8707 		sip->num_primary_banks = pm->n_guns;
8708 	}
8709 
8710 	// Secondary banks
8711 	if ( pm->n_missiles > sip->num_secondary_banks ) {
8712 		Assert(pm->n_missiles <= MAX_SHIP_SECONDARY_BANKS);
8713 		Error(LOCATION, "There are %d secondary banks in the model file,\nbut only %d secondary banks specified for %s.\nThis must be fixed, as it will cause crashes.\n", pm->n_missiles, sip->num_secondary_banks, sip->name);
8714 		for ( i = sip->num_secondary_banks; i < pm->n_missiles; i++ ) {
8715 			// Make unspecified weapon for bank be a missile
8716 			for ( j = 0; j < Num_player_weapon_precedence; j++ ) {
8717 				Assertion((Player_weapon_precedence[j] > 0), "Error reading player weapon precedence list. Check weapons.tbl for $Player Weapon Precedence entry, and correct as necessary.\n");
8718 				int weapon_id = Player_weapon_precedence[j];
8719 				if (Weapon_info[weapon_id].subtype == WP_MISSILE) {
8720 					swp->secondary_bank_weapons[i] = weapon_id;
8721 					break;
8722 				}
8723 			}
8724 			Assert(swp->secondary_bank_weapons[i] >= 0);
8725 		}
8726 		sip->num_secondary_banks = pm->n_missiles;
8727 	}
8728 	else if ( pm->n_missiles < sip->num_secondary_banks ) {
8729 		Warning(LOCATION, "There are %d secondary banks specified for %s,\n but only %d secondary banks in the model.\n", sip->num_secondary_banks, sip->name, pm->n_missiles);
8730 		sip->num_secondary_banks = pm->n_missiles;
8731 	}
8732 
8733 	// added ballistic primary support - Goober5000
8734 	swp->num_primary_banks = sip->num_primary_banks;
8735 	for ( i = 0; i < swp->num_primary_banks; i++ )
8736 	{
8737 		wip = &Weapon_info[swp->primary_bank_weapons[i]];
8738 
8739 		if ( wip->wi_flags2 & WIF2_BALLISTIC )
8740 		{
8741 			if (Fred_running){
8742 				swp->primary_bank_ammo[i] = 100;
8743 			}
8744 			else
8745 			{
8746 				float capacity, size;
8747 				capacity = (float) sip->primary_bank_ammo_capacity[i];
8748 				size = (float) wip->cargo_size;
8749 				swp->primary_bank_ammo[i] = fl2i((capacity / size)+0.5f);
8750 				swp->primary_bank_start_ammo[i] = swp->primary_bank_ammo[i];
8751 			}
8752 
8753 			swp->primary_bank_capacity[i] = sip->primary_bank_ammo_capacity[i];
8754 		}
8755 	}
8756 
8757 	swp->num_secondary_banks = sip->num_secondary_banks;
8758 	for ( i = 0; i < swp->num_secondary_banks; i++ ) {
8759 		if (Fred_running){
8760 			swp->secondary_bank_ammo[i] = 100;
8761 		} else {
8762 			wip = &Weapon_info[swp->secondary_bank_weapons[i]];
8763 			float size = (float) wip->cargo_size;
8764 			swp->secondary_bank_ammo[i] = fl2i(sip->secondary_bank_ammo_capacity[i]/size);
8765 			// Karajorma - Support ships will use the wrong values if we don't set this.
8766 			swp->secondary_bank_start_ammo[i] = swp->secondary_bank_ammo[i];
8767 		}
8768 
8769 		swp->secondary_bank_capacity[i] = sip->secondary_bank_ammo_capacity[i];
8770 	}
8771 
8772 	for ( i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++ ){
8773 		swp->next_primary_fire_stamp[i] = timestamp(0);
8774 		swp->last_primary_fire_stamp[i] = -1;
8775 		swp->burst_counter[i] = 0;
8776 		swp->last_primary_fire_sound_stamp[i] = timestamp(0);
8777 	}
8778 
8779 	for ( i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++ ){
8780 		swp->next_secondary_fire_stamp[i] = timestamp(0);
8781 		swp->last_secondary_fire_stamp[i] = -1;
8782 		swp->burst_counter[i + MAX_SHIP_PRIMARY_BANKS] = 0;
8783 	}
8784 
8785 	//Countermeasures
8786 	shipp->current_cmeasure = sip->cmeasure_type;
8787 }
8788 
8789 
8790 /**
8791  * Faster version of ship_check_collision that does not do checking at the polygon
8792  * level.  Just checks to see if a vector will intersect a sphere.
8793  */
ship_check_collision_fast(object * obj,object * other_obj,vec3d * hitpos)8794 int ship_check_collision_fast( object * obj, object * other_obj, vec3d * hitpos)
8795 {
8796 	int num;
8797 	mc_info mc;
8798 
8799 	Assert( obj->type == OBJ_SHIP );
8800 	Assert( obj->instance >= 0 );
8801 
8802 	num = obj->instance;
8803 
8804 	mc_info_init(&mc);
8805 	mc.model_instance_num = Ships[num].model_instance_num;
8806 	mc.model_num = Ship_info[Ships[num].ship_info_index].model_num;	// Fill in the model to check
8807 	mc.orient = &obj->orient;					// The object's orient
8808 	mc.pos = &obj->pos;							// The object's position
8809 	mc.p0 = &other_obj->last_pos;			// Point 1 of ray to check
8810 	mc.p1 = &other_obj->pos;					// Point 2 of ray to check
8811 	mc.flags = MC_ONLY_SPHERE;				// flags
8812 
8813 	model_collide(&mc);
8814 	if (mc.num_hits)
8815 		*hitpos = mc.hit_point_world;
8816 
8817 	return mc.num_hits;
8818 }
8819 
8820 /**
8821  * Ensure create time for ship is unique
8822  */
ship_make_create_time_unique(ship * shipp)8823 void ship_make_create_time_unique(ship *shipp)
8824 {
8825 	static int last_smctu_initial_time = -1;
8826 	static int last_smctu_final_time = -1;
8827 	int		sanity_counter = 0, collision;
8828 	ship		*compare_shipp;
8829 	ship_obj	*so;
8830 	uint		new_create_time;
8831 
8832 	new_create_time = shipp->create_time;
8833 
8834 	while (1) {
8835 
8836 		collision = 0;
8837 
8838 		for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8839 			compare_shipp = &Ships[Objects[so->objnum].instance];
8840 
8841 			if ( compare_shipp == shipp ) {
8842 				continue;
8843 			}
8844 
8845 			if ( compare_shipp->create_time == new_create_time )
8846 			{
8847 				if((unsigned int)sanity_counter == 0 && (unsigned int)last_smctu_initial_time == shipp->create_time)
8848 				{
8849 					//WMC: If we're creating a whole bunch of ships at once, we can
8850 					//shortcut this process by looking at the last call to this function
8851 					//This fixes a bug when more than 50 ships are created at once.
8852 					new_create_time = last_smctu_final_time + 1;
8853 				}
8854 				else
8855 				{
8856 					new_create_time++;
8857 				}
8858 				collision = 1;
8859 				break;
8860 			}
8861 		}
8862 
8863 		if ( !collision ) {
8864 			last_smctu_initial_time = shipp->create_time;
8865 			last_smctu_final_time = new_create_time;
8866 			shipp->create_time = new_create_time;
8867 			break;
8868 		}
8869 
8870 		if ( sanity_counter++ > MAX_SHIPS ) {
8871 			Int3();
8872 			break;
8873 		}
8874 	}
8875 }
8876 
8877 int	Ship_subsys_hwm = 0;
8878 
show_ship_subsys_count()8879 void show_ship_subsys_count()
8880 {
8881 	object	*objp;
8882 	int		count = 0;
8883 	int		o_type = 0;
8884 
8885 	for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8886 		o_type = (int)objp->type;
8887 		if (o_type == OBJ_SHIP) {
8888 			count += Ship_info[Ships[objp->instance].ship_info_index].n_subsystems;
8889 		}
8890 	}
8891 
8892 	if (count > Ship_subsys_hwm) {
8893 		Ship_subsys_hwm = count;
8894 	}
8895 }
8896 
ship_init_afterburners(ship * shipp)8897 void ship_init_afterburners(ship *shipp)
8898 {
8899 	Assert( shipp );
8900 
8901 	shipp->ab_count = 0;
8902 
8903 	if (shipp->ship_info_index < 0) {
8904 		Int3();
8905 		return;
8906 	}
8907 
8908 	ship_info *sip = &Ship_info[shipp->ship_info_index];
8909 	Assert( sip->model_num >= 0 );
8910 	polymodel *pm = model_get(sip->model_num);
8911 	Assert( pm != NULL );
8912 
8913 	if ( !(sip->flags & SIF_AFTERBURNER) ) {
8914 		return;
8915 	}
8916 
8917 	if (sip->afterburner_trail.bitmap_id < 0) {
8918 		return;
8919 	}
8920 
8921 	for (int i = 0; i < pm->n_thrusters; i++) {
8922 		thruster_bank *bank = &pm->thrusters[i];
8923 
8924 		for (int j = 0; j < bank->num_points; j++) {
8925 			// this means you've reached the max # of AB trails for a ship
8926 			if (shipp->ab_count >= MAX_SHIP_CONTRAILS) {
8927 				Int3();
8928 				break;
8929 			}
8930 
8931 			trail_info *ci = &shipp->ab_info[shipp->ab_count];
8932 
8933 			if (bank->points[j].norm.xyz.z > -0.5f) {
8934 				continue; // only make ab trails for thrusters that are pointing backwards
8935 			}
8936 
8937 			ci->pt = bank->points[j].pnt; //offset
8938 
8939 			ci->w_start = bank->points[j].radius * sip->afterburner_trail_width_factor;	// width * table loaded width factor
8940 			ci->w_end = 0.05f; //end width
8941 
8942 			ci->a_start = 1.0f * sip->afterburner_trail_alpha_factor; // start alpha  * table loaded alpha factor
8943 			ci->a_end = 0.0f; //end alpha
8944 
8945 			ci->max_life = sip->afterburner_trail_life;	// table loaded max life
8946 			ci->stamp = 60;	//spew time???
8947 
8948 			ci->n_fade_out_sections = sip->afterburner_trail_faded_out_sections; // initial fade out
8949 
8950 			ci->texture.bitmap_id = sip->afterburner_trail.bitmap_id; // table loaded bitmap used on this ships burner trails
8951 
8952 			nprintf(("AB TRAIL", "AB trail point #%d made for '%s'\n", shipp->ab_count, shipp->ship_name));
8953 
8954 			shipp->ab_count++;
8955 		}
8956 	}
8957 }
8958 
8959 /**
8960  * Returns object index of ship.
8961  * @return -1 means failed.
8962  */
ship_create(matrix * orient,vec3d * pos,int ship_type,char * ship_name)8963 int ship_create(matrix *orient, vec3d *pos, int ship_type, char *ship_name)
8964 {
8965 	int			i, n, objnum, j, k, t;
8966 	ship_info	*sip;
8967 	ship			*shipp;
8968 
8969 	t = ship_get_num_ships();
8970 
8971 	// The following check caps the number of ships that can be created.  Because Fred needs
8972 	// to create all the ships, regardless of when they arrive/depart, it needs a higher
8973 	// limit than FreeSpace.  On release, however, we will reduce it, thus FreeSpace needs
8974 	// to check against what this limit will be, otherwise testing the missions before
8975 	// release could work fine, yet not work anymore once a release build is made.
8976 	if (Fred_running) {
8977 		if (t >= MAX_SHIPS)
8978 			return -1;
8979 
8980 	} else {
8981 		if (t >= SHIPS_LIMIT) {
8982 			Error(LOCATION, XSTR("There is a limit of %d ships in the mission at once.  Please be sure that you do not have more than %d ships present in the mission at the same time.", 1495), SHIPS_LIMIT, SHIPS_LIMIT );
8983 			return -1;
8984 		}
8985 	}
8986 
8987 	for (n=0; n<MAX_SHIPS; n++){
8988 		if (Ships[n].objnum == -1){
8989 			break;
8990 		}
8991 	}
8992 
8993 	if (n == MAX_SHIPS){
8994 		return -1;
8995 	}
8996 
8997 	Assert((ship_type >= 0) && (ship_type < Num_ship_classes));
8998 	sip = &(Ship_info[ship_type]);
8999 	shipp = &Ships[n];
9000 	shipp->clear();
9001 
9002 	sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);		// use the highest detail level
9003 	if(strlen(sip->cockpit_pof_file))
9004 	{
9005 		sip->cockpit_model_num = model_load(sip->cockpit_pof_file, 0, NULL);
9006 	}
9007 
9008 	// maybe load an optional hud target model
9009 	if(strlen(sip->pof_file_hud)){
9010 		// check to see if a "real" ship uses this model. if so, load it up for him so that subsystems are setup properly
9011 		int idx;
9012 		for(idx=0; idx<Num_ship_classes; idx++){
9013 			if(!stricmp(Ship_info[idx].pof_file, sip->pof_file_hud)){
9014 				Ship_info[idx].model_num = model_load(Ship_info[idx].pof_file, Ship_info[idx].n_subsystems, &Ship_info[idx].subsystems[0]);
9015 			}
9016 		}
9017 
9018 		// mow load it for me with no subsystems
9019 		sip->model_num_hud = model_load(sip->pof_file_hud, 0, NULL);
9020 	}
9021 
9022 	polymodel *pm = model_get(sip->model_num);
9023 
9024 	ship_copy_subsystem_fixup(sip);
9025 	show_ship_subsys_count();
9026 
9027 	if ( sip->num_detail_levels != pm->n_detail_levels )
9028 	{
9029 		if ( !Is_standalone )
9030 		{
9031 			// just log to file for standalone servers
9032 			Warning(LOCATION, "For ship '%s', detail level\nmismatch. Table has %d,\nPOF has %d.", sip->name, sip->num_detail_levels, pm->n_detail_levels );
9033 		}
9034 		else
9035 		{
9036 			nprintf(("Warning",  "For ship '%s', detail level mismatch. Table has %d, POF has %d.", sip->name, sip->num_detail_levels, pm->n_detail_levels ));
9037 		}
9038 	}
9039 	for ( i=0; i<pm->n_detail_levels; i++ )
9040 		pm->detail_depth[i] = (i < sip->num_detail_levels) ? i2fl(sip->detail_distance[i]) : 0.0f;
9041 
9042 	// JAS: Nav buoys don't need to do collisions!
9043 	// G5K: Corrected to apply specifically for ships with the no-collide flag.  (In retail, navbuoys already have this flag, so this doesn't break anything.)
9044 	if ( sip->flags & SIF_NO_COLLIDE )	{
9045 		objnum = obj_create(OBJ_SHIP, -1, n, orient, pos, model_get_radius(sip->model_num), OF_RENDERS | OF_PHYSICS );
9046 	} else {
9047 		objnum = obj_create(OBJ_SHIP, -1, n, orient, pos, model_get_radius(sip->model_num), OF_RENDERS | OF_COLLIDES | OF_PHYSICS );
9048 	}
9049 	Assert( objnum >= 0 );
9050 
9051 	shipp->ai_index = ai_get_slot(n);
9052 	Assert( shipp->ai_index >= 0 );
9053 
9054 	// Goober5000 - if no ship name specified, or if specified ship already exists,
9055 	// or if specified ship has exited, use a default name
9056 	if ((ship_name == NULL) || (ship_name_lookup(ship_name) >= 0) || (ship_find_exited_ship_by_name(ship_name) >= 0)) {
9057 		char suffix[NAME_LENGTH];
9058 		sprintf(suffix, NOX(" %d"), n);
9059 
9060 		// ensure complete ship name doesn't overflow the buffer
9061 		int name_len = MIN(NAME_LENGTH - strlen(suffix) - 1, strlen(Ship_info[ship_type].name));
9062 		Assert(name_len > 0);
9063 
9064 		strncpy(shipp->ship_name, Ship_info[ship_type].name, name_len);
9065 		strcpy(shipp->ship_name + name_len, suffix);
9066 	} else {
9067 		strcpy_s(shipp->ship_name, ship_name);
9068 	}
9069 
9070 	ship_set_default_weapons(shipp, sip);	//	Moved up here because ship_set requires that weapon info be valid.  MK, 4/28/98
9071 	ship_set(n, objnum, ship_type);
9072 
9073 	init_ai_object(objnum);
9074 	ai_clear_ship_goals( &Ai_info[Ships[n].ai_index] );		// only do this one here.  Can't do it in init_ai because it might wipe out goals in mission file
9075 
9076 	//	Allocate shield and initialize it.
9077 	if (pm->shield.ntris) {
9078 		shipp->shield_integrity = (float *) vm_malloc(sizeof(float) * pm->shield.ntris);
9079 		for (i=0; i<pm->shield.ntris; i++)
9080 			shipp->shield_integrity[i] = 1.0f;
9081 	} else
9082 		shipp->shield_integrity = NULL;
9083 
9084 	// Bump the object radius to ensure that collision detection works right
9085 	// even when spread shields extend outside the model's natural radius
9086 	if (sip->flags2 & SIF2_AUTO_SPREAD_SHIELDS) {
9087 		Objects[objnum].radius += sip->auto_shield_spread;
9088 	}
9089 
9090 	// allocate memory for keeping glow point bank status (enabled/disabled)
9091 	{
9092 		bool val = true; // default value, enabled
9093 
9094 		if (pm->n_glow_point_banks)
9095 			shipp->glow_point_bank_active.resize( pm->n_glow_point_banks, val );
9096 	}
9097 
9098 	// fix up references into paths for this ship's model to point to a ship_subsys entry instead
9099 	// of a submodel index.  The ship_subsys entry should be the same for *all* instances of the
9100 	// same ship.
9101 	if (!(sip->flags & SIF_PATH_FIXUP))
9102 	{
9103 		for ( i = 0; i < pm->n_paths; i++ )
9104 		{
9105 			for ( j = 0; j < pm->paths[i].nverts; j++ )
9106 			{
9107 				for ( k = 0; k < pm->paths[i].verts[j].nturrets; k++ )
9108 				{
9109 					int ptindex = pm->paths[i].verts[j].turret_ids[k];		// this index is a submodel number (ala bspgen)
9110 					int index;
9111 					ship_subsys *ss;
9112 
9113 					// iterate through the ship_subsystems looking for an id that matches
9114 					index = 0;
9115 					ss = GET_FIRST(&Ships[n].subsys_list);
9116 					while ( ss != END_OF_LIST( &Ships[n].subsys_list ) ) {
9117 						if ( ss->system_info->subobj_num == ptindex ) {			// when these are equal, fix up the ref
9118 							pm->paths[i].verts[j].turret_ids[k] = index;				// in path structure to index a ship_subsys
9119 							break;
9120 						}
9121 						index++;
9122 						ss = GET_NEXT( ss );
9123 					}
9124 
9125 					if ( ss == END_OF_LIST(&Ships[n].subsys_list) )
9126 						Warning(LOCATION, "Couldn't fix up turret indices in spline path\n\nModel: %s\nPath: %s\nVertex: %d\nTurret model id:%d\n\nThis probably means that the turret was not specified in the ship table(s).", sip->pof_file, pm->paths[i].name, j, ptindex );
9127 				}
9128 			}
9129 		}
9130 		sip->flags |= SIF_PATH_FIXUP;
9131 	}
9132 
9133 	// Add this ship to Ship_obj_list
9134 	shipp->ship_list_index = ship_obj_list_add(objnum);
9135 
9136 	// Set time when ship is created
9137 	shipp->create_time = timer_get_milliseconds();
9138 
9139 	ship_make_create_time_unique(shipp);
9140 
9141 	// first try at ABtrails -Bobboau
9142 	ship_init_afterburners(shipp);
9143 
9144 	// call the contrail system
9145 	ct_ship_create(shipp);
9146 
9147 	model_anim_set_initial_states(shipp);
9148 
9149 	shipp->model_instance_num = model_create_instance(sip->model_num);
9150 
9151 	shipp->time_created = Missiontime;
9152 
9153 	return objnum;
9154 }
9155 
9156 /**
9157  * Change the ship model for a ship to that for ship class 'ship_type'
9158  *
9159  * @param n			index of ship in ::Ships[] array
9160  * @param ship_type	ship class (index into ::Ship_info[])
9161  */
ship_model_change(int n,int ship_type)9162 void ship_model_change(int n, int ship_type)
9163 {
9164 	int			i;
9165 	ship_info	*sip;
9166 	ship			*sp;
9167 	polymodel * pm;
9168 	object *objp;
9169 
9170 	Assert( n >= 0 && n < MAX_SHIPS );
9171 	sp = &Ships[n];
9172 	sip = &(Ship_info[ship_type]);
9173 	objp = &Objects[sp->objnum];
9174 
9175 	// get new model
9176 	if (sip->model_num == -1) {
9177 		sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
9178 	}
9179 
9180 	if ( sip->cockpit_model_num == -1 ) {
9181 		if ( strlen(sip->cockpit_pof_file) ) {
9182 			sip->cockpit_model_num = model_load(sip->cockpit_pof_file, 0, NULL);
9183 		}
9184 	}
9185 
9186 	pm = model_get(sip->model_num);
9187 	Objects[sp->objnum].radius = model_get_radius(pm->id);
9188 
9189 	// page in nondims in game
9190 	if ( !Fred_running )
9191 		model_page_in_textures(sip->model_num, ship_type);
9192 
9193 	// allocate memory for keeping glow point bank status (enabled/disabled)
9194 	{
9195 		bool val = true; // default value, enabled
9196 
9197 		// clear out any old gpb's first, then add new ones if needed
9198 		sp->glow_point_bank_active.clear();
9199 
9200 		if (pm->n_glow_point_banks)
9201 			sp->glow_point_bank_active.resize( pm->n_glow_point_banks, val );
9202 	}
9203 
9204 	ship_copy_subsystem_fixup(sip);
9205 
9206 	if ( sip->num_detail_levels != pm->n_detail_levels )
9207 	{
9208 		if ( !Is_standalone )
9209 		{
9210 			// just log to file for standalone servers
9211 			Warning(LOCATION, "For ship '%s', detail level\nmismatch. Table has %d,\nPOF has %d.", sip->name, sip->num_detail_levels, pm->n_detail_levels );
9212 		}
9213 		else
9214 		{
9215 			nprintf(("Warning",  "For ship '%s', detail level mismatch. Table has %d, POF has %d.", sip->name, sip->num_detail_levels, pm->n_detail_levels ));
9216 		}
9217 	}
9218 	for ( i=0; i<pm->n_detail_levels; i++ )
9219 		pm->detail_depth[i] = (i < sip->num_detail_levels) ? i2fl(sip->detail_distance[i]) : 0.0f;
9220 
9221 	if (sip->flags2 & SIF2_MODEL_POINT_SHIELDS) {
9222 		objp->n_quadrants = pm->shield_points.size();
9223 		sp->shield_points = pm->shield_points;
9224 	} else {
9225 		objp->n_quadrants = DEFAULT_SHIELD_SECTIONS;
9226 	}
9227 	objp->shield_quadrant.resize(objp->n_quadrants);
9228 
9229 	if (sp->shield_integrity != NULL) {
9230 		vm_free(sp->shield_integrity);
9231 		sp->shield_integrity = NULL;
9232 	}
9233 
9234 	//	Allocate shield and initialize it.
9235 	if (pm->shield.ntris) {
9236 		sp->shield_integrity = (float *) vm_malloc(sizeof(float) * pm->shield.ntris);
9237 
9238 		for (i = 0; i < pm->shield.ntris; i++) {
9239 			sp->shield_integrity[i] = 1.0f;
9240 		}
9241 	} else {
9242 		sp->shield_integrity = NULL;
9243 	}
9244 
9245 	// reset texture animations
9246 	sp->base_texture_anim_frametime = game_get_overall_frametime();
9247 }
9248 
9249 /**
9250  * Change the ship class on a ship, and changing all required information
9251  * for consistency (ie textures, subsystems, weapons, physics)
9252  *
9253  * @param n			index of ship in ::Ships[] array
9254  * @param ship_type	ship class (index into ::Ship_info[])
9255  * @param by_sexp	SEXP reference
9256  */
change_ship_type(int n,int ship_type,int by_sexp)9257 void change_ship_type(int n, int ship_type, int by_sexp)
9258 {
9259 	int i;
9260 	ship_info	*sip;
9261 	ship_info	*sip_orig;
9262 	ship			*sp;
9263 	ship_weapon *swp;
9264 	ship_subsys *ss;
9265 	object		*objp;
9266 	p_object	*p_objp;
9267 	float hull_pct, shield_pct;
9268 	physics_info ph_inf;
9269 
9270 	Assert( n >= 0 && n < MAX_SHIPS );
9271 	sp = &Ships[n];
9272 
9273 	// do a quick out if we're already using the new ship class
9274 	if (sp->ship_info_index == ship_type)
9275 		return;
9276 
9277 	swp = &sp->weapons;
9278 	sip = &(Ship_info[ship_type]);
9279 	sip_orig = &Ship_info[sp->ship_info_index];
9280 	objp = &Objects[sp->objnum];
9281 	p_objp = mission_parse_get_parse_object(sp->ship_name);
9282 	ph_inf = objp->phys_info;
9283 
9284 
9285 	// Goober5000 - we can't copy the ship object because the tree of structs contains at least
9286 	// one class without a copy constructor.  So let's just save the information we need.
9287 
9288 	// these are wiped by ets_init_ship
9289 	int orig_wep_rechg_idx = sp->weapon_recharge_index;
9290 	int orig_shd_rechg_idx = sp->shield_recharge_index;
9291 	int orig_eng_rechg_idx = sp->engine_recharge_index;
9292 
9293 
9294 	// Goober5000 - maintain the original hull, shield, and subsystem percentages... gah
9295 	// ...except when in FRED, because this stuff is handled in the missionparse/missionsave part. The E
9296 
9297 	if (!Fred_running) {
9298 		// hull
9299 		if (sp->special_hitpoints) {
9300 			hull_pct = objp->hull_strength / sp->ship_max_hull_strength;
9301 		} else {
9302 			Assert( Ship_info[sp->ship_info_index].max_hull_strength > 0.0f );
9303 			hull_pct = objp->hull_strength / Ship_info[sp->ship_info_index].max_hull_strength;
9304 		}
9305 
9306 		// extra check
9307 		Assert(hull_pct > 0.0f && hull_pct <= 1.0f);
9308 		CLAMP(hull_pct, 0.1f, 1.0f);
9309 
9310 		// shield
9311 		if (sp->special_shield > 0) {
9312 			shield_pct = shield_get_strength(objp) / sp->ship_max_shield_strength;
9313 		} else if (Ship_info[sp->ship_info_index].max_shield_strength > 0.0f) {
9314 			shield_pct = shield_get_strength(objp) / Ship_info[sp->ship_info_index].max_shield_strength;
9315 		} else {
9316 			shield_pct = 0.0f;
9317 		}
9318 
9319 		// extra check
9320 		Assert(shield_pct >= 0.0f && shield_pct <= 1.0f);
9321 		CLAMP(shield_pct, 0.0f, 1.0f);
9322 	} else {
9323 		shield_pct = hull_pct = 1.0f;
9324 	}
9325 
9326 	// subsystems
9327 	int num_saved_subsystems = 0;
9328 	char **subsys_names = new char *[sip_orig->n_subsystems];
9329 	float *subsys_pcts = new float[sip_orig->n_subsystems];
9330 
9331 	ss = GET_FIRST(&sp->subsys_list);
9332 	while ( ss != END_OF_LIST(&sp->subsys_list) )
9333 	{
9334 		if (num_saved_subsystems == sip_orig->n_subsystems)
9335 		{
9336 			Error(LOCATION, "Subsystem mismatch while changing ship class from '%s' to '%s'!", sip_orig->name, sip->name);
9337 			break;
9338 		}
9339 
9340 		// save subsys information
9341 		subsys_names[num_saved_subsystems] = new char[NAME_LENGTH];
9342 		strcpy(subsys_names[num_saved_subsystems], ss->system_info->subobj_name);
9343 
9344 		if (ss->max_hits > 0.0f)
9345 			subsys_pcts[num_saved_subsystems] = ss->current_hits / ss->max_hits;
9346 		else
9347 			subsys_pcts[num_saved_subsystems] = ss->max_hits;
9348 
9349 		// extra check
9350 		Assert(subsys_pcts[num_saved_subsystems] >= 0.0f && subsys_pcts[num_saved_subsystems] <= 1.0f);
9351         CLAMP(subsys_pcts[num_saved_subsystems], 0.0f, 1.0f);
9352 
9353 		num_saved_subsystems++;
9354 		ss = GET_NEXT(ss);
9355 	}
9356 
9357 	// point to new ship data
9358 	ship_model_change(n, ship_type);
9359 	sp->ship_info_index = ship_type;
9360 
9361 	if (!Fred_running) {
9362 		//WMC - set warp effects
9363 		ship_set_warp_effects(objp, sip);
9364 	}
9365 
9366 	// set the correct hull strength
9367 	if (Fred_running) {
9368 		sp->ship_max_hull_strength = 100.0f;
9369 		objp->hull_strength = 100.0f;
9370 	} else {
9371 		if (sp->special_hitpoints > 0) {
9372 			sp->ship_max_hull_strength = (float)sp->special_hitpoints;
9373 		} else {
9374 			sp->ship_max_hull_strength = sip->max_hull_strength;
9375 		}
9376 
9377 		objp->hull_strength = hull_pct * sp->ship_max_hull_strength;
9378 	}
9379 
9380 	// set the correct shield strength
9381 	if (Fred_running) {
9382 		if (sp->ship_max_shield_strength)
9383 			sp->ship_max_shield_strength = 100.0f;
9384 		objp->shield_quadrant[0] = 100.0f;
9385 	} else {
9386 		if (sp->special_shield >= 0) {
9387 			sp->ship_max_shield_strength = (float)sp->special_shield;
9388 		} else {
9389 			sp->ship_max_shield_strength = sip->max_shield_strength;
9390 		}
9391 
9392 		shield_set_strength(objp, shield_pct * sp->ship_max_shield_strength);
9393 	}
9394 
9395 	// Goober5000: div-0 checks
9396 	Assert(sp->ship_max_hull_strength > 0.0f);
9397 	Assert(objp->hull_strength > 0.0f);
9398 
9399 	// Mantis 2763: moved down to have access to the right ship_max_shield_strength value
9400 	// make sure that shields are disabled/enabled if they need to be - Chief1983
9401 	if (!Fred_running) {
9402 		if ((p_objp->flags2 & P2_OF_FORCE_SHIELDS_ON) && (sp->ship_max_shield_strength > 0.0f)) {
9403 			objp->flags &= ~OF_NO_SHIELDS;
9404 		} else if ((p_objp->flags & P_OF_NO_SHIELDS) || (sp->ship_max_shield_strength == 0.0f)) {
9405 			objp->flags |= OF_NO_SHIELDS;
9406 		// Since there's not a mission flag set to be adjusting this, see if there was a change from a ship that normally has shields to one that doesn't, and vice versa
9407 		} else if (!(sip_orig->flags2 & SIF2_INTRINSIC_NO_SHIELDS) && (sip->flags2 & SIF2_INTRINSIC_NO_SHIELDS)) {
9408 			objp->flags |= OF_NO_SHIELDS;
9409 		} else if ((sip_orig->flags2 & SIF2_INTRINSIC_NO_SHIELDS) && !(sip->flags2 & SIF2_INTRINSIC_NO_SHIELDS) && (sp->ship_max_shield_strength > 0.0f)) {
9410 			objp->flags &= ~OF_NO_SHIELDS;
9411 		}
9412 	}
9413 
9414 	// niffiwan: set new armor types
9415 	sp->armor_type_idx = sip->armor_type_idx;
9416 	sp->shield_armor_type_idx = sip->shield_armor_type_idx;
9417 	sp->collision_damage_type_idx = sip->collision_damage_type_idx;
9418 	sp->debris_damage_type_idx = sip->debris_damage_type_idx;
9419 
9420 	// subsys stuff done only after hull stuff is set
9421 	// if the subsystem list is not currently empty, then we need to clear it out first.
9422 	ship_subsystems_delete(sp);
9423 
9424 	// fix up the subsystems
9425 	subsys_set( sp->objnum );
9426 
9427 	// Goober5000 - restore the subsystem percentages
9428 	ss = GET_FIRST(&sp->subsys_list);
9429 	while ( ss != END_OF_LIST(&sp->subsys_list) )
9430 	{
9431 		for (i = 0; i < num_saved_subsystems; i++)
9432 		{
9433 			if (!subsystem_stricmp(ss->system_info->subobj_name, subsys_names[i]))
9434 			{
9435 				ss->current_hits = ss->max_hits * subsys_pcts[i];
9436 				break;
9437 			}
9438 		}
9439 
9440 		ss = GET_NEXT(ss);
9441 	}
9442 	ship_recalc_subsys_strength(sp);
9443 
9444 	// now free the memory
9445 	for (i = 0; i < sip_orig->n_subsystems; i++)
9446 		delete[] subsys_names[i];
9447 
9448 	delete [] subsys_names;
9449 	delete [] subsys_pcts;
9450 
9451 	sp->afterburner_fuel = MAX(0, sip->afterburner_fuel_capacity - (sip_orig->afterburner_fuel_capacity - sp->afterburner_fuel));
9452 	sp->cmeasure_count = MAX(0, sip->cmeasure_max - (sip_orig->cmeasure_max - sp->cmeasure_count));
9453 
9454 	// avoid cases where either of these are 0
9455 	if (sp->current_max_speed != 0 && sip_orig->max_speed != 0) {
9456 		sp->current_max_speed = sip->max_speed * (sp->current_max_speed / sip_orig->max_speed);
9457 	}
9458 	else {
9459 		sp->current_max_speed = sip->max_speed;
9460 	}
9461 
9462 	ship_set_default_weapons(sp, sip);
9463 	physics_ship_init(&Objects[sp->objnum]);
9464 	ets_init_ship(&Objects[sp->objnum]);
9465 
9466 	// Reset physics to previous values
9467 	if (by_sexp) {
9468 		Objects[sp->objnum].phys_info.desired_rotvel = ph_inf.desired_rotvel;
9469 		Objects[sp->objnum].phys_info.desired_vel = ph_inf.desired_vel;
9470 		Objects[sp->objnum].phys_info.forward_thrust = ph_inf.forward_thrust;
9471 		Objects[sp->objnum].phys_info.fspeed = ph_inf.fspeed;
9472 		Objects[sp->objnum].phys_info.heading = ph_inf.heading;
9473 		Objects[sp->objnum].phys_info.last_rotmat = ph_inf.last_rotmat;
9474 		Objects[sp->objnum].phys_info.prev_fvec = ph_inf.prev_fvec;
9475 		Objects[sp->objnum].phys_info.prev_ramp_vel = ph_inf.prev_ramp_vel;
9476 		Objects[sp->objnum].phys_info.reduced_damp_decay = ph_inf.reduced_damp_decay;
9477 		Objects[sp->objnum].phys_info.rotvel = ph_inf.rotvel;
9478 		Objects[sp->objnum].phys_info.shockwave_decay = ph_inf.shockwave_decay;
9479 		Objects[sp->objnum].phys_info.shockwave_shake_amp = ph_inf.shockwave_shake_amp;
9480 		Objects[sp->objnum].phys_info.side_thrust = ph_inf.side_thrust;
9481 		Objects[sp->objnum].phys_info.speed = ph_inf.speed;
9482 		Objects[sp->objnum].phys_info.vel = ph_inf.vel;
9483 		Objects[sp->objnum].phys_info.vert_thrust = ph_inf.vert_thrust;
9484 	}
9485 
9486 	ship_set_new_ai_class(n, sip->ai_class);
9487 
9488 	//======================================================
9489 
9490 	// Bobboau's thruster stuff again
9491 	if (sip->afterburner_trail.bitmap_id < 0)
9492 		generic_bitmap_load(&sip->afterburner_trail);
9493 
9494 	sp->ab_count = 0;
9495 	if (sip->flags & SIF_AFTERBURNER)
9496 	{
9497 		polymodel *pm = model_get(sip->model_num);
9498 
9499 		for (int h = 0; h < pm->n_thrusters; h++)
9500 		{
9501 			for (int j = 0; j < pm->thrusters[h].num_points; j++)
9502 			{
9503 				// this means you've reached the max # of AB trails for a ship
9504 				Assert(sip->ct_count <= MAX_SHIP_CONTRAILS);
9505 
9506 				trail_info *ci = &sp->ab_info[sp->ab_count];
9507 
9508 				// only make ab trails for thrusters that are pointing backwards
9509 				if (pm->thrusters[h].points[j].norm.xyz.z > -0.5)
9510 					continue;
9511 
9512 				ci->pt = pm->thrusters[h].points[j].pnt;	//offset
9513 				ci->w_start = pm->thrusters[h].points[j].radius * sip->afterburner_trail_width_factor;	// width * table loaded width factor
9514 
9515 				ci->w_end = 0.05f;//end width
9516 
9517 				ci->a_start = 1.0f * sip->afterburner_trail_alpha_factor;	// start alpha  * table loaded alpha factor
9518 
9519 				ci->a_end = 0.0f;//end alpha
9520 
9521 				ci->max_life = sip->afterburner_trail_life;	// table loaded max life
9522 
9523 				ci->stamp = 60;	//spew time???
9524 
9525 				ci->n_fade_out_sections = sip->afterburner_trail_faded_out_sections; // table loaded n sections to be faded out
9526 
9527 				ci->texture.bitmap_id = sip->afterburner_trail.bitmap_id; // table loaded bitmap used on this ships burner trails
9528 				nprintf(("AB TRAIL", "AB trail point #%d made for '%s'\n", sp->ab_count, sp->ship_name));
9529 				sp->ab_count++;
9530 				Assert(MAX_SHIP_CONTRAILS > sp->ab_count);
9531 			}
9532 		}
9533 	}//end AB trails -Bobboau
9534 
9535 	// Goober5000 - check other class-specific flags too
9536 
9537 	if (sip->flags & SIF_STEALTH)			// changing TO a stealthy ship class
9538 		sp->flags2 |= SF2_STEALTH;
9539 	else if (sip_orig->flags & SIF_STEALTH)	// changing FROM a stealthy ship class
9540 		sp->flags2 &= ~SF2_STEALTH;
9541 
9542 	if (sip->flags & SIF_SHIP_CLASS_DONT_COLLIDE_INVIS)				// changing TO a don't-collide-invisible ship class
9543 		sp->flags2 |= SF2_DONT_COLLIDE_INVIS;
9544 	else if (sip_orig->flags & SIF_SHIP_CLASS_DONT_COLLIDE_INVIS)	// changing FROM a don't-collide-invisible ship class
9545 		sp->flags2 &= ~SF2_DONT_COLLIDE_INVIS;
9546 
9547 	if (sip->flags & SIF_NO_COLLIDE)								// changing TO a no_collide ship
9548 		Objects[sp->objnum].flags &= ~OF_COLLIDES;
9549 	else if (sip_orig->flags & SIF_NO_COLLIDE)						// changing FROM a no_collide ship
9550 		Objects[sp->objnum].flags |= OF_COLLIDES;
9551 
9552 	if (sip->flags2 & SIF2_NO_ETS)
9553 		sp->flags2 |= SF2_NO_ETS;
9554 	else if (sip_orig->flags2 & SIF2_NO_ETS)
9555 		sp->flags2 &= ~SF2_NO_ETS;
9556 
9557 
9558 	// Chief1983: Make sure that when changing to a new ship with secondaries, you switch to bank 0.  They still won't
9559 	// fire if the SF2_SECONDARIES_LOCKED flag is on as this should have carried over.
9560 	if ( swp->num_secondary_banks > 0 && swp->current_secondary_bank == -1 ){
9561 		swp->current_secondary_bank = 0;
9562 	}
9563 
9564 	// Bobboau's animation fixup
9565 	for( i = 0; i<MAX_SHIP_PRIMARY_BANKS;i++){
9566 			swp->primary_animation_position[i] = MA_POS_NOT_SET;
9567 	}
9568 	for( i = 0; i<MAX_SHIP_SECONDARY_BANKS;i++){
9569 			swp->secondary_animation_position[i] = MA_POS_NOT_SET;
9570 	}
9571 	model_anim_set_initial_states(sp);
9572 
9573 	//Reassign sound stuff
9574 	ship_assign_sound(sp);
9575 
9576 	// create new model instance data
9577 	sp->model_instance_num = model_create_instance(sip->model_num);
9578 
9579 	// Valathil - Reinitialize collision checks
9580 	if ( Cmdline_old_collision_sys ) {
9581 		obj_remove_pairs(objp);
9582 		obj_add_pairs(OBJ_INDEX(objp));
9583 	} else {
9584 		obj_remove_collider(OBJ_INDEX(objp));
9585 		obj_add_collider(OBJ_INDEX(objp));
9586 	}
9587 
9588 	// The E - If we're switching during gameplay, make sure we get valid primary/secondary selections
9589 	if ( by_sexp ) {
9590 		if (sip_orig->num_primary_banks > sip->num_primary_banks) {
9591 			sp->weapons.current_primary_bank = 0;
9592 		}
9593 
9594 		if (sip_orig->num_secondary_banks > sip->num_secondary_banks) {
9595 			sp->weapons.current_secondary_bank = 0;
9596 		}
9597 
9598 		// While we're at it, let's copy over the ETS settings too
9599 		sp->weapon_recharge_index = orig_wep_rechg_idx;
9600 		sp->shield_recharge_index = orig_shd_rechg_idx;
9601 		sp->engine_recharge_index = orig_eng_rechg_idx;
9602 	}
9603 
9604 	// zookeeper - If we're switching in the loadout screen, make sure we retain initial velocity set in FRED
9605 	if (!(Game_mode & GM_IN_MISSION) && !(Fred_running)) {
9606 		Objects[sp->objnum].phys_info.speed = (float) p_objp->initial_velocity * sip->max_speed / 100.0f;
9607 		Objects[sp->objnum].phys_info.vel.xyz.z = Objects[sp->objnum].phys_info.speed;
9608 		Objects[sp->objnum].phys_info.prev_ramp_vel = Objects[sp->objnum].phys_info.vel;
9609 		Objects[sp->objnum].phys_info.desired_vel = Objects[sp->objnum].phys_info.vel;
9610 	}
9611 
9612 	// Goober5000 - if we're changing to a ship class that has a different default set of orders, update the orders
9613 	// (this avoids wiping the orders if we're e.g. changing between fighter classes)
9614 	if (Fred_running)
9615 	{
9616 		int old_defaults = ship_get_default_orders_accepted(sip_orig);
9617 		int new_defaults = ship_get_default_orders_accepted(sip);
9618 
9619 		if (old_defaults != new_defaults)
9620 			sp->orders_accepted = new_defaults;
9621 	}
9622 
9623 	// Goober5000 - deal with texture replacement by re-applying the same code we used during parsing
9624 	if (sp->ship_replacement_textures != NULL)
9625 	{
9626 		// clear them out because the new positions may be different
9627 		for (i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
9628 			sp->ship_replacement_textures[i] = -1;
9629 
9630 		// now fill them in according to texture name
9631 		for (i = 0; i < p_objp->num_texture_replacements; i++)
9632 		{
9633 			int j;
9634 			polymodel *pm = model_get(sip->model_num);
9635 
9636 			// look for textures
9637 			for (j = 0; j < pm->n_textures; j++)
9638 			{
9639 				texture_map *tmap = &pm->maps[j];
9640 
9641 				int tnum = tmap->FindTexture(p_objp->replacement_textures[i].old_texture);
9642 				if(tnum > -1)
9643 					sp->ship_replacement_textures[j * TM_NUM_TYPES + tnum] = p_objp->replacement_textures[i].new_texture_id;
9644 			}
9645 		}
9646 	}
9647 
9648 	if (sip->uses_team_colors)
9649 	{
9650 		sp->team_name = sip->default_team_name;
9651 	}
9652 }
9653 
9654 #ifndef NDEBUG
9655 /**
9656  * Fire the debug laser
9657  */
ship_fire_primary_debug(object * objp)9658 int ship_fire_primary_debug(object *objp)
9659 {
9660 	int	i;
9661 	ship	*shipp = &Ships[objp->instance];
9662 	vec3d wpos;
9663 
9664 	if ( !timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[0]) )
9665 		return 0;
9666 
9667 	// do timestamp stuff for next firing time
9668 	shipp->weapons.next_primary_fire_stamp[0] = timestamp(250);
9669 	shipp->weapons.last_primary_fire_stamp[0] = timestamp();
9670 
9671 	//	Debug code!  Make the single laser fire only one bolt and from the object center!
9672 	for (i=0; i<MAX_WEAPON_TYPES; i++)
9673 		if (!stricmp(Weapon_info[i].name, NOX("Debug Laser")))
9674 			break;
9675 
9676 	vm_vec_add(&wpos, &objp->pos, &(objp->orient.vec.fvec) );
9677 	if (i != MAX_WEAPONS) {
9678 		int weapon_objnum;
9679 		weapon_objnum = weapon_create( &wpos, &objp->orient, i, OBJ_INDEX(objp) );
9680 		weapon_set_tracking_info(weapon_objnum, OBJ_INDEX(objp), Ai_info[shipp->ai_index].target_objnum);
9681 		return 1;
9682 	} else
9683 		return 0;
9684 }
9685 #endif
9686 
9687 /**
9688  * Launch countermeasures from object *objp.
9689  *
9690  * @param objp object from which to launch countermeasure
9691  * @param rand_val is used in multiplayer to ensure that all clients in the game fire countermeasure the same way
9692  */
ship_launch_countermeasure(object * objp,int rand_val)9693 int ship_launch_countermeasure(object *objp, int rand_val)
9694 {
9695 	if(!Countermeasures_enabled) {
9696 		return 0;
9697 	}
9698 
9699 	int	check_count, cmeasure_count;
9700 	int cobjnum=-1;
9701 	vec3d	pos;
9702 	ship	*shipp;
9703 	ship_info *sip;
9704 
9705 	shipp = &Ships[objp->instance];
9706 	sip = &Ship_info[shipp->ship_info_index];
9707 
9708 	int arand;
9709 	if(rand_val < 0) {
9710 		arand = myrand();
9711 	} else {
9712 		arand = rand_val;
9713 	}
9714 
9715 	// in the case where the server is an observer, he can launch countermeasures unless we do this.
9716 	if( objp->type == OBJ_OBSERVER){
9717 		return 0;
9718 	}
9719 
9720 	if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) ){
9721 		return 0;
9722 	}
9723 
9724 	shipp->cmeasure_fire_stamp = timestamp(CMEASURE_WAIT);	//	Can launch every half second.
9725 #ifndef NDEBUG
9726 	if (Weapon_energy_cheat) {
9727 		shipp->cmeasure_count++;
9728 	}
9729 #endif
9730 
9731 	// we might check the count of countermeasures left depending on game state.  Multiplayer clients
9732 	// do not need to check any objects other than themselves for the count
9733 	check_count = 1;
9734 
9735 	if ( MULTIPLAYER_CLIENT && (objp != Player_obj) ){
9736 		check_count = 0;
9737 	}
9738 
9739 	if ( check_count && ((shipp->cmeasure_count <= 0) || (sip->cmeasure_type < 0)) ) {
9740 		if ( objp == Player_obj ) {
9741 			if(sip->cmeasure_max < 1 || sip->cmeasure_type < 0) {
9742 				//TODO: multi-lingual support
9743 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Not equipped with countermeasures", 1633));
9744 			} else if(shipp->current_cmeasure < 0) {
9745 				//TODO: multi-lingual support
9746 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No countermeasures selected", 1634));
9747 			} else if(shipp->cmeasure_count <= 0) {
9748 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No more countermeasure charges.", 485));
9749 			}
9750 			snd_play( &Snds[ship_get_sound(Player_obj, SND_OUT_OF_MISSLES)], 0.0f );
9751 		}
9752 
9753 		// if we have a player ship, then send the fired packet anyway so that the player
9754 		// who fired will get his 'out of countermeasures' sound
9755 		cmeasure_count = 0;
9756 		if ( objp->flags & OF_PLAYER_SHIP ){
9757 			goto send_countermeasure_fired;
9758 		}
9759 
9760 		return 0;
9761 	}
9762 
9763 	cmeasure_count = shipp->cmeasure_count;
9764 	shipp->cmeasure_count--;
9765 
9766 	vm_vec_scale_add(&pos, &objp->pos, &objp->orient.vec.fvec, -objp->radius/2.0f);
9767 
9768 	cobjnum = weapon_create(&pos, &objp->orient, shipp->current_cmeasure, OBJ_INDEX(objp));
9769 	if (cobjnum >= 0)
9770 	{
9771 		cmeasure_set_ship_launch_vel(&Objects[cobjnum], objp, arand);
9772 		nprintf(("Network", "Cmeasure created by %s\n", shipp->ship_name));
9773 
9774 		// Play sound effect for counter measure launch
9775 		Assert(shipp->current_cmeasure < Num_weapon_types);
9776 		if ( Weapon_info[shipp->current_cmeasure].launch_snd >= 0 ) {
9777 			snd_play_3d( &Snds[Weapon_info[shipp->current_cmeasure].launch_snd], &pos, &View_position );
9778 		}
9779 
9780 send_countermeasure_fired:
9781 		// the new way of doing things
9782 		if(Game_mode & GM_MULTIPLAYER){
9783 			send_NEW_countermeasure_fired_packet( objp, cmeasure_count, /*arand*/Objects[cobjnum].net_signature );
9784 		}
9785 	}
9786 
9787 	return (cobjnum >= 0);		// return 0 if not fired, 1 otherwise
9788 }
9789 
9790 /**
9791  * See if enough time has elapsed to play fail sound again
9792  */
ship_maybe_play_primary_fail_sound()9793 void ship_maybe_play_primary_fail_sound()
9794 {
9795 	ship_weapon *swp = &Player_ship->weapons;
9796 	int stampval;
9797 
9798 	hud_start_flash_weapon(swp->current_primary_bank);
9799 
9800 	if ( timestamp_elapsed(Laser_energy_out_snd_timer) )
9801 	{
9802 		// check timestamp according to ballistics
9803 		if (Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags2 & WIF2_BALLISTIC)
9804 		{
9805 			stampval = 500;
9806 		}
9807 		else
9808 		{
9809 			stampval = 50;
9810 		}
9811 		Laser_energy_out_snd_timer = timestamp(stampval);
9812 		snd_play( &Snds[ship_get_sound(Player_obj, SND_OUT_OF_WEAPON_ENERGY)]);
9813 	}
9814 }
9815 
9816 /**
9817  * See if enough time has elapsed to play secondary fail sound again
9818  */
ship_maybe_play_secondary_fail_sound(weapon_info * wip)9819 int ship_maybe_play_secondary_fail_sound(weapon_info *wip)
9820 {
9821 	hud_start_flash_weapon(Player_ship->weapons.num_primary_banks + Player_ship->weapons.current_secondary_bank);
9822 
9823 	if ( timestamp_elapsed(Missile_out_snd_timer) ) {
9824 
9825 		if ( wip->wi_flags & WIF_SWARM ) {
9826 			Missile_out_snd_timer = timestamp(500);
9827 		} else {
9828 			Missile_out_snd_timer = timestamp(50);
9829 		}
9830 		snd_play( &Snds[ship_get_sound(Player_obj, SND_OUT_OF_MISSLES)] );
9831 		return 1;
9832 	}
9833 	return 0;
9834 }
9835 
9836 /**
9837  * See if weapon for ship can fire based on weapons subystem strength.
9838  *
9839  * @return 1 if weapon failed to fire, 0 if weapon can fire
9840  */
ship_weapon_maybe_fail(ship * sp)9841 int ship_weapon_maybe_fail(ship *sp)
9842 {
9843 	int	rval;
9844 	float	weapons_subsys_str;
9845 
9846 	// If playing on lowest skill level, weapons will not fail due to subsystem damage
9847 	if ( Game_skill_level == 0 ){
9848 		return 0;
9849 	}
9850 
9851 	rval = 0;
9852 	weapons_subsys_str = ship_get_subsystem_strength( sp, SUBSYSTEM_WEAPONS );
9853 	if ( weapons_subsys_str < SUBSYS_WEAPONS_STR_FIRE_FAIL ) {
9854 		rval = 1;
9855 	}
9856 	else if ( weapons_subsys_str < SUBSYS_WEAPONS_STR_FIRE_OK ) {
9857 		// chance to fire depends on weapons subsystem strength
9858 		if ( (frand()-0.2f) > weapons_subsys_str )
9859 			rval = 1;
9860 	}
9861 
9862 	if (!rval) {
9863 		// is subsystem disrupted?
9864 		if ( ship_subsys_disrupted(sp, SUBSYSTEM_WEAPONS) ) {
9865 			rval=1;
9866 		}
9867 	}
9868 
9869 	return rval;
9870 }
9871 
9872 // create a moving tracer based upon a weapon which just fired
9873 float t_rad = 0.5f;
9874 float t_len = 10.0f;
9875 float t_vel = 0.2f;
9876 float t_min = 150.0f;
9877 float t_max = 300.0f;
9878 DCF(t_rad, "")
9879 {
9880 	dc_get_arg(ARG_FLOAT);
9881 	t_rad = Dc_arg_float;
9882 }
9883 DCF(t_len, "")
9884 {
9885 	dc_get_arg(ARG_FLOAT);
9886 	t_len = Dc_arg_float;
9887 }
9888 DCF(t_vel, "")
9889 {
9890 	dc_get_arg(ARG_FLOAT);
9891 	t_vel = Dc_arg_float;
9892 }
9893 DCF(t_min, "")
9894 {
9895 	dc_get_arg(ARG_FLOAT);
9896 	t_min = Dc_arg_float;
9897 }
9898 DCF(t_max, "")
9899 {
9900 	dc_get_arg(ARG_FLOAT);
9901 	t_max = Dc_arg_float;
9902 }
ship_fire_tracer(int weapon_objnum)9903 void ship_fire_tracer(int weapon_objnum)
9904 {
9905 	particle_info pinfo;
9906 	object *objp = &Objects[weapon_objnum];
9907 	weapon_info *wip = &Weapon_info[Weapons[Objects[weapon_objnum].instance].weapon_info_index];
9908 
9909 	// setup particle info
9910 	memset(&pinfo, 0, sizeof(particle_info));
9911 	pinfo.pos = objp->pos;
9912 	pinfo.vel = objp->phys_info.vel;
9913 	vm_vec_scale(&pinfo.vel, t_vel);
9914 	pinfo.lifetime = wip->lifetime;
9915 	pinfo.rad = t_rad;
9916 	pinfo.type = PARTICLE_BITMAP;
9917 	pinfo.optional_data = wip->laser_bitmap.first_frame;
9918 	pinfo.tracer_length = t_len;
9919 	pinfo.reverse = 0;
9920 	pinfo.attached_objnum = -1;
9921 	pinfo.attached_sig = 0;
9922 
9923 	// create the particle
9924 	particle_create(&pinfo);
9925 }
9926 
9927 /**
9928  * Stops a single primary bank
9929  */
ship_stop_fire_primary_bank(object * obj,int bank_to_stop)9930 int ship_stop_fire_primary_bank(object * obj, int bank_to_stop)
9931 {
9932 	ship			*shipp;
9933 	ship_weapon	*swp;
9934 
9935 	if(obj == NULL){
9936 		return 0;
9937 	}
9938 
9939 	if(obj->type != OBJ_SHIP){
9940 		return 0;
9941 	}
9942 
9943 	shipp = &Ships[obj->instance];
9944 	swp = &shipp->weapons;
9945 
9946 	if(Ship_info[shipp->ship_info_index].draw_primary_models[bank_to_stop]){
9947 		if(shipp->primary_rotate_rate[bank_to_stop] > 0.0f)
9948 			shipp->primary_rotate_rate[bank_to_stop] -= Weapon_info[swp->primary_bank_weapons[bank_to_stop]].weapon_submodel_rotate_accell*flFrametime;
9949 		if(shipp->primary_rotate_rate[bank_to_stop] < 0.0f)shipp->primary_rotate_rate[bank_to_stop] = 0.0f;
9950 		shipp->primary_rotate_ang[bank_to_stop] += shipp->primary_rotate_rate[bank_to_stop]*flFrametime;
9951 		if(shipp->primary_rotate_ang[bank_to_stop] > PI2)shipp->primary_rotate_ang[bank_to_stop] -= PI2;
9952 		if(shipp->primary_rotate_ang[bank_to_stop] < 0.0f)shipp->primary_rotate_ang[bank_to_stop] += PI2;
9953 	}
9954 
9955 	if(shipp->was_firing_last_frame[bank_to_stop] == 0)
9956 		return 0;
9957 
9958 	shipp->was_firing_last_frame[bank_to_stop] = 0;
9959 
9960 	return 1;
9961 }
9962 
9963 
9964 /**
9965  * Stuff to do when the ship has stoped fireing all primary weapons
9966  */
ship_stop_fire_primary(object * obj)9967 int ship_stop_fire_primary(object * obj)
9968 {
9969 	int i, num_primary_banks = 0, bank_to_stop = 0;
9970 	ship			*shipp;
9971 	ship_weapon	*swp;
9972 
9973 	if(obj == NULL){
9974 		return 0;
9975 	}
9976 
9977 	if(obj->type != OBJ_SHIP){
9978 		return 0;
9979 	}
9980 
9981 	shipp = &Ships[obj->instance];
9982 	swp = &shipp->weapons;
9983 
9984 	bank_to_stop = swp->current_primary_bank;
9985 
9986 	if ( shipp->flags & SF_PRIMARY_LINKED ) {
9987 		num_primary_banks = swp->num_primary_banks;
9988 	} else {
9989 		num_primary_banks = MIN(1, swp->num_primary_banks);
9990 	}
9991 
9992 	for ( i = 0; i < num_primary_banks; i++ ) {
9993 		// Goober5000 - allow more than two banks
9994 		bank_to_stop = (swp->current_primary_bank+i) % swp->num_primary_banks;
9995 		//only stop if it was fireing last frame
9996 		ship_stop_fire_primary_bank(obj, bank_to_stop);
9997 	}
9998 	for(i = 0; i<swp->num_primary_banks+num_primary_banks;i++)
9999 		ship_stop_fire_primary_bank(obj, i%swp->num_primary_banks);
10000 
10001 	return 1;
10002 }
10003 
10004 
10005 
10006 int tracers[MAX_SHIPS][4][4];
10007 
10008 float ship_get_subsystem_strength( ship *shipp, int type );
10009 
10010 // fires a primary weapon for the given object.  It also handles multiplayer cases.
10011 // in multiplayer, the starting network signature, and number of banks fired are sent
10012 // to all the clients in the game. All the info is passed to send_primary at the end of
10013 // the function.  The check_energy parameter (defaults to 1) tells us whether or not
10014 // we should check the energy.  It will be 0 when a multiplayer client is firing an AI
10015 // primary.
ship_fire_primary(object * obj,int stream_weapons,int force)10016 int ship_fire_primary(object * obj, int stream_weapons, int force)
10017 {
10018 	vec3d		gun_point, pnt, firing_pos, target_position, target_velocity_vec;
10019 	int			n = obj->instance;
10020 	ship			*shipp;
10021 	ship_weapon	*swp;
10022 	ship_info	*sip;
10023 	ai_info		*aip;
10024 	int			weapon, i, j, w, v, weapon_objnum;
10025 	int			bank_to_fire, num_fired = 0;
10026 	int			banks_fired;				// used for multiplayer to help determine whether or not to send packet
10027 	banks_fired = 0;			// used in multiplayer -- bitfield of banks that were fired
10028 	bool has_fired = false;		// used to determine whether we should fire the scripting hook
10029 	bool has_autoaim, has_converging_autoaim, needs_target_pos;	// used to flag weapon/ship as having autoaim
10030 	float autoaim_fov = 0;			// autoaim limit
10031 	float dist_to_target = 0;		// distance to target, for autoaim & automatic convergence
10032 
10033 	int			sound_played;	// used to track what sound is played.  If the player is firing two banks
10034 										// of the same laser, we only want to play one sound
10035 	Assert( obj != NULL );
10036 
10037 	if(obj == NULL){
10038 		return 0;
10039 	}
10040 
10041 	// in the case where the server is an observer, he can fire (which) would be bad - unless we do this.
10042 	if( obj->type == OBJ_OBSERVER){
10043 		return 0;
10044 	}
10045 
10046 	Assert( obj->type == OBJ_SHIP );
10047 	Assert( n >= 0 );
10048 	Assert( Ships[n].objnum == OBJ_INDEX(obj));
10049 	if((obj->type != OBJ_SHIP) || (n < 0) || (n >= MAX_SHIPS) || (Ships[n].objnum != OBJ_INDEX(obj))){
10050 		return 0;
10051 	}
10052 
10053 	shipp = &Ships[n];
10054 	swp = &shipp->weapons;
10055 
10056 	// bogus
10057 	if((shipp->ship_info_index < 0) || (shipp->ship_info_index >= Num_ship_classes)){
10058 		return 0;
10059 	}
10060 	if((shipp->ai_index < 0) || (shipp->ai_index >= MAX_AI_INFO)){
10061 		return 0;
10062 	}
10063 	sip = &Ship_info[shipp->ship_info_index];
10064 	aip = &Ai_info[shipp->ai_index];
10065 
10066 	if ( swp->num_primary_banks <= 0 ) {
10067 		return 0;
10068 	}
10069 
10070 	if ( swp->current_primary_bank < 0 ){
10071 		return 0;
10072 	}
10073 
10074 	// If the primaries have been locked, bail
10075 	if (shipp->flags2 & SF2_PRIMARIES_LOCKED)
10076 	{
10077 		return 0;
10078 	}
10079 
10080 	sound_played = -1;
10081 
10082 	// Fire the correct primary bank.  If primaries are linked (SF_PRIMARY_LINKED set), then fire
10083 	// both primary banks.
10084 	int	num_primary_banks;
10085 
10086 	if ( shipp->flags & SF_PRIMARY_LINKED ) {
10087 		num_primary_banks = swp->num_primary_banks;
10088 	} else {
10089 		num_primary_banks = MIN(1, swp->num_primary_banks);
10090 	}
10091 
10092 	Assert(num_primary_banks > 0);
10093 	if (num_primary_banks < 1){
10094 		return 0;
10095 	}
10096 
10097 	// if we're firing stream weapons, but the trigger is not down, do nothing
10098 	if(stream_weapons && !(shipp->flags & SF_TRIGGER_DOWN)){
10099 		return 0;
10100 	}
10101 
10102 	if(num_primary_banks == 1)
10103 		for(i = 0; i<swp->num_primary_banks; i++){
10104 			if(i!=swp->current_primary_bank)ship_stop_fire_primary_bank(obj, i);
10105 		}
10106 
10107 	// lets start gun convergence / autoaim code from here - Wanderer
10108 	has_converging_autoaim = ((sip->aiming_flags & AIM_FLAG_AUTOAIM_CONVERGENCE || (The_mission.ai_profile->player_autoaim_fov[Game_skill_level] > 0.0f && !( Game_mode & GM_MULTIPLAYER ))) && aip->target_objnum != -1);
10109 	has_autoaim = ((has_converging_autoaim || (sip->aiming_flags & AIM_FLAG_AUTOAIM)) && aip->target_objnum != -1);
10110 	needs_target_pos = ((has_autoaim || (sip->aiming_flags & AIM_FLAG_AUTO_CONVERGENCE)) && aip->target_objnum != -1);
10111 
10112 	if (needs_target_pos) {
10113 		if (has_autoaim) {
10114 			autoaim_fov = MAX(shipp->autoaim_fov, The_mission.ai_profile->player_autoaim_fov[Game_skill_level]);
10115 		}
10116 
10117 		// If a subsystem is targeted, fire in that direction instead
10118 		if (aip->targeted_subsys != NULL)
10119 		{
10120 			get_subsystem_world_pos(&Objects[aip->target_objnum], aip->targeted_subsys, &target_position);
10121 		}
10122 		else
10123 		{
10124 			target_position = Objects[aip->target_objnum].pos;
10125 		}
10126 
10127 		dist_to_target = vm_vec_dist_quick(&target_position, &obj->pos);
10128 	}
10129 
10130 	for ( i = 0; i < num_primary_banks; i++ ) {
10131 		// Goober5000 - allow more than two banks
10132 		bank_to_fire = (swp->current_primary_bank+i) % swp->num_primary_banks;
10133 
10134 
10135 		weapon = swp->primary_bank_weapons[bank_to_fire];
10136 		Assert( weapon >= 0 && weapon < MAX_WEAPON_TYPES );
10137 		if ( (weapon < 0) || (weapon >= MAX_WEAPON_TYPES) ) {
10138 			Int3();		// why would a ship try to fire a weapon that doesn't exist?
10139 			continue;
10140 		}
10141 
10142 		if (swp->primary_animation_position[bank_to_fire] == MA_POS_SET) {
10143 			if ( timestamp_elapsed(swp->primary_animation_done_time[bank_to_fire]) )
10144 				swp->primary_animation_position[bank_to_fire] = MA_POS_READY;
10145 			else
10146 				continue;
10147 		}
10148 
10149 		weapon_info* winfo_p = &Weapon_info[weapon];
10150 
10151 		if (needs_target_pos) {
10152 			target_velocity_vec = Objects[aip->target_objnum].phys_info.vel;
10153 			if (The_mission.ai_profile->flags & AIPF_USE_ADDITIVE_WEAPON_VELOCITY)
10154 				vm_vec_scale_sub2(&target_velocity_vec, &obj->phys_info.vel, winfo_p->vel_inherit_amount);
10155 		}
10156 
10157 		if(sip->draw_primary_models[bank_to_fire]){
10158 			if(shipp->primary_rotate_rate[bank_to_fire] < winfo_p->weapon_submodel_rotate_vel)
10159 				shipp->primary_rotate_rate[bank_to_fire] += winfo_p->weapon_submodel_rotate_accell*flFrametime;
10160 			if(shipp->primary_rotate_rate[bank_to_fire] > winfo_p->weapon_submodel_rotate_vel)shipp->primary_rotate_rate[bank_to_fire] = winfo_p->weapon_submodel_rotate_vel;
10161 			shipp->primary_rotate_ang[bank_to_fire] += shipp->primary_rotate_rate[bank_to_fire]*flFrametime;
10162 			if(shipp->primary_rotate_ang[bank_to_fire] > PI2)shipp->primary_rotate_ang[bank_to_fire] -= PI2;
10163 			if(shipp->primary_rotate_ang[bank_to_fire] < 0.0f)shipp->primary_rotate_ang[bank_to_fire] += PI2;
10164 
10165 			if(shipp->primary_rotate_rate[bank_to_fire] < winfo_p->weapon_submodel_rotate_vel)continue;
10166 		}
10167 		// if this is a targeting laser, start it up   ///- only targeting laser if it is tag-c, otherwise it's a fighter beam -Bobboau
10168 		if((winfo_p->wi_flags & WIF_BEAM) && (winfo_p->tag_level == 3) && (shipp->flags & SF_TRIGGER_DOWN) && (winfo_p->b_info.beam_type == BEAM_TYPE_C) ){
10169 			ship_start_targeting_laser(shipp);
10170 			continue;
10171 		}
10172 
10173 		// if we're firing stream weapons and this is a non stream weapon, skip it
10174 		if(stream_weapons && !(winfo_p->wi_flags & WIF_STREAM)){
10175 			continue;
10176 		}
10177 		// if we're firing non stream weapons and this is a stream weapon, skip it
10178 		if(!stream_weapons && (winfo_p->wi_flags & WIF_STREAM)){
10179 			continue;
10180 		}
10181 
10182 		// only non-multiplayer clients (single, multi-host) need to do timestamp checking
10183 		if ( !timestamp_elapsed(swp->next_primary_fire_stamp[bank_to_fire]) ) {
10184 			continue;
10185 		}
10186 
10187 		// if weapons are linked and this is a nolink weapon, skip it
10188 		if (shipp->flags & SF_PRIMARY_LINKED && winfo_p->wi_flags3 & WIF3_NOLINK) {
10189 			continue;
10190 		}
10191 
10192 		// do timestamp stuff for next firing time
10193 		float next_fire_delay;
10194 		bool fast_firing = false;
10195 		if (winfo_p->burst_shots > swp->burst_counter[bank_to_fire]) {
10196 			next_fire_delay = (float) winfo_p->burst_delay * 1000.0f;
10197 			swp->burst_counter[bank_to_fire]++;
10198 			if (winfo_p->burst_flags & WBF_FAST_FIRING)
10199 				fast_firing = true;
10200 		} else {
10201 			next_fire_delay	= (float) winfo_p->fire_wait * 1000.0f;
10202 			swp->burst_counter[bank_to_fire] = 0;
10203 		}
10204 		if (!((obj->flags & OF_PLAYER_SHIP) || (fast_firing))) {
10205 			if (shipp->team == Ships[Player_obj->instance].team){
10206 				next_fire_delay *= aip->ai_ship_fire_delay_scale_friendly;
10207 			} else {
10208 				next_fire_delay *= aip->ai_ship_fire_delay_scale_hostile;
10209 			}
10210 		}
10211 
10212 		polymodel *pm = model_get( sip->model_num );
10213 
10214 		// Goober5000 (thanks to _argv[-1] for the original idea)
10215 		if ( !((winfo_p->wi_flags3 & WIF3_NO_LINKED_PENALTY) || (The_mission.ai_profile->flags & AIPF_DISABLE_LINKED_FIRE_PENALTY)) )
10216 		{
10217 			int effective_primary_banks = 0;
10218 			for (int it = 0; it < num_primary_banks; it++)
10219 				if (Weapon_info[swp->primary_bank_weapons[it]].wi_flags3 & (WIF3_NOLINK | WIF3_NO_LINKED_PENALTY))
10220 					continue;
10221 				else
10222 					effective_primary_banks++;
10223 
10224 			next_fire_delay *= 1.0f + (effective_primary_banks - 1) * 0.5f;		//	50% time penalty if banks linked
10225 		}
10226 
10227 		if (winfo_p->fof_spread_rate > 0.0f)
10228 		{
10229 			//Adjust the primary_bank_fof_cooldown based on how long it's been since the last shot.
10230 			float reset_amount = (timestamp_until(swp->last_primary_fire_stamp[bank_to_fire]) / 1000.0f) * winfo_p->fof_reset_rate;
10231 			swp->primary_bank_fof_cooldown[bank_to_fire] += winfo_p->fof_spread_rate + reset_amount;
10232 			CLAMP(swp->primary_bank_fof_cooldown[bank_to_fire], 0.0f, 1.0f);
10233 		}
10234 
10235 		//	MK, 2/4/98: Since you probably were allowed to fire earlier, but couldn't fire until your frame interval
10236 		//	rolled around, subtract out up to half the previous frametime.
10237 		//	Note, unless we track whether the fire button has been held down, and not tapped, it's hard to
10238 		//	know how much time to subtract off.  It could be this fire is "late" because the user didn't want to fire.
10239 		if ((next_fire_delay > 0.0f)) {
10240 			if (obj->flags & OF_PLAYER_SHIP) {
10241 				int	t = timestamp_until(swp->next_primary_fire_stamp[bank_to_fire]);
10242 				if (t < 0) {
10243 					float	tx;
10244 
10245 					tx = (float) t/-1000.0f;
10246 					if (tx > flFrametime/2.0f){
10247 						tx = 1000.0f * flFrametime * 0.7f;
10248 					}
10249 					next_fire_delay -= tx;
10250 				}
10251 
10252 				if ((int) next_fire_delay < 1){
10253 					next_fire_delay = 1.0f;
10254 				}
10255 			}
10256 
10257 			swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay));
10258 			swp->last_primary_fire_stamp[bank_to_fire] = timestamp();
10259 		}
10260 
10261 		if (sip->flags2 & SIF2_DYN_PRIMARY_LINKING ) {
10262 			Assert(pm->gun_banks[bank_to_fire].num_slots != 0);
10263 			swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay * ( swp->primary_bank_slot_count[ bank_to_fire ] ) / pm->gun_banks[bank_to_fire].num_slots ) );
10264 			swp->last_primary_fire_stamp[bank_to_fire] = timestamp();
10265 		} else if (winfo_p->wi_flags2 & WIF2_CYCLE) {
10266 			Assert(pm->gun_banks[bank_to_fire].num_slots != 0);
10267 			swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay / pm->gun_banks[bank_to_fire].num_slots));
10268 			swp->last_primary_fire_stamp[bank_to_fire] = timestamp();
10269 			//to maintain balance of fighters with more fire points they will fire faster than ships with fewer points
10270 		}else{
10271 			swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay));
10272 			swp->last_primary_fire_stamp[bank_to_fire] = timestamp();
10273 		}
10274 		// Here is where we check if weapons subsystem is capable of firing the weapon.
10275 		// Note that we can have partial bank firing, if the weapons subsystem is partially
10276 		// functional, which should be cool.
10277 		if ( ship_weapon_maybe_fail(shipp) && !force) {
10278 			if ( obj == Player_obj ) {
10279 				ship_maybe_play_primary_fail_sound();
10280 			}
10281 			ship_stop_fire_primary_bank(obj, bank_to_fire);
10282 			continue;
10283 		}
10284 
10285 
10286 		if ( pm->n_guns > 0 ) {
10287 			int num_slots = pm->gun_banks[bank_to_fire].num_slots;
10288 			vec3d predicted_target_pos, plr_to_target_vec;
10289 			vec3d player_forward_vec = obj->orient.vec.fvec;
10290 			bool in_automatic_aim_fov = false;
10291 			float dist_to_aim = 0;
10292 
10293 			// more autoaim stuff here - Wanderer
10294 			// needs weapon speed
10295 			if (needs_target_pos) {
10296 				float time_to_target, angle_to_target;
10297 				vec3d last_delta_vec;
10298 
10299 				time_to_target = 0.0f;
10300 
10301 				if (winfo_p->max_speed != 0)
10302 				{
10303 					time_to_target = dist_to_target / winfo_p->max_speed;
10304 				}
10305 
10306 				vm_vec_scale_add(&predicted_target_pos, &target_position, &target_velocity_vec, time_to_target);
10307 				polish_predicted_target_pos(winfo_p, &Objects[aip->target_objnum], &target_position, &predicted_target_pos, dist_to_target, &last_delta_vec, 1);
10308 				vm_vec_sub(&plr_to_target_vec, &predicted_target_pos, &obj->pos);
10309 
10310 				if (has_autoaim) {
10311 					angle_to_target = vm_vec_delta_ang(&player_forward_vec, &plr_to_target_vec, NULL);
10312 					if (angle_to_target < autoaim_fov)
10313 						in_automatic_aim_fov = true;
10314 				}
10315 
10316 				dist_to_aim = vm_vec_mag_quick(&plr_to_target_vec);
10317 
10318 				// minimum convergence distance
10319 				if (sip->minimum_convergence_distance > dist_to_aim) {
10320 					float dist_mult;
10321 					dist_mult = sip->minimum_convergence_distance / dist_to_aim;
10322 					vm_vec_scale_add(&predicted_target_pos, &obj->pos, &plr_to_target_vec, dist_mult);
10323 				}
10324 			}
10325 
10326 			if(winfo_p->wi_flags & WIF_BEAM){		// the big change I made for fighter beams, if there beams fill out the Fire_Info for a targeting laser then fire it, for each point in the weapon bank -Bobboau
10327 				float t;
10328 				if (winfo_p->burst_shots > swp->burst_counter[bank_to_fire]) {
10329 					t = winfo_p->burst_delay;
10330 					swp->burst_counter[bank_to_fire]++;
10331 				} else {
10332 					t = winfo_p->fire_wait;//doing that time scale thing on enemy fighter is just ugly with beams, especaly ones that have careful timeing
10333 					swp->burst_counter[bank_to_fire] = 0;
10334 				}
10335 				swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int) (t * 1000.0f));
10336 				swp->last_primary_fire_stamp[bank_to_fire] = timestamp();
10337 				beam_fire_info fbfire_info;
10338 
10339 				int points;
10340 				if (winfo_p->b_info.beam_shots){
10341 					if (winfo_p->b_info.beam_shots > num_slots){
10342 						points = num_slots;
10343 					}else{
10344 						points = winfo_p->b_info.beam_shots;
10345 					}
10346 				}else{
10347 					points = num_slots;
10348 				}
10349 
10350 				if ( shipp->weapon_energy < points*winfo_p->energy_consumed*flFrametime)
10351 				{
10352 					swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay));
10353 					if ( obj == Player_obj )
10354 					{
10355 						ship_maybe_play_primary_fail_sound();
10356 					}
10357 					ship_stop_fire_primary_bank(obj, bank_to_fire);
10358 					continue;
10359 				}
10360 
10361 				shipp->beam_sys_info.turret_norm.xyz.x = 0.0f;
10362 				shipp->beam_sys_info.turret_norm.xyz.y = 0.0f;
10363 				shipp->beam_sys_info.turret_norm.xyz.z = 1.0f;
10364 				shipp->beam_sys_info.model_num = sip->model_num;
10365 				shipp->beam_sys_info.turret_gun_sobj = pm->detail[0];
10366 				shipp->beam_sys_info.turret_num_firing_points = 1;
10367 				shipp->beam_sys_info.turret_fov = (float)cos((winfo_p->field_of_fire != 0.0f)?winfo_p->field_of_fire:180);
10368 
10369 				shipp->fighter_beam_turret_data.disruption_timestamp = timestamp(0);
10370 				shipp->fighter_beam_turret_data.turret_next_fire_pos = 0;
10371 				shipp->fighter_beam_turret_data.current_hits = 1.0;
10372 				shipp->fighter_beam_turret_data.system_info = &shipp->beam_sys_info;
10373 
10374 				fbfire_info.target_subsys = Ai_info[shipp->ai_index].targeted_subsys;
10375 				fbfire_info.beam_info_index = shipp->weapons.primary_bank_weapons[bank_to_fire];
10376 				fbfire_info.beam_info_override = NULL;
10377 				fbfire_info.shooter = &Objects[shipp->objnum];
10378 
10379 				if (aip->target_objnum >= 0) {
10380 					fbfire_info.target = &Objects[aip->target_objnum];
10381 				} else {
10382 					fbfire_info.target = NULL;
10383 				}
10384 				fbfire_info.turret = &shipp->fighter_beam_turret_data;
10385 				fbfire_info.bfi_flags |= BFIF_IS_FIGHTER_BEAM;
10386 				fbfire_info.bank = bank_to_fire;
10387 
10388 				for ( v = 0; v < points; v++ ){
10389 					if(winfo_p->b_info.beam_shots){
10390 						j = (shipp->last_fired_point[bank_to_fire]+1)%num_slots;
10391 						shipp->last_fired_point[bank_to_fire] = j;
10392 					}else{
10393 						j=v;
10394 					}
10395 
10396 					fbfire_info.targeting_laser_offset = pm->gun_banks[bank_to_fire].pnt[j];
10397 					shipp->beam_sys_info.pnt = pm->gun_banks[bank_to_fire].pnt[j];
10398 					shipp->beam_sys_info.turret_firing_point[0] = pm->gun_banks[bank_to_fire].pnt[j];
10399 
10400 					fbfire_info.point = j;
10401 
10402 					beam_fire(&fbfire_info);
10403 					has_fired = true;
10404 					num_fired++;
10405 				}
10406 			}
10407 			else	//if this isn't a fighter beam, do it normally -Bobboau
10408 			{
10409 				int points = 0, numtimes = 1;
10410 
10411 				// ok if this is a cycling weapon use shots as the number of points to fire from at a time
10412 				// otherwise shots is the number of times all points will be fired (used mostly for the 'shotgun' effect)
10413 				if ( sip->flags2 & SIF2_DYN_PRIMARY_LINKING ) {
10414 					numtimes = 1;
10415 					points = MIN( num_slots, swp->primary_bank_slot_count[ bank_to_fire ] );
10416 				} else if ( winfo_p->wi_flags2 & WIF2_CYCLE ) {
10417 					numtimes = 1;
10418 					points = MIN(num_slots, winfo_p->shots);
10419 				} else {
10420 					numtimes = winfo_p->shots;
10421 					points = num_slots;
10422 				}
10423 
10424 				// The energy-consumption code executes even for ballistic primaries, because
10425 				// there may be a reason why you want to have ballistics consume energy.  Perhaps
10426 				// you can't fire too many too quickly or they'll overheat.  If not, just set
10427 				// the weapon's energy_consumed to 0 and it'll work just fine. - Goober5000
10428 
10429 				// fail unless we're forcing (energy based primaries)
10430 				if ( (shipp->weapon_energy < points*numtimes * winfo_p->energy_consumed)			//was num_slots
10431 				 && !force ) {
10432 
10433 					swp->next_primary_fire_stamp[bank_to_fire] = timestamp((int)(next_fire_delay));
10434 					if ( obj == Player_obj )
10435 					{
10436 						ship_maybe_play_primary_fail_sound();
10437 					}
10438 					ship_stop_fire_primary_bank(obj, bank_to_fire);
10439 					continue;
10440 				}
10441 				// moved the above to here to use points instead of num_slots for energy consumption check
10442 
10443 				// ballistics support for primaries - Goober5000
10444 				if ( winfo_p->wi_flags2 & WIF2_BALLISTIC )
10445 				{
10446 					// Make sure this ship is set up for ballistics.
10447 					// If you get this error, add the ballistic primaries tags to ships.tbl.
10448 					Assert ( sip->flags & SIF_BALLISTIC_PRIMARIES );
10449 
10450 					// If ship is being repaired/rearmed, it cannot fire ballistics
10451 					if ( aip->ai_flags & AIF_BEING_REPAIRED )
10452 					{
10453 						continue;
10454 					}
10455 
10456 					// duplicated from the secondaries firing routine...
10457 					// determine if there is enough ammo left to fire weapons on this bank.  As with primary
10458 					// weapons, we might or might not check ammo counts depending on game mode, who is firing,
10459 					// and if I am a client in multiplayer
10460 					int check_ammo = 1;
10461 
10462 					if ( MULTIPLAYER_CLIENT && (obj != Player_obj) )
10463 					{
10464 						check_ammo = 0;
10465 					}
10466 
10467 					// not enough ammo
10468 					if ( check_ammo && ( swp->primary_bank_ammo[bank_to_fire] <= 0) )
10469 					{
10470 						if ( obj == Player_obj )
10471 						{
10472 							ship_maybe_play_primary_fail_sound();
10473 						}
10474 						else
10475 						{
10476 							// TODO:  AI switch primary weapon / re-arm?
10477 						}
10478 						continue;
10479 					}
10480 
10481 					// deplete ammo
10482 					if ( Weapon_energy_cheat == 0 )
10483 					{
10484 						swp->primary_bank_ammo[bank_to_fire] -= points*numtimes;
10485 
10486 						// make sure we don't go below zero; any such error is excusable
10487 						// because it only happens when the bank is depleted in one shot
10488 						if (swp->primary_bank_ammo[bank_to_fire] < 0)
10489 						{
10490 							swp->primary_bank_ammo[bank_to_fire] = 0;
10491 						}
10492 					}
10493 				}
10494 
10495 				// now handle the energy as usual
10496 				// deplete the weapon reserve energy by the amount of energy used to fire the weapon
10497 				// Only subtract the energy amount required for equipment operation once
10498 				shipp->weapon_energy -= points*numtimes * winfo_p->energy_consumed;
10499 				// note for later: option for fuel!
10500 
10501 				// Mark all these weapons as in the same group
10502 				int new_group_id = weapon_create_group_id();
10503 
10504 				for ( w = 0; w < numtimes; w++ ) {
10505 					polymodel *weapon_model = NULL;
10506 					if(winfo_p->external_model_num >= 0)
10507 						weapon_model = model_get(winfo_p->external_model_num);
10508 
10509 					if (weapon_model)
10510 						if ((weapon_model->n_guns <= swp->external_model_fp_counter[bank_to_fire]) || (swp->external_model_fp_counter[bank_to_fire] < 0))
10511 							swp->external_model_fp_counter[bank_to_fire] = 0;
10512 
10513 					for ( j = 0; j < points; j++ ) {
10514 						int pt; //point
10515 						if ( (winfo_p->wi_flags2 & WIF2_CYCLE) || (sip->flags2 & SIF2_DYN_PRIMARY_LINKING) ){
10516 							pt = (shipp->last_fired_point[bank_to_fire]+1)%num_slots;
10517 						}else{
10518 							pt = j;
10519 						}
10520 
10521 						int sub_shots = 1;
10522 						// Use 0 instead of bank_to_fire as index when checking the number of external weapon model firingpoints
10523 						if (weapon_model && weapon_model->n_guns)
10524 							if (!(winfo_p->wi_flags2 & WIF2_EXTERNAL_WEAPON_FP))
10525 								sub_shots = weapon_model->gun_banks[0].num_slots;
10526 
10527 						for(int s = 0; s<sub_shots; s++){
10528 							pnt = pm->gun_banks[bank_to_fire].pnt[pt];
10529 							// Use 0 instead of bank_to_fire as index to external weapon model firingpoints
10530 							if (weapon_model && weapon_model->n_guns) {
10531 								if (winfo_p->wi_flags2 & WIF2_EXTERNAL_WEAPON_FP) {
10532 									vm_vec_add2(&pnt, &weapon_model->gun_banks[0].pnt[swp->external_model_fp_counter[bank_to_fire]]);
10533 								} else {
10534 									vm_vec_add2(&pnt, &weapon_model->gun_banks[0].pnt[s]);
10535 								}
10536 							}
10537 
10538 							vm_vec_unrotate(&gun_point, &pnt, &obj->orient);
10539 							vm_vec_add(&firing_pos, &gun_point, &obj->pos);
10540 
10541 							matrix firing_orient;
10542 
10543 							/*	I AIM autoaim convergence
10544 								II AIM autoaim
10545 								III AIM auto convergence
10546 								IV AIM std convergence
10547 								V SIF convergence
10548 								no convergence or autoaim
10549 							*/
10550 							if (has_autoaim && in_automatic_aim_fov) {
10551 								vec3d firing_vec;
10552 
10553 								if (has_converging_autoaim) {
10554 									// converging autoaim
10555 									vm_vec_sub(&firing_vec, &predicted_target_pos, &firing_pos);
10556 								} else {
10557 									// autoaim
10558 									vm_vec_sub(&firing_vec, &predicted_target_pos, &obj->pos);
10559 								}
10560 
10561 								vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
10562 							} else if ((sip->aiming_flags & AIM_FLAG_STD_CONVERGENCE) || ((sip->aiming_flags & AIM_FLAG_AUTO_CONVERGENCE) && (aip->target_objnum != -1))) {
10563 								// std & auto convergence
10564 								vec3d target_vec, firing_vec, convergence_offset;
10565 
10566 								// make sure vector is of the set length
10567 								vm_vec_copy_normalize(&target_vec, &player_forward_vec);
10568 								if ((sip->aiming_flags & AIM_FLAG_AUTO_CONVERGENCE) && (aip->target_objnum != -1)) {
10569 									// auto convergence
10570 									vm_vec_scale(&target_vec, dist_to_aim);
10571 								} else {
10572 									// std convergence
10573 									vm_vec_scale(&target_vec, sip->convergence_distance);
10574 								}
10575 
10576 								// if there is convergence offset then make use of it)
10577 								if (sip->aiming_flags & AIM_FLAG_CONVERGENCE_OFFSET) {
10578 									vm_vec_unrotate(&convergence_offset, &sip->convergence_offset, &obj->orient);
10579 									vm_vec_add2(&target_vec, &convergence_offset);
10580 								}
10581 
10582 								vm_vec_add2(&target_vec, &obj->pos);
10583 								vm_vec_sub(&firing_vec, &target_vec, &firing_pos);
10584 
10585 								// set orientation
10586 								vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
10587 							} else if (sip->aiming_flags & AIM_FLAG_STD_CONVERGENCE) {
10588 								// fixed distance convergence
10589 								vec3d target_vec, firing_vec, convergence_offset;
10590 
10591 								// make sure vector is of the set length
10592 								vm_vec_copy_normalize(&target_vec, &player_forward_vec);
10593 								vm_vec_scale(&target_vec, sip->convergence_distance);
10594 
10595 								// if there is convergence offset then make use of it)
10596 								if (sip->aiming_flags & AIM_FLAG_CONVERGENCE_OFFSET) {
10597 									vm_vec_unrotate(&convergence_offset, &sip->convergence_offset, &obj->orient);
10598 									vm_vec_add2(&target_vec, &convergence_offset);
10599 								}
10600 
10601 								vm_vec_add2(&target_vec, &obj->pos);
10602 								vm_vec_sub(&firing_vec, &target_vec, &firing_pos);
10603 
10604 								// set orientation
10605 								vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
10606 							} else if (sip->flags2 & SIF2_GUN_CONVERGENCE) {
10607 								// model file defined convergence
10608 								vec3d firing_vec;
10609 								vm_vec_unrotate(&firing_vec, &pm->gun_banks[bank_to_fire].norm[pt], &obj->orient);
10610 								vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
10611 							} else {
10612 								// no autoaim or convergence
10613 								firing_orient = obj->orient;
10614 							}
10615 
10616 							// create the weapon -- the network signature for multiplayer is created inside
10617 							// of weapon_create
10618 							weapon_objnum = weapon_create( &firing_pos, &firing_orient, weapon, OBJ_INDEX(obj), new_group_id,
10619 								0, 0, swp->primary_bank_fof_cooldown[bank_to_fire] );
10620 							winfo_p = &Weapon_info[Weapons[Objects[weapon_objnum].instance].weapon_info_index];
10621 							has_fired = true;
10622 
10623 							weapon_set_tracking_info(weapon_objnum, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);
10624 
10625 							if (winfo_p->wi_flags & WIF_FLAK)
10626 							{
10627 								object *target;
10628 								vec3d predicted_pos;
10629 								float flak_range=(winfo_p->lifetime)*(winfo_p->max_speed);
10630 								float range_to_target = flak_range;
10631 								float wepstr=ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10632 
10633 								if (aip->target_objnum != -1) {
10634 									target = &Objects[aip->target_objnum];
10635 								} else {
10636 									target = NULL;
10637 								}
10638 
10639 								if (target != NULL) {
10640 									set_predicted_enemy_pos(&predicted_pos, obj, &target->pos, &target->phys_info.vel, aip);
10641 									range_to_target=vm_vec_dist(&predicted_pos, &obj->pos);
10642 								}
10643 
10644 								//if we have a target and its in range
10645 								if ( (target != NULL) && (range_to_target < flak_range) )
10646 								{
10647 									//set flak range to range of ship
10648 									flak_pick_range(&Objects[weapon_objnum], &firing_pos, &predicted_pos,wepstr);
10649 								}
10650 								else
10651 								{
10652 									flak_set_range(&Objects[weapon_objnum], flak_range - winfo_p->untargeted_flak_range_penalty);
10653 								}
10654 
10655 								if ((winfo_p->muzzle_flash>=0) && (((shipp==Player_ship) && (vm_vec_mag(&Player_obj->phys_info.vel)>=45)) || (shipp!=Player_ship)))
10656 								{
10657 									flak_muzzle_flash(&firing_pos,&obj->orient.vec.fvec, &obj->phys_info, swp->primary_bank_weapons[bank_to_fire]);
10658 								}
10659 							}
10660 							// create the muzzle flash effect
10661 							if ( (obj != Player_obj) || (sip->flags2 & SIF2_SHOW_SHIP_MODEL) || (Viewer_mode) ) {
10662 								// show the flash only if in not cockpit view, or if "show ship" flag is set
10663 								shipfx_flash_create( obj, sip->model_num, &pnt, &obj->orient.vec.fvec, 1, weapon );
10664 							}
10665 
10666 							// maybe shudder the ship - if its me
10667 							if((winfo_p->wi_flags & WIF_SHUDDER) && (obj == Player_obj) && !(Game_mode & GM_STANDALONE_SERVER)){
10668 								// calculate some arbitrary value between 100
10669 								// (mass * velocity) / 10
10670 								game_shudder_apply(500, (winfo_p->mass * winfo_p->max_speed) * 0.1f);
10671 							}
10672 
10673 							num_fired++;
10674 							shipp->last_fired_point[bank_to_fire] = (shipp->last_fired_point[bank_to_fire] + 1) % num_slots;
10675 						}
10676 					}
10677 					swp->external_model_fp_counter[bank_to_fire]++;
10678 				}
10679 			}
10680 
10681 			CLAMP(shipp->weapon_energy, 0.0f, sip->max_weapon_reserve);
10682 
10683 			banks_fired |= (1<<bank_to_fire);				// mark this bank as fired.
10684 		}
10685 
10686 
10687 		// Only play the weapon fired sound if it hasn't been played yet.  This is to
10688 		// avoid playing the same sound multiple times when banks are linked with the
10689 		// same weapon.
10690 
10691 		if (!(winfo_p->wi_flags & WIF_BEAM)){	// not a beam weapon?
10692 			if ( sound_played != winfo_p->launch_snd ) {
10693 				sound_played = winfo_p->launch_snd;
10694 				if ( obj == Player_obj ) {
10695 					if ( winfo_p->launch_snd != -1 ) {
10696 						weapon_info *wip;
10697 						ship_weapon *sw_pl;
10698 
10699 						//Update the last timestamp until continous fire is over, so we have the timestamp of the cease-fire.
10700 						if (shipp->was_firing_last_frame[bank_to_fire] == 1) {
10701 							swp->last_primary_fire_sound_stamp[bank_to_fire] = timestamp();
10702 						}
10703 
10704 						//Check for pre-launch sound and play if relevant
10705 						if( (winfo_p->pre_launch_snd != -1)									//If this weapon type has a pre-fire sound
10706 							&& ((timestamp() - swp->last_primary_fire_sound_stamp[bank_to_fire]) >= winfo_p->pre_launch_snd_min_interval)	//and if we're past our minimum delay from the last cease-fire
10707 							&& (shipp->was_firing_last_frame[bank_to_fire] == 0)				//and if we are at the beginning of a firing stream
10708 						){
10709 							snd_play( &Snds[winfo_p->pre_launch_snd], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY); //play it
10710 						} else { //Otherwise, play normal firing sounds
10711 							// HACK
10712 							if(winfo_p->launch_snd == SND_AUTOCANNON_SHOT){
10713 								snd_play( &Snds[winfo_p->launch_snd], 0.0f, 1.0f, SND_PRIORITY_TRIPLE_INSTANCE );
10714 							} else {
10715 								snd_play( &Snds[winfo_p->launch_snd], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
10716 							}
10717 						}
10718 
10719 						sw_pl = &Player_ship->weapons;
10720 						if (sw_pl->current_primary_bank >= 0)
10721 						{
10722 							wip = &Weapon_info[sw_pl->primary_bank_weapons[sw_pl->current_primary_bank]];
10723 							int force_level = (int) ((wip->armor_factor + wip->shield_factor * 0.2f) * (wip->damage * wip->damage - 7.5f) * 0.45f + 0.6f) * 10 + 2000;
10724 
10725 							// modify force feedback for ballistics: make it stronger
10726 							if (wip->wi_flags2 & WIF2_BALLISTIC)
10727 								joy_ff_play_primary_shoot(force_level * 2);
10728 							// no ballistics
10729 							else
10730 								joy_ff_play_primary_shoot(force_level);
10731 						}
10732 					}
10733 				}else {
10734 					if ( winfo_p->launch_snd != -1 ) {
10735 						snd_play_3d( &Snds[winfo_p->launch_snd], &obj->pos, &View_position );
10736 					}
10737 				}
10738 			}
10739 		}
10740 
10741 		shipp->was_firing_last_frame[bank_to_fire] = 1;
10742 	}	// end for (go to next primary bank)
10743 
10744 	// if multiplayer and we're client-side firing, send the packet
10745 	if(Game_mode & GM_MULTIPLAYER){
10746 		// if i'm a client, and this is not me, don't send
10747 		if(!(MULTIPLAYER_CLIENT && (shipp != Player_ship))){
10748 			send_NEW_primary_fired_packet( shipp, banks_fired );
10749 		}
10750 	}
10751 
10752    // STATS
10753    if (obj->flags & OF_PLAYER_SHIP) {
10754 		// in multiplayer -- only the server needs to keep track of the stats.  Call the cool
10755 		// function to find the player given the object *.  It had better return a valid player
10756 		// or our internal structure as messed up.
10757 		if( Game_mode & GM_MULTIPLAYER ) {
10758 			if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
10759 				int player_num;
10760 
10761 				player_num = multi_find_player_by_object ( obj );
10762 				Assert ( player_num != -1 );
10763 
10764 				Net_players[player_num].m_player->stats.mp_shots_fired += num_fired;
10765 			}
10766 		} else {
10767 			Player->stats.mp_shots_fired += num_fired;
10768 		}
10769 	}
10770 
10771 	if (has_fired) {
10772 		object *objp = &Objects[shipp->objnum];
10773 		object* target;
10774 		if (Ai_info[shipp->ai_index].target_objnum != -1)
10775 			target = &Objects[Ai_info[shipp->ai_index].target_objnum];
10776 		else
10777 			target = NULL;
10778 		if (objp == Player_obj && Player_ai->target_objnum != -1)
10779 			target = &Objects[Player_ai->target_objnum];
10780 
10781 		Script_system.SetHookObjects(2, "User", objp, "Target", target);
10782 		Script_system.RunCondition(CHA_ONWPFIRED, 0, NULL, objp, 1);
10783 
10784 		Script_system.RunCondition(CHA_PRIMARYFIRE, 0, NULL, objp);
10785 	}
10786 
10787 	return num_fired;
10788 }
10789 
ship_start_targeting_laser(ship * shipp)10790 void ship_start_targeting_laser(ship *shipp)
10791 {
10792 	int bank0_laser = 0;
10793 	int bank1_laser = 0;
10794 
10795 	// determine if either of our banks have a targeting laser
10796 	if((shipp->weapons.primary_bank_weapons[0] >= 0) && (Weapon_info[shipp->weapons.primary_bank_weapons[0]].wi_flags & WIF_BEAM) && (Weapon_info[shipp->weapons.primary_bank_weapons[0]].b_info.beam_type == BEAM_TYPE_C)){
10797 		bank0_laser = 1;
10798 	}
10799 	if((shipp->weapons.primary_bank_weapons[1] >= 0) && (Weapon_info[shipp->weapons.primary_bank_weapons[1]].wi_flags & WIF_BEAM) && (Weapon_info[shipp->weapons.primary_bank_weapons[1]].b_info.beam_type == BEAM_TYPE_C)){
10800 		bank1_laser = 1;
10801 	}
10802 
10803 	// if primary banks are linked
10804 	if(shipp->flags & SF_PRIMARY_LINKED){
10805 		if(bank0_laser){
10806 			shipp->targeting_laser_bank = 0;
10807 			return;
10808 		}
10809 		if(bank1_laser){
10810 			shipp->targeting_laser_bank = 1;
10811 			return;
10812 		}
10813 	}
10814 	// if we only have 1 bank selected
10815 	else {
10816 		if(bank0_laser && (shipp->weapons.current_primary_bank == 0)){
10817 			shipp->targeting_laser_bank = 0;
10818 			return;
10819 		}
10820 		if(bank1_laser && (shipp->weapons.current_primary_bank == 1)){
10821 			shipp->targeting_laser_bank = 1;
10822 			return;
10823 		}
10824 	}
10825 }
10826 
ship_stop_targeting_laser(ship * shipp)10827 void ship_stop_targeting_laser(ship *shipp)
10828 {
10829 	shipp->targeting_laser_bank = -1;
10830 	shipp->targeting_laser_objnum = -1; // erase old laser obj num if it has any -Bobboau
10831 }
10832 
ship_process_targeting_lasers()10833 void ship_process_targeting_lasers()
10834 {
10835 	fighter_beam_fire_info fire_info;
10836 	ship_obj *so;
10837 	ship *shipp;
10838 	polymodel *m;
10839 
10840 	// interate over all ships
10841 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
10842 		// sanity checks
10843 		if(so->objnum < 0){
10844 			continue;
10845 		}
10846 		if(Objects[so->objnum].type != OBJ_SHIP){
10847 			continue;
10848 		}
10849 		if(Objects[so->objnum].instance < 0){
10850 			continue;
10851 		}
10852 		shipp = &Ships[Objects[so->objnum].instance];
10853 
10854 		// if our trigger is no longer down, switch it off
10855 		if(!(shipp->flags & SF_TRIGGER_DOWN)){
10856 			ship_stop_targeting_laser(shipp);
10857 			continue;
10858 		}
10859 
10860 		// if we have a bank to fire - fire it
10861 		if((shipp->targeting_laser_bank >= 0) && (shipp->targeting_laser_bank < 2)){
10862 			// try and get the model
10863 			m = model_get(Ship_info[shipp->ship_info_index].model_num);
10864 			if(m == NULL){
10865 				continue;
10866 			}
10867 
10868 			// fire a targeting laser
10869 			fire_info.life_left = 0.0;					//for fighter beams
10870 			fire_info.life_total = 0.0f;					//for fighter beams
10871 			fire_info.warmdown_stamp = -1;				//for fighter beams
10872 			fire_info.warmup_stamp = -1;				//for fighter beams
10873 			fire_info.accuracy = 0.0f;
10874 			fire_info.beam_info_index = shipp->weapons.primary_bank_weapons[(int)shipp->targeting_laser_bank];
10875 			fire_info.beam_info_override = NULL;
10876 			fire_info.shooter = &Objects[shipp->objnum];
10877 			fire_info.target = NULL;
10878 			fire_info.target_subsys = NULL;
10879 			fire_info.turret = NULL;
10880 			fire_info.targeting_laser_offset = m->gun_banks[shipp->targeting_laser_bank].pnt[0];
10881 			shipp->targeting_laser_objnum = beam_fire_targeting(&fire_info);
10882 
10883 			// hmm, why didn't it fire?
10884 			if(shipp->targeting_laser_objnum < 0){
10885 				Int3();
10886 				ship_stop_targeting_laser(shipp);
10887 			}
10888 		}
10889 	}}
10890 
10891 /**
10892  * Attempt to detonate weapon last fired by *src.
10893  * Only used for weapons that support remote detonation.
10894  *
10895  * @param swp	Ship weapon
10896  * @param src	Source of weapon
10897  * @return true if detonated, else return false.
10898  *
10899  *	Calls ::weapon_hit() to detonate weapon.
10900  *	If it's a weapon that spawns particles, those will be released.
10901  */
maybe_detonate_weapon(ship_weapon * swp,object * src)10902 int maybe_detonate_weapon(ship_weapon *swp, object *src)
10903 {
10904 	int			objnum = swp->last_fired_weapon_index;
10905 	object		*objp;
10906 	weapon_info	*wip;
10907 
10908 	objp = &Objects[objnum];
10909 
10910 	if (objp->type != OBJ_WEAPON){
10911 		return 0;
10912 	}
10913 
10914 	if ((objp->instance < 0) || (objp->instance >= MAX_WEAPONS)){
10915 		return 0;
10916 	}
10917 
10918 	// check to make sure that the weapon to detonate still exists
10919 	if ( swp->last_fired_weapon_signature != objp->signature ){
10920 		return 0;
10921 	}
10922 
10923 	Assert(Weapons[objp->instance].weapon_info_index != -1);
10924 	wip = &Weapon_info[Weapons[objp->instance].weapon_info_index];
10925 
10926 	if (wip->wi_flags & WIF_REMOTE) {
10927 
10928 		if ((objnum >= 0) && (objnum < MAX_OBJECTS)) {
10929 			int	weapon_sig;
10930 
10931 			weapon_sig = objp->signature;
10932 
10933 			if (swp->last_fired_weapon_signature == weapon_sig) {
10934 				weapon_detonate(objp);
10935 				swp->last_fired_weapon_index = -1;
10936 
10937 				return 1;
10938 			}
10939 		}
10940 	}
10941 
10942 	return 0;
10943 }
10944 
10945 /**
10946  * Maybe detonate secondary weapon that's already out.
10947  * @return Return true if we detonate it, false if not.
10948  */
ship_fire_secondary_detonate(object * obj,ship_weapon * swp)10949 int ship_fire_secondary_detonate(object *obj, ship_weapon *swp)
10950 {
10951 	if (swp->last_fired_weapon_index != -1)
10952 		if (timestamp_elapsed(swp->detonate_weapon_time)) {
10953 			object	*first_objp = &Objects[swp->last_fired_weapon_index];
10954 			if (maybe_detonate_weapon(swp, obj)) {
10955 				//	If dual fire was set, there could be another weapon to detonate.  Scan all weapons.
10956 				missile_obj	*mo;
10957 
10958 				// check for currently locked missiles (highest precedence)
10959 				for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
10960 					object	*mobjp;
10961 					Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
10962 					mobjp = &Objects[mo->objnum];
10963 					if ((mobjp != first_objp) && (mobjp->parent_sig == obj->parent_sig)) {
10964 						if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags & WIF_REMOTE) {
10965 							weapon_detonate(mobjp);
10966 						}
10967 					}
10968 				}
10969 
10970 				return 1;
10971 			}
10972 		}
10973 
10974 	return 0;
10975 }
10976 
10977 /**
10978  * Try to switch to a secondary bank that has ammo
10979  *
10980  * @note: not currently used - mark for removal?
10981  */
ship_select_next_valid_secondary_bank(ship_weapon * swp)10982 int ship_select_next_valid_secondary_bank(ship_weapon *swp)
10983 {
10984 	int cycled=0;
10985 
10986 	int ns = swp->num_secondary_banks;
10987 
10988 	if ( ns > 1 ) {
10989 		int i,j=swp->current_secondary_bank+1;
10990 		for (i=0; i<ns; i++) {
10991 			if ( j >= ns ) {
10992 				j=0;
10993 			}
10994 
10995 			if ( swp->secondary_bank_ammo[j] > 0 ) {
10996 				swp->current_secondary_bank=j;
10997 				cycled = 1;
10998 				break;
10999 			}
11000 
11001 			j++;
11002 		}
11003 	}
11004 
11005 	return cycled;
11006 }
11007 
11008 
11009 extern void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index);
11010 
11011 //	Object *obj fires its secondary weapon, if it can.
11012 //	If its most recently fired weapon is a remotely detonatable weapon, detonate it.
11013 //	Returns number of weapons fired.  Note, for swarmers, returns 1 if it is allowed
11014 //	to fire the missiles when allow_swarm is NOT set.  They don't actually get fired on a call here unless allow_swarm is set.
11015 //	When you want to fire swarmers, you call this function with allow_swarm NOT set and frame interval
11016 //	code comes aruond and fires it.
11017 // allow_swarm -> default value is 0... since swarm missiles are fired over several frames,
11018 //                need to avoid firing when normally called
ship_fire_secondary(object * obj,int allow_swarm)11019 int ship_fire_secondary( object *obj, int allow_swarm )
11020 {
11021 	int			n, weapon, j, bank, bank_adjusted, starting_bank_count = -1, num_fired;
11022 	ushort		starting_sig = 0;
11023 	ship			*shipp;
11024 	ship_weapon *swp;
11025 	ship_info	*sip;
11026 	weapon_info	*wip;
11027 	ai_info		*aip;
11028 	polymodel	*pm;
11029 	vec3d		missile_point, pnt, firing_pos;
11030 	bool has_fired = false;		// Used to determine whether to fire the scripting hook
11031 
11032 	Assert( obj != NULL );
11033 
11034 	// in the case where the server is an observer, he can fire (which would be bad) - unless we do this.
11035 	if( obj->type == OBJ_OBSERVER ){
11036 		return 0;
11037 	}
11038 
11039 	// in the case where the object is a ghost (a delayed fire packet from right before he died, for instance)
11040 	if( (obj->type == OBJ_GHOST) || (obj->type == OBJ_NONE) ){
11041 		return 0;
11042 	}
11043 
11044 	Assert( obj->type == OBJ_SHIP );
11045 	if(obj->type != OBJ_SHIP){
11046 		return 0;
11047 	}
11048 	n = obj->instance;
11049 	Assert( n >= 0 && n < MAX_SHIPS );
11050 	if((n < 0) || (n >= MAX_SHIPS)){
11051 		return 0;
11052 	}
11053 	Assert( Ships[n].objnum == OBJ_INDEX(obj));
11054 	if(Ships[n].objnum != OBJ_INDEX(obj)){
11055 		return 0;
11056 	}
11057 
11058 	shipp = &Ships[n];
11059 	swp = &shipp->weapons;
11060 	sip = &Ship_info[shipp->ship_info_index];
11061 	aip = &Ai_info[shipp->ai_index];
11062 
11063 	// if no secondary weapons are present on ship, return
11064 	if ( swp->num_secondary_banks <= 0 ){
11065 		return 0;
11066 	}
11067 
11068 	// If the secondaries have been locked, bail
11069 	if (shipp->flags2 & SF2_SECONDARIES_LOCKED)
11070 	{
11071 		return 0;
11072 	}
11073 
11074 	// If ship is being repaired/rearmed, it cannot fire missiles
11075 	if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
11076 		return 0;
11077 	}
11078 
11079 	num_fired = 0;		// tracks how many missiles actually fired
11080 
11081 	// niffiwan: allow swarm/corkscrew bank to keep firing if current bank changes
11082 	if (shipp->swarm_missile_bank != -1 && allow_swarm) {
11083 		bank = shipp->swarm_missile_bank;
11084 	} else if (shipp->corkscrew_missile_bank != -1 && allow_swarm) {
11085 		bank = shipp->corkscrew_missile_bank;
11086 	} else {
11087 		bank = swp->current_secondary_bank;
11088 	}
11089 
11090 	if ( bank < 0 || bank >= sip->num_secondary_banks ) {
11091 		return 0;
11092 	}
11093 	bank_adjusted = MAX_SHIP_PRIMARY_BANKS + bank;
11094 
11095 	if (swp->secondary_animation_position[bank] == MA_POS_SET) {
11096 		if ( timestamp_elapsed(swp->secondary_animation_done_time[bank]) )
11097 			swp->secondary_animation_position[bank] = MA_POS_READY;
11098 		else
11099 			return 0;
11100 	}
11101 
11102 	weapon = swp->secondary_bank_weapons[bank];
11103 	Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < MAX_WEAPON_TYPES) );
11104 	if((swp->secondary_bank_weapons[bank] < 0) || (swp->secondary_bank_weapons[bank] >= MAX_WEAPON_TYPES)){
11105 		return 0;
11106 	}
11107 	wip = &Weapon_info[weapon];
11108 
11109 	if ( MULTIPLAYER_MASTER ) {
11110 		starting_sig = multi_get_next_network_signature( MULTI_SIG_NON_PERMANENT );
11111 		starting_bank_count = swp->secondary_bank_ammo[bank];
11112 	}
11113 
11114 	if (ship_fire_secondary_detonate(obj, swp)) {
11115 		// in multiplayer, master sends a secondary fired packet with starting signature of -1 -- indicates
11116 		// to client code to set the detonate timer to 0.
11117 		if ( MULTIPLAYER_MASTER ) {
11118 			send_secondary_fired_packet( shipp, 0, starting_bank_count, 1, allow_swarm );
11119 		}
11120 
11121 		//	For all banks, if ok to fire a weapon, make it wait a bit.
11122 		//	Solves problem of fire button likely being down next frame and
11123 		//	firing weapon despite fire causing detonation of existing weapon.
11124 		if (swp->current_secondary_bank >= 0) {
11125 			if (timestamp_elapsed(swp->next_secondary_fire_stamp[bank])){
11126 				swp->next_secondary_fire_stamp[bank] = timestamp(MAX((int) flFrametime*3000, 250));
11127 			}
11128 		}
11129 		return 0;
11130 	}
11131 
11132 	// niffiwan: 04/03/12: duplicate of a check approx 100 lines above - not needed?
11133 	if ( bank < 0 ){
11134 		return 0;
11135 	}
11136 
11137 	if ( !timestamp_elapsed(swp->next_secondary_fire_stamp[bank]) && !allow_swarm) {
11138 		if (timestamp_until(swp->next_secondary_fire_stamp[bank]) > 60000){
11139 			swp->next_secondary_fire_stamp[bank] = timestamp(1000);
11140 		}
11141 		goto done_secondary;
11142 	}
11143 
11144 	// Ensure if this is a "require-lock" missile, that a lock actually exists
11145 	if ( wip->wi_flags & WIF_NO_DUMBFIRE ) {
11146 		if ( aip->current_target_is_locked <= 0 ) {
11147 			if ( obj == Player_obj ) {
11148 				if ( !Weapon_energy_cheat ) {
11149 					float max_dist;
11150 
11151 					max_dist = wip->lifetime * wip->max_speed;
11152 					if (wip->wi_flags2 & WIF2_LOCAL_SSM){
11153 						max_dist= wip->lssm_lock_range;
11154 					}
11155 
11156 					if ((aip->target_objnum != -1) && (vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
11157 						HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Too far from target to acquire lock", 487));
11158 					} else {
11159 						char missile_name[NAME_LENGTH];
11160 						strcpy_s(missile_name, wip->name);
11161 						end_string_at_first_hash_symbol(missile_name);
11162 						HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s without a lock", 488), missile_name);
11163 					}
11164 
11165 					snd_play( &Snds[ship_get_sound(Player_obj, SND_OUT_OF_MISSLES)] );
11166 					swp->next_secondary_fire_stamp[bank] = timestamp(800);	// to avoid repeating messages
11167 					return 0;
11168 				}
11169 			} else {
11170 				// multiplayer clients should always fire the weapon here, so return only if not
11171 				// a multiplayer client.
11172 				if ( !MULTIPLAYER_CLIENT ) {
11173 					return 0;
11174 				}
11175 			}
11176 		}
11177 	}
11178 
11179 	if (wip->wi_flags2 & WIF2_TAGGED_ONLY)
11180 	{
11181 		if (!ship_is_tagged(&Objects[aip->target_objnum]))
11182 		{
11183 			if (obj==Player_obj)
11184 			{
11185 				if ( !Weapon_energy_cheat )
11186 				{
11187 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Cannot fire %s if target is not tagged"),wip->name);
11188 					snd_play( &Snds[ship_get_sound(Player_obj, SND_OUT_OF_MISSLES)] );
11189 					swp->next_secondary_fire_stamp[bank] = timestamp(800);	// to avoid repeating messages
11190 					return 0;
11191 				}
11192 			}
11193 			else
11194 			{
11195 				if ( !MULTIPLAYER_CLIENT )
11196 				{
11197 					return 0;
11198 				}
11199 			}
11200 		}
11201 	}
11202 
11203 
11204 
11205 	// if trying to fire a swarm missile, make sure being called from right place
11206 	if ( (wip->wi_flags & WIF_SWARM) && !allow_swarm ) {
11207 		Assert(wip->swarm_count > 0);
11208 		if(wip->swarm_count <= 0){
11209 			shipp->num_swarm_missiles_to_fire = SWARM_DEFAULT_NUM_MISSILES_FIRED;
11210 		} else {
11211 			shipp->num_swarm_missiles_to_fire = wip->swarm_count;
11212 		}
11213 		shipp->swarm_missile_bank = bank;
11214 		return 1;		//	Note: Missiles didn't get fired, but the frame interval code will fire them.
11215 	}
11216 
11217 	// if trying to fire a corkscrew missile, make sure being called from right place
11218 	if ( (wip->wi_flags & WIF_CORKSCREW) && !allow_swarm ) {
11219 		//phreak 11-9-02
11220 		//changed this from 4 to custom number defined in tables
11221 		shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
11222 		shipp->corkscrew_missile_bank = bank;
11223 		return 1;		//	Note: Missiles didn't get fired, but the frame interval code will fire them.
11224 	}
11225 
11226 	float t;
11227 
11228 	if (Weapon_info[weapon].burst_shots > swp->burst_counter[bank_adjusted]) {
11229 		t = Weapon_info[weapon].burst_delay;
11230 		swp->burst_counter[bank_adjusted]++;
11231 	} else {
11232 		t = Weapon_info[weapon].fire_wait;	// They can fire 5 times a second
11233 		swp->burst_counter[bank_adjusted] = 0;
11234 	}
11235 	swp->next_secondary_fire_stamp[bank] = timestamp((int) (t * 1000.0f));
11236 	swp->last_secondary_fire_stamp[bank] = timestamp();
11237 
11238 	// Here is where we check if weapons subsystem is capable of firing the weapon.
11239 	// do only in single player or if I am the server of a multiplayer game
11240 	if ( !(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER ) {
11241 		if ( ship_weapon_maybe_fail(shipp) ) {
11242 			if ( obj == Player_obj )
11243 				if ( ship_maybe_play_secondary_fail_sound(wip) ) {
11244 					char missile_name[NAME_LENGTH];
11245 					strcpy_s(missile_name, Weapon_info[weapon].name);
11246 					end_string_at_first_hash_symbol(missile_name);
11247 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s due to weapons system damage", 489), missile_name);
11248 				}
11249 			goto done_secondary;
11250 		}
11251 	}
11252 
11253 	pm = model_get( sip->model_num );
11254 	if ( pm->n_missiles > 0 ) {
11255 		int check_ammo;		// used to tell if we should check ammo counts or not
11256 		int num_slots;
11257 
11258 		if ( bank > pm->n_missiles ) {
11259 			nprintf(("WARNING","WARNING ==> Tried to fire bank %d, but ship has only %d banks\n", bank+1, pm->n_missiles));
11260 			return 0;		// we can make a quick out here!!!
11261 		}
11262 
11263 		num_slots = pm->missile_banks[bank].num_slots;
11264 
11265 		// determine if there is enough ammo left to fire weapons on this bank.  As with primary
11266 		// weapons, we might or might not check ammo counts depending on game mode, who is firing,
11267 		// and if I am a client in multiplayer
11268 		check_ammo = 1;
11269 
11270 		if ( MULTIPLAYER_CLIENT && (obj != Player_obj) ){
11271 			check_ammo = 0;
11272 		}
11273 
11274 		if ( check_ammo && ( swp->secondary_bank_ammo[bank] <= 0) ) {
11275 			if ( shipp->objnum == OBJ_INDEX(Player_obj) ) {
11276 				if ( ship_maybe_play_secondary_fail_sound(wip) ) {
11277 //					HUD_sourced_printf(HUD_SOURCE_HIDDEN, "No %s missiles left in bank", Weapon_info[swp->secondary_bank_weapons[bank]].name);
11278 				}
11279 			}
11280 			else {
11281 				// TODO:  AI switch secondary weapon / re-arm?
11282 			}
11283 			goto done_secondary;
11284 		}
11285 
11286 		int start_slot, end_slot;
11287 
11288 		if ( shipp->flags & SF_SECONDARY_DUAL_FIRE && num_slots > 1) {
11289 			start_slot = swp->secondary_next_slot[bank];
11290 			// AL 11-19-97: Ensure enough ammo remains when firing linked secondary weapons
11291 			if ( check_ammo && (swp->secondary_bank_ammo[bank] < 2) ) {
11292 				end_slot = start_slot;
11293 			} else {
11294 				end_slot = start_slot+1;
11295 			}
11296 		} else {
11297 			// de-set the flag just in case dual-fire was set but couldn't be used
11298 			// because there's less than two firepoints
11299 			shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
11300 			start_slot = swp->secondary_next_slot[bank];
11301 			end_slot = start_slot;
11302 		}
11303 
11304 		int pnt_index=start_slot;
11305 		//If this is a tertiary weapon, only subtract one piece of ammo
11306 		for ( j = start_slot; j <= end_slot; j++ ) {
11307 			int	weapon_num;
11308 
11309 			swp->secondary_next_slot[bank]++;
11310 			if ( swp->secondary_next_slot[bank] > (num_slots-1) ){
11311 				swp->secondary_next_slot[bank] = 0;
11312 			}
11313 
11314 			if ( pnt_index >= num_slots ){
11315 				pnt_index = 0;
11316 			}
11317 			shipp->secondary_point_reload_pct[bank][pnt_index] = 0.0f;
11318 			pnt = pm->missile_banks[bank].pnt[pnt_index++];
11319 
11320 			polymodel *weapon_model = NULL;
11321 			if(wip->external_model_num >= 0){
11322 				weapon_model = model_get(wip->external_model_num);
11323 			}
11324 
11325 			if (weapon_model && weapon_model->n_guns) {
11326 				int external_bank = bank + MAX_SHIP_PRIMARY_BANKS;
11327 				if (wip->wi_flags2 & WIF2_EXTERNAL_WEAPON_FP) {
11328 					if ((weapon_model->n_guns <= swp->external_model_fp_counter[external_bank]) || (swp->external_model_fp_counter[external_bank] < 0))
11329 						swp->external_model_fp_counter[external_bank] = 0;
11330 					vm_vec_add2(&pnt, &weapon_model->gun_banks[0].pnt[swp->external_model_fp_counter[external_bank]]);
11331 					swp->external_model_fp_counter[external_bank]++;
11332 				} else {
11333 					// make it use the 0 index slot
11334 					vm_vec_add2(&pnt, &weapon_model->gun_banks[0].pnt[0]);
11335 				}
11336 			}
11337 			vm_vec_unrotate(&missile_point, &pnt, &obj->orient);
11338 			vm_vec_add(&firing_pos, &missile_point, &obj->pos);
11339 
11340 			if ( Game_mode & GM_MULTIPLAYER ) {
11341 				Assert( Weapon_info[weapon].subtype == WP_MISSILE );
11342 			}
11343 
11344 			matrix firing_orient;
11345 			if(!(sip->flags2 & SIF2_GUN_CONVERGENCE))
11346 			{
11347 				firing_orient = obj->orient;
11348 			}
11349 			else
11350 			{
11351 				vec3d firing_vec;
11352 				vm_vec_unrotate(&firing_vec, &pm->missile_banks[bank].norm[pnt_index-1], &obj->orient);
11353 				vm_vector_2_matrix(&firing_orient, &firing_vec, NULL, NULL);
11354 			}
11355 
11356 			// create the weapon -- for multiplayer, the net_signature is assigned inside
11357 			// of weapon_create
11358 			weapon_num = weapon_create( &firing_pos, &firing_orient, weapon, OBJ_INDEX(obj), -1, aip->current_target_is_locked);
11359 
11360 			if (weapon_num >= 0) {
11361 				weapon = Weapons[Objects[weapon_num].instance].weapon_info_index;
11362 				weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);
11363 				has_fired = true;
11364 
11365 				// create the muzzle flash effect
11366 				if ( (obj != Player_obj) || (sip->flags2 & SIF2_SHOW_SHIP_MODEL) || (Viewer_mode) ) {
11367 					// show the flash only if in not cockpit view, or if "show ship" flag is set
11368 					shipfx_flash_create(obj, sip->model_num, &pnt, &obj->orient.vec.fvec, 0, weapon);
11369 				}
11370 
11371 				if((wip->wi_flags & WIF_SHUDDER) && (obj == Player_obj) && !(Game_mode & GM_STANDALONE_SERVER)){
11372 					// calculate some arbitrary value between 100
11373 					// (mass * velocity) / 10
11374 					game_shudder_apply(500, (wip->mass * wip->max_speed) * 0.1f);
11375 				}
11376 
11377 				num_fired++;
11378 				swp->last_fired_weapon_index = weapon_num;
11379 				swp->detonate_weapon_time = timestamp(500);		//	Can detonate 1/2 second later.
11380 				swp->last_fired_weapon_signature = Objects[weapon_num].signature;
11381 
11382 				// subtract the number of missiles fired
11383 				if ( Weapon_energy_cheat == 0 ){
11384 					swp->secondary_bank_ammo[bank]--;
11385 				}
11386 			}
11387 		}
11388 	}
11389 
11390 	if ( obj == Player_obj ) {
11391 		if ( Weapon_info[weapon].launch_snd != -1 ) {
11392 			snd_play( &Snds[Weapon_info[weapon].launch_snd], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
11393 			swp = &Player_ship->weapons;
11394 			if (bank >= 0) {
11395 				wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
11396 				if (Player_ship->flags & SF_SECONDARY_DUAL_FIRE){
11397 					joy_ff_play_secondary_shoot((int) (wip->cargo_size * 2.0f));
11398 				} else {
11399 					joy_ff_play_secondary_shoot((int) wip->cargo_size);
11400 				}
11401 			}
11402 		}
11403 
11404 	} else {
11405 		if ( Weapon_info[weapon].launch_snd != -1 ) {
11406 			snd_play_3d( &Snds[Weapon_info[weapon].launch_snd], &obj->pos, &View_position );
11407 		}
11408 	}
11409 
11410 done_secondary:
11411 
11412 	if(num_fired > 0){
11413 		// if I am the master of a multiplayer game, send a secondary fired packet along with the
11414 		// first network signatures for the newly created weapons.  if nothing got fired, send a failed
11415 		// packet if
11416 		if ( MULTIPLAYER_MASTER ) {
11417 			Assert(starting_sig != 0);
11418 			send_secondary_fired_packet( shipp, starting_sig, starting_bank_count, num_fired, allow_swarm );
11419 		}
11420 
11421 		// STATS
11422 		if (obj->flags & OF_PLAYER_SHIP) {
11423 			// in multiplayer -- only the server needs to keep track of the stats.  Call the cool
11424 			// function to find the player given the object *.  It had better return a valid player
11425 			// or our internal structure as messed up.
11426 			if( Game_mode & GM_MULTIPLAYER ) {
11427 				if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
11428 					int player_num;
11429 
11430 					player_num = multi_find_player_by_object ( obj );
11431 					Assert ( player_num != -1 );
11432 
11433 					Net_players[player_num].m_player->stats.ms_shots_fired += num_fired;
11434 				}
11435 			} else {
11436 				Player->stats.ms_shots_fired += num_fired;
11437 			}
11438 		}
11439 
11440 		// maybe announce a shockwave weapon
11441 		ai_maybe_announce_shockwave_weapon(obj, weapon);
11442 	}
11443 
11444 	// if we are out of ammo in this bank then don't carry over firing swarm/corkscrew
11445 	// missiles to a new bank
11446 	if (swp->secondary_bank_ammo[bank] <= 0) {
11447 		// NOTE: these are set to 1 since they will get reduced by 1 in the
11448 		//       swarm/corkscrew code once this function returns
11449 
11450 		if (shipp->num_swarm_missiles_to_fire > 1) {
11451 			shipp->num_swarm_missiles_to_fire = 1;
11452 			shipp->swarm_missile_bank = -1;
11453 		}
11454 
11455 		if (shipp->num_corkscrew_to_fire > 1) {
11456 			shipp->num_corkscrew_to_fire = 1;
11457 			shipp->corkscrew_missile_bank = -1;
11458 		}
11459 	}
11460 
11461 	// AL 3-7-98: Move to next valid secondary bank if out of ammo
11462 	//
11463 
11464 	//21-07-02 01:24 DTP; COMMENTED OUT some of the mistakes
11465 	//this bug was made by AL, when he assumed he had to take the next fire_wait time remaining and add 250 ms of delay to it,
11466 	//and put it in the next valid bank. for the player to have a 250 ms of penalty
11467 	//
11468 	//what that caused was that the next valid bank inherited the current valid banks FULL fire delay. since he used the Weapon_info struct that has
11469 	// no information / member that stores the next valids banks remaning fire_wait delay.
11470 	//
11471 	//what he should have done was to check of the next valid bank had any fire delay that had elapsed, if it had elapsed,
11472 	//then it would have no firedelay. and then add 250 ms of delay. in effect, this way there is no penalty if there is any firedelay remaning in
11473 	//the next valid bank. the delay is there to prevent things like Trible/Quad Fire Trebuchets.
11474 	//
11475 	// niffiwan: only try to switch banks if object has multiple banks, and firing bank is the current bank
11476 	if ( (obj->flags & OF_PLAYER_SHIP) && (swp->secondary_bank_ammo[bank] <= 0) && (swp->num_secondary_banks >= 2) && (bank == swp->current_secondary_bank) ) {
11477 		// niffiwan: call ship_select_next_secondary instead of ship_select_next_valid_secondary_bank
11478 		// ensures all "extras" are dealt with, like animations, scripting hooks, etc
11479 		if (ship_select_next_secondary(obj) ) {			//DTP here we switch to the next valid bank, but we can't call weapon_info on next fire_wait
11480 
11481 			if ( timestamp_elapsed(shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank]) ) {	//DTP, this is simply a copy of the manual cycle functions
11482 				shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(1000);	//Bumped from 250 to 1000 because some people seem to be to triggerhappy :).
11483 				shipp->weapons.last_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp();
11484 			}
11485 
11486 			if ( obj == Player_obj ) {
11487 				snd_play( &Snds[ship_get_sound(Player_obj, SND_SECONDARY_CYCLE)] );
11488 			}
11489 		}
11490 	}
11491 
11492 	if (has_fired) {
11493 		object *objp = &Objects[shipp->objnum];
11494 		object* target;
11495 		if (Ai_info[shipp->ai_index].target_objnum != -1)
11496 			target = &Objects[Ai_info[shipp->ai_index].target_objnum];
11497 		else
11498 			target = NULL;
11499 		if (objp == Player_obj && Player_ai->target_objnum != -1)
11500 			target = &Objects[Player_ai->target_objnum];
11501 		Script_system.SetHookObjects(2, "User", objp, "Target", target);
11502 		Script_system.RunCondition(CHA_ONWPFIRED, 0, NULL, objp);
11503 		Script_system.RunCondition(CHA_SECONDARYFIRE, 0, NULL, objp);
11504 	}
11505 
11506 	return num_fired;
11507 }
11508 
11509 // Goober5000
primary_out_of_ammo(ship_weapon * swp,int bank)11510 int primary_out_of_ammo(ship_weapon *swp, int bank)
11511 {
11512 	// true if both ballistic and ammo <= 0,
11513 	// false if not ballistic or if ballistic and ammo > 0
11514 
11515 	if ( Weapon_info[swp->primary_bank_weapons[bank]].wi_flags2 & WIF2_BALLISTIC )
11516 	{
11517 		if (swp->primary_bank_ammo[bank] <= 0)
11518 		{
11519 			return 1;
11520 		}
11521 	}
11522 
11523 	// note: never out of ammo if not ballistic
11524 	return 0;
11525 }
11526 
11527 /**
11528  * Return true if a new index gets selected.
11529  *
11530  * @param objp      pointer to object for ship cycling primary
11531  * @param direction forward == CYCLE_PRIMARY_NEXT, backward == CYCLE_PRIMARY_PREV
11532  *
11533  * NOTE: This code can be called for any arbitrary ship.  HUD messages and sounds are only used
11534  *       for the player ship.
11535  */
ship_select_next_primary(object * objp,int direction)11536 int ship_select_next_primary(object *objp, int direction)
11537 {
11538 	ship	*shipp;
11539 	ship_info *sip;
11540 	ship_weapon *swp;
11541 	int new_bank;
11542 	int original_bank;
11543 	unsigned int original_link_flag;
11544 	int i;
11545 
11546 	Assert(objp != NULL);
11547 	Assert(objp->type == OBJ_SHIP);
11548 	Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS);
11549 
11550 	shipp = &Ships[objp->instance];
11551 	sip = &Ship_info[shipp->ship_info_index];
11552 	swp = &shipp->weapons;
11553 
11554 	Assert(direction == CYCLE_PRIMARY_NEXT || direction == CYCLE_PRIMARY_PREV);
11555 
11556 	original_bank = swp->current_primary_bank;
11557 	original_link_flag = shipp->flags & SF_PRIMARY_LINKED;
11558 
11559 	// redid case structure to possibly add more primaries in the future - Goober5000
11560 	if ( swp->num_primary_banks == 0 )
11561 	{
11562 		if ( objp == Player_obj )
11563 		{
11564 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has no primary weapons", 490));
11565 			gamesnd_play_error_beep();
11566 		}
11567 		return 0;
11568 	}
11569 	else if ( swp->num_primary_banks == 1 )
11570 	{
11571 		if ( objp == Player_obj )
11572 		{
11573 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has only one primary weapon: %s", 491),Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].name, swp->current_primary_bank + 1);
11574 			gamesnd_play_error_beep();
11575 		}
11576 		return 0;
11577 	}
11578 	else if ( swp->num_primary_banks > MAX_SHIP_PRIMARY_BANKS )
11579 	{
11580 		Int3();
11581 		return 0;
11582 	}
11583 	else
11584 	{
11585 		Assert((swp->current_primary_bank >= 0) && (swp->current_primary_bank < swp->num_primary_banks));
11586 
11587 		// first check if linked
11588 		if ( shipp->flags2 & SF2_SHIP_SELECTIVE_LINKING )
11589 		{
11590 			printf("npb:%i\n", swp->num_primary_banks );
11591 		}
11592 
11593 		if ( shipp->flags & SF_PRIMARY_LINKED )
11594 		{
11595 			shipp->flags &= ~SF_PRIMARY_LINKED;
11596 			if ( direction == CYCLE_PRIMARY_NEXT )
11597 			{
11598 				swp->current_primary_bank = 0;
11599 			}
11600 			else
11601 			{
11602 				swp->current_primary_bank = swp->num_primary_banks - 1;
11603 			}
11604 		}
11605 		// now handle when not linked: cycle and constrain
11606 		else
11607 		{
11608 			if ( direction == CYCLE_PRIMARY_NEXT )
11609 			{
11610 				if ( swp->current_primary_bank < swp->num_primary_banks - 1 )
11611 				{
11612 					swp->current_primary_bank++;
11613 				}
11614 				else if( sip->flags2 & SIF2_NO_PRIMARY_LINKING )
11615 				{
11616 					swp->current_primary_bank = 0;
11617 				}
11618 				else
11619 				{
11620 					shipp->flags |= SF_PRIMARY_LINKED;
11621 				}
11622 			}
11623 			else
11624 			{
11625 				if ( swp->current_primary_bank > 0 )
11626 				{
11627 					swp->current_primary_bank--;
11628 				}
11629 				else if( sip->flags2 & SIF2_NO_PRIMARY_LINKING )
11630 				{
11631 					swp->current_primary_bank = swp->num_primary_banks - 1;
11632 				}
11633 				else
11634 				{
11635 					shipp->flags |= SF_PRIMARY_LINKED;
11636 				}
11637 			}
11638 		}
11639 	}
11640 
11641 	// test for ballistics - Goober5000
11642 	if ( sip->flags & SIF_BALLISTIC_PRIMARIES )
11643 	{
11644 		// if we can't link, disengage primary linking and change to next available bank
11645 		if (shipp->flags & SF_PRIMARY_LINKED)
11646 		{
11647 			for (i = 0; i < swp->num_primary_banks; i++)
11648 			{
11649 				if (primary_out_of_ammo(swp, i))
11650 				{
11651 					shipp->flags &= ~SF_PRIMARY_LINKED;
11652 
11653 					if (direction == CYCLE_PRIMARY_NEXT)
11654 					{
11655 						swp->current_primary_bank = 0;
11656 					}
11657 					else
11658 					{
11659 						swp->current_primary_bank = shipp->weapons.num_primary_banks-1;
11660 					}
11661 					break;
11662 				}
11663 			}
11664 		}
11665 
11666 		// check to see if we keep cycling...we have to if we're out of ammo in the current bank
11667 		if ( primary_out_of_ammo(swp, swp->current_primary_bank) )
11668 		{
11669 			// cycle around until we find ammunition...
11670 			// we land on the original bank if all banks fail
11671 			Assert(swp->current_primary_bank < swp->num_primary_banks);
11672 			new_bank = swp->current_primary_bank;
11673 
11674 			for (i = 1; i < swp->num_primary_banks; i++)
11675 			{
11676 				// cycle in the proper direction
11677 				if ( direction == CYCLE_PRIMARY_NEXT )
11678 				{
11679 					new_bank = (swp->current_primary_bank + i) % swp->num_primary_banks;
11680 				}
11681 				else
11682 				{
11683 					new_bank = (swp->current_primary_bank + swp->num_primary_banks - i) % swp->num_primary_banks;
11684 				}
11685 
11686 				// check to see if this is a valid bank
11687 				if (!primary_out_of_ammo(swp, new_bank))
11688 				{
11689 					break;
11690 				}
11691 			}
11692 			// set the new bank; defaults to resetting to the old bank if we completed a full iteration
11693 			swp->current_primary_bank = new_bank;
11694 		}
11695 
11696 		// make sure we're okay
11697 		Assert((swp->current_primary_bank >= 0) && (swp->current_primary_bank < swp->num_primary_banks));
11698 
11699 		if(swp->current_primary_bank != original_bank)
11700 			swp->previous_primary_bank = original_bank;
11701 		else
11702 			swp->previous_primary_bank = swp->current_primary_bank;
11703 
11704 		// if this ship is ballistics-equipped, and we cycled, then we had to verify some stuff,
11705 		// so we should check if we actually changed banks
11706 		if ( (swp->current_primary_bank != original_bank) || ((shipp->flags & SF_PRIMARY_LINKED) != original_link_flag) )
11707 		{
11708 			if ( objp == Player_obj )
11709 			{
11710 				snd_play( &Snds[ship_get_sound(objp, SND_PRIMARY_CYCLE)], 0.0f );
11711 			}
11712 			ship_primary_changed(shipp);
11713 			objp = &Objects[shipp->objnum];
11714 			object* target;
11715 			if (Ai_info[shipp->ai_index].target_objnum != -1)
11716 				target = &Objects[Ai_info[shipp->ai_index].target_objnum];
11717 			else
11718 				target = NULL;
11719 			if (objp == Player_obj && Player_ai->target_objnum != -1)
11720 				target = &Objects[Player_ai->target_objnum];
11721 			Script_system.SetHookObjects(2, "User", objp, "Target", target);
11722 			Script_system.RunCondition(CHA_ONWPSELECTED, 0, NULL, objp);
11723 			Script_system.SetHookObjects(2, "User", objp, "Target", target);
11724 			Script_system.RunCondition(CHA_ONWPDESELECTED, 0, NULL, objp);
11725 			return 1;
11726 		}
11727 
11728 		// could not select new weapon:
11729 		if ( objp == Player_obj )
11730 		{
11731 			gamesnd_play_error_beep();
11732 		}
11733 		return 0;
11734 	}	// end of ballistics implementation
11735 
11736 	if ( objp == Player_obj )
11737 	{
11738 		snd_play( &Snds[ship_get_sound(objp, SND_PRIMARY_CYCLE)], 0.0f );
11739 	}
11740 
11741 	ship_primary_changed(shipp);
11742 	object* target;
11743 	if (Ai_info[shipp->ai_index].target_objnum != -1)
11744 		target = &Objects[Ai_info[shipp->ai_index].target_objnum];
11745 	else
11746 		target = NULL;
11747 	if (objp == Player_obj && Player_ai->target_objnum != -1)
11748 		target = &Objects[Player_ai->target_objnum];
11749 	Script_system.SetHookObjects(2, "User", objp, "Target", target);
11750 	Script_system.RunCondition(CHA_ONWPSELECTED, 0, NULL, objp);
11751 	Script_system.SetHookObjects(2, "User", objp, "Target", target);
11752 	Script_system.RunCondition(CHA_ONWPDESELECTED, 0, NULL, objp);
11753 
11754 	return 1;
11755 }
11756 
11757 // ------------------------------------------------------------------------------
11758 // ship_select_next_secondary() selects the next secondary bank with missles
11759 //
11760 //	returns:		1	=> The secondary bank was switched
11761 //					0	=> The secondary bank stayed the same
11762 //
11763 // If a secondary bank has no missles left, it is skipped.
11764 //
11765 // NOTE: This can be called for an arbitrary ship.  HUD messages and sounds are only used
11766 //			for the player ship.
ship_select_next_secondary(object * objp)11767 int ship_select_next_secondary(object *objp)
11768 {
11769 	Assert(objp != NULL);
11770 	Assert(objp->type == OBJ_SHIP);
11771 	Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS);
11772 
11773 	int	original_bank, new_bank, i;
11774 	ship	*shipp;
11775 	ship_weapon *swp;
11776 
11777 	shipp = &Ships[objp->instance];
11778 	swp = &shipp->weapons;
11779 
11780 	// redid the switch structure to allow additional seconary banks if added later - Goober5000
11781 	if ( swp->num_secondary_banks == 0 )
11782 	{
11783 		if ( objp == Player_obj )
11784 		{
11785 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has no secondary weapons", 492));
11786 			gamesnd_play_error_beep();
11787 		}
11788 		return 0;
11789 	}
11790 	else if ( swp->num_secondary_banks == 1 )
11791 	{
11792 		if ( objp == Player_obj )
11793 		{
11794 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has only one secondary weapon: %s", 493), Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name, swp->current_secondary_bank + 1);
11795 			gamesnd_play_error_beep();
11796 		}
11797 		return 0;
11798 	}
11799 	else if ( swp->num_secondary_banks > MAX_SHIP_SECONDARY_BANKS )
11800 	{
11801 		Int3();
11802 		return 0;
11803 	}
11804 	else
11805 	{
11806 		Assert((swp->current_secondary_bank >= 0) && (swp->current_secondary_bank < swp->num_secondary_banks));
11807 
11808 		original_bank = swp->current_secondary_bank;
11809 
11810 		for ( i = 1; i < swp->num_secondary_banks; i++ ) {
11811 			new_bank = (swp->current_secondary_bank+i) % swp->num_secondary_banks;
11812 			if ( swp->secondary_bank_ammo[new_bank] <= 0 )
11813 				continue;
11814 			swp->current_secondary_bank = new_bank;
11815 			break;
11816 		}
11817 
11818 		if ( swp->current_secondary_bank != original_bank )
11819 		{
11820 			if(swp->current_primary_bank != original_bank)
11821 				swp->previous_primary_bank = original_bank;
11822 			else
11823 				swp->previous_primary_bank = swp->current_primary_bank;
11824 			if ( objp == Player_obj )
11825 			{
11826 				snd_play( &Snds[ship_get_sound(Player_obj, SND_SECONDARY_CYCLE)], 0.0f );
11827 			}
11828 			ship_secondary_changed(shipp);
11829 
11830 			objp = &Objects[shipp->objnum];
11831 			object* target;
11832 			if (Ai_info[shipp->ai_index].target_objnum != -1)
11833 				target = &Objects[Ai_info[shipp->ai_index].target_objnum];
11834 			else
11835 				target = NULL;
11836 			if (objp == Player_obj && Player_ai->target_objnum != -1)
11837 				target = &Objects[Player_ai->target_objnum];
11838 			Script_system.SetHookObjects(2, "User", objp, "Target", target);
11839 			Script_system.RunCondition(CHA_ONWPSELECTED, 0, NULL, objp);
11840 			Script_system.SetHookObjects(2, "User", objp, "Target", target);
11841 			Script_system.RunCondition(CHA_ONWPDESELECTED, 0, NULL, objp);
11842 			return 1;
11843 		}
11844 	} // end if
11845 
11846 	// If we've reached this point, must have failed
11847 	if ( objp == Player_obj )
11848 	{
11849 		gamesnd_play_error_beep();
11850 	}
11851 	return 0;
11852 }
11853 
11854 // Goober5000 - copied from secondary routine
11855 //	Stuff list of weapon indices for object *objp in list *outlist.
11856 //	Return number of weapons in list.
get_available_primary_weapons(object * objp,int * outlist,int * outbanklist)11857 int get_available_primary_weapons(object *objp, int *outlist, int *outbanklist)
11858 {
11859 	int	count = 0;
11860 	int	i;
11861 	ship	*shipp;
11862 
11863 	Assert(objp->type == OBJ_SHIP);
11864 	Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11865 	shipp = &Ships[objp->instance];
11866 
11867 	for (i=0; i<shipp->weapons.num_primary_banks; i++)
11868 	{
11869 		if (!primary_out_of_ammo(&(shipp->weapons), i))
11870 		{
11871 			outbanklist[count] = i;
11872 			outlist[count++] = shipp->weapons.primary_bank_weapons[i];
11873 		}
11874 	}
11875 
11876 	return count;
11877 }
11878 
11879 /**
11880  * Stuff list of weapon indices for object *objp in list *outlist.
11881  * @return number of weapons in list.
11882  */
get_available_secondary_weapons(object * objp,int * outlist,int * outbanklist)11883 int get_available_secondary_weapons(object *objp, int *outlist, int *outbanklist)
11884 {
11885 	int	count = 0;
11886 	int	i;
11887 	ship	*shipp;
11888 
11889 	Assert(objp->type == OBJ_SHIP);
11890 	Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11891 	shipp = &Ships[objp->instance];
11892 
11893 	for (i=0; i<shipp->weapons.num_secondary_banks; i++)
11894 		if (shipp->weapons.secondary_bank_ammo[i]) {
11895 			outbanklist[count] = i;
11896 			outlist[count++] = shipp->weapons.secondary_bank_weapons[i];
11897 		}
11898 
11899 	return count;
11900 }
11901 
wing_bash_ship_name(char * ship_name,const char * wing_name,int index)11902 void wing_bash_ship_name(char *ship_name, const char *wing_name, int index)
11903 {
11904 	// if wing name has a hash symbol, create the ship name a particular way
11905 	// (but don't do this for names that have the hash as the first or last character)
11906 	const char *p = get_pointer_to_first_hash_symbol(wing_name);
11907 	if ((p != NULL) && (p != wing_name) && (*(p+1) != '\0'))
11908 	{
11909 		size_t len = (p - wing_name);
11910 		strncpy(ship_name, wing_name, len);
11911 		sprintf(ship_name + len, NOX(" %d"), index);
11912 		strcat(ship_name, p);
11913 	}
11914 	// most of the time we should create the name the standard retail way
11915 	else
11916 		sprintf(ship_name, NOX("%s %d"), wing_name, index);
11917 }
11918 
11919 /**
11920  * Return the object index of the ship with name *name.
11921  */
wing_name_lookup(const char * name,int ignore_count)11922 int wing_name_lookup(const char *name, int ignore_count)
11923 {
11924 	int i, wing_limit;
11925 
11926 	if (name == NULL)
11927 		return -1;
11928 
11929 	if ( Fred_running )
11930 		wing_limit = MAX_WINGS;
11931 	else
11932 		wing_limit = Num_wings;
11933 
11934 	if (Fred_running || ignore_count ) {  // current_count not used for Fred..
11935 		for (i=0; i<wing_limit; i++)
11936 			if (Wings[i].wave_count && !stricmp(Wings[i].name, name))
11937 				return i;
11938 
11939 	} else {
11940 		for (i=0; i<wing_limit; i++)
11941 			if (Wings[i].current_count && !stricmp(Wings[i].name, name))
11942 				return i;
11943 	}
11944 
11945 	return -1;
11946 }
11947 
11948 /**
11949  * Needed in addition to wing_name_lookup because it does a straight lookup without
11950  * caring about how many ships are in the wing, etc.
11951  */
wing_lookup(const char * name)11952 int wing_lookup(const char *name)
11953 {
11954    int idx;
11955 	for(idx=0;idx<Num_wings;idx++)
11956 		if(stricmp(Wings[idx].name,name)==0)
11957 		   return idx;
11958 
11959 	return -1;
11960 }
11961 
11962 /**
11963  * Return the index of Ship_info[].name that is *token.
11964  */
ship_info_lookup_sub(const char * token)11965 int ship_info_lookup_sub(const char *token)
11966 {
11967 	int	i;
11968 
11969 	for (i = 0; i < Num_ship_classes; i++)
11970 		if (!stricmp(token, Ship_info[i].name))
11971 			return i;
11972 
11973 	return -1;
11974 }
11975 
11976 /**
11977  * Return the index of Ship_templates[].name that is *token.
11978  */
ship_template_lookup(const char * token)11979 int ship_template_lookup(const char *token)
11980 {
11981 	int	i;
11982 
11983 	for ( i = 0; i < (int)Ship_templates.size(); i++ ) {
11984 		if ( !stricmp(token, Ship_templates[i].name) ) {
11985 			return i;
11986 		}
11987 	}
11988 	return -1;
11989 }
11990 
11991 // Goober5000
ship_info_lookup(const char * token)11992 int ship_info_lookup(const char *token)
11993 {
11994 	int idx;
11995 	const char *p;
11996 	char name[NAME_LENGTH], temp1[NAME_LENGTH], temp2[NAME_LENGTH];
11997 
11998 	// bogus
11999 	if (token == NULL)
12000 		return -1;
12001 
12002 	// first try a straightforward lookup
12003 	idx = ship_info_lookup_sub(token);
12004 	if (idx >= 0)
12005 		return idx;
12006 
12007 	// ship copy types might be mismatched
12008 	p = get_pointer_to_first_hash_symbol(token);
12009 	if (p == NULL)
12010 		return -1;
12011 
12012 	// conversion from FS1 missions
12013 	if (!stricmp(token, "GTD Orion#1 (Galatea)"))
12014 	{
12015 		idx = ship_info_lookup_sub("GTD Orion#Galatea");
12016 		if (idx >= 0)
12017 			return idx;
12018 
12019 		idx = ship_info_lookup_sub("GTD Orion (Galatea)");
12020 		if (idx >= 0)
12021 			return idx;
12022 
12023 		return -1;
12024 	}
12025 	else if (!stricmp(token, "GTD Orion#2 (Bastion)"))
12026 	{
12027 		idx = ship_info_lookup_sub("GTD Orion#Bastion");
12028 		if (idx >= 0)
12029 			return idx;
12030 
12031 		idx = ship_info_lookup_sub("GTD Orion (Bastion)");
12032 		if (idx >= 0)
12033 			return idx;
12034 
12035 		return -1;
12036 	}
12037 	else if (!stricmp(token, "SF Dragon#2 (weakened)"))
12038 	{
12039 		idx = ship_info_lookup_sub("SF Dragon#weakened");
12040 		if (idx >= 0)
12041 			return idx;
12042 
12043 		idx = ship_info_lookup_sub("SF Dragon (weakened)");
12044 		if (idx >= 0)
12045 			return idx;
12046 
12047 		return -1;
12048 	}
12049 	else if (!stricmp(token, "SF Dragon#3 (Player)"))
12050 	{
12051 		idx = ship_info_lookup_sub("SF Dragon#Terrans");
12052 		if (idx >= 0)
12053 			return idx;
12054 
12055 		idx = ship_info_lookup_sub("SF Dragon (Terrans)");
12056 		if (idx >= 0)
12057 			return idx;
12058 
12059 		return -1;
12060 	}
12061 	else if (!stricmp(token, "GTF Loki (stealth)"))
12062 	{
12063 		idx = ship_info_lookup_sub("GTF Loki#stealth");
12064 		if (idx >= 0)
12065 			return idx;
12066 
12067 		return -1;
12068 	}
12069 
12070 	// get first part of new string
12071 	strcpy_s(temp1, token);
12072 	end_string_at_first_hash_symbol(temp1);
12073 
12074 	// get second part
12075 	strcpy_s(temp2, p + 1);
12076 
12077 	// found a hash
12078 	if (*p == '#')
12079 	{
12080 		// assemble using parentheses
12081 		sprintf(name, "%s (%s)", temp1, temp2);
12082 	}
12083 	// found a parenthesis
12084 	else if (*p == '(')
12085 	{
12086 		// chop off right parenthesis (it exists because otherwise the left wouldn't have been flagged)
12087 		char *p2 = strchr(temp2, ')');
12088 		*p2 = '\0';
12089 
12090 		// assemble using hash
12091 		sprintf(name, "%s#%s", temp1, temp2);
12092 	}
12093 	// oops
12094 	else
12095 	{
12096 		Warning(LOCATION, "Unrecognized hash symbol.  Contact a programmer!");
12097 		return -1;
12098 	}
12099 
12100 	// finally check the new name
12101 	return ship_info_lookup_sub(name);
12102 }
12103 
12104 /**
12105  * Return the ship index of the ship with name *name.
12106  */
ship_name_lookup(const char * name,int inc_players)12107 int ship_name_lookup(const char *name, int inc_players)
12108 {
12109 	int	i;
12110 
12111 	// bogus
12112 	if(name == NULL){
12113 		return -1;
12114 	}
12115 
12116 	for (i=0; i<MAX_SHIPS; i++){
12117 		if (Ships[i].objnum >= 0){
12118 			if (Objects[Ships[i].objnum].type == OBJ_SHIP || (Objects[Ships[i].objnum].type == OBJ_START && inc_players)){
12119 				if (!stricmp(name, Ships[i].ship_name)){
12120 					return i;
12121 				}
12122 			}
12123 		}
12124 	}
12125 
12126 	// couldn't find it
12127 	return -1;
12128 }
12129 
ship_type_name_lookup(const char * name)12130 int ship_type_name_lookup(const char *name)
12131 {
12132 	// bogus
12133 	if(name == NULL || !strlen(name)){
12134 		return -1;
12135 	}
12136 
12137 	//Look through Ship_types array
12138 	uint max_size = Ship_types.size();
12139 	for(uint idx=0; idx < max_size; idx++){
12140 		if(!stricmp(name, Ship_types[idx].name)){
12141 			return idx;
12142 		}
12143 	}
12144 	// couldn't find it
12145 	return -1;
12146 }
12147 
12148 // checks the (arrival & departure) state of a ship.  Return values:
12149 // -1: has yet to arrive in mission
12150 //  0: is currently in mission
12151 //  1: has been destroyed, departed, or never existed
ship_query_state(char * name)12152 int ship_query_state(char *name)
12153 {
12154 	int i;
12155 
12156 	// bogus
12157 	Assert(name != NULL);
12158 	if(name == NULL){
12159 		return -1;
12160 	}
12161 
12162 	for (i=0; i<MAX_SHIPS; i++){
12163 		if (Ships[i].objnum >= 0){
12164 			if ((Objects[Ships[i].objnum].type == OBJ_SHIP) || (Objects[Ships[i].objnum].type == OBJ_START)){
12165 				if (!stricmp(name, Ships[i].ship_name)){
12166 					return 0;
12167 				}
12168 			}
12169 		}
12170 	}
12171 
12172 	if (mission_parse_get_arrival_ship(name))
12173 		return -1;
12174 
12175 	return 1;
12176 }
12177 
12178 // Finds the world position of a subsystem.
12179 // Return true/false for subsystem found/not found.
12180 // Stuff vector *pos with absolute position.
12181 // subsysp is a pointer to the subsystem.
get_subsystem_pos(vec3d * pos,object * objp,ship_subsys * subsysp)12182 int get_subsystem_pos(vec3d *pos, object *objp, ship_subsys *subsysp)
12183 {
12184 	if (subsysp == NULL) {
12185 		*pos = objp->pos;
12186 		return 0;
12187 	}
12188 
12189 	model_subsystem *mss = subsysp->system_info;
12190 
12191 	if (mss->subobj_num == -1) {
12192 		// If it's a special point subsys, we can use its offset directly
12193 
12194 		vm_vec_unrotate(pos, &subsysp->system_info->pnt, &objp->orient);
12195 		vm_vec_add2(pos, &objp->pos);
12196 	} else {
12197 		// Submodel subsystems may require a more complicated calculation
12198 
12199 		find_submodel_instance_world_point(pos, objp, mss->subobj_num);
12200 	}
12201 
12202 	return 1;
12203 }
12204 
12205 //=================================================
12206 // Takes all the angle info from the ship structure and stuffs it
12207 // into the model data so that the model code has all the correct
12208 // angles and stuff that it needs.    This is a poorly designed
12209 // system that should be re-engineered so that all the model functions
12210 // accept a list of angles and everyone passes them through, but
12211 // that would require some major code revision.
12212 // So, anytime you are using a model that has rotating parts, you
12213 // need to do a ship_model_start before any model_ functions are
12214 // called and a ship_model_stop after you're done.   Even for
12215 // collision detection and stuff, not just rendering.
12216 // See John for details.
12217 
ship_model_start(object * objp)12218 void ship_model_start(object *objp)
12219 {
12220 	model_subsystem	*psub;
12221 	ship		*shipp;
12222 	ship_subsys	*pss;
12223 	int model_num;
12224 
12225 	shipp = &Ships[objp->instance];
12226 	model_num = Ship_info[shipp->ship_info_index].model_num;
12227 
12228 	// First clear all the angles in the model to zero
12229 	model_clear_instance(model_num);
12230 
12231 	// Go through all subsystems and bash the model angles for all
12232 	// the subsystems that need it.
12233 	for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
12234 		psub = pss->system_info;
12235 		switch (psub->type) {
12236 			case SUBSYSTEM_RADAR:
12237 			case SUBSYSTEM_NAVIGATION:
12238 			case SUBSYSTEM_COMMUNICATION:
12239 			case SUBSYSTEM_UNKNOWN:
12240 			case SUBSYSTEM_ENGINE:
12241 			case SUBSYSTEM_SENSORS:
12242 			case SUBSYSTEM_WEAPONS:
12243 			case SUBSYSTEM_SOLAR:
12244 			case SUBSYSTEM_GAS_COLLECT:
12245 			case SUBSYSTEM_ACTIVATION:
12246 				break;
12247 			case SUBSYSTEM_TURRET:
12248 				Assertion( !(psub->flags & MSS_FLAG_ROTATES), "Turret %s on ship %s has the $rotate or $triggered subobject property defined. Please fix the model.\n", psub->name, Ship_info[shipp->ship_info_index].name ); // Turrets can't rotate!!! See John!
12249 				break;
12250 			default:
12251 				Error(LOCATION, "Illegal subsystem type.\n");
12252 		}
12253 
12254 		if ( psub->subobj_num >= 0 )	{
12255 			model_set_instance(model_num, psub->subobj_num, &pss->submodel_info_1, pss->flags );
12256 		}
12257 
12258 		if ( (psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >= 0) )		{
12259 			model_set_instance(model_num, psub->turret_gun_sobj, &pss->submodel_info_2, pss->flags );
12260 		}
12261 	}
12262 	model_do_dumb_rotation(model_num);
12263 }
12264 
12265 /**
12266  * Clears all the instance specific stuff out of the model info
12267  */
ship_model_stop(object * objp)12268 void ship_model_stop(object *objp)
12269 {
12270 	Assert(objp != NULL);
12271 	Assert(objp->instance >= 0);
12272 	Assert(objp->type == OBJ_SHIP);
12273 
12274 	// Then, clear all the angles in the model to zero
12275 	model_clear_instance(Ship_info[Ships[objp->instance].ship_info_index].model_num);
12276 }
12277 
12278 /**
12279  * Like ship_model_start but fills submodel instances instead of the submodels themselves
12280  */
ship_model_update_instance(object * objp)12281 void ship_model_update_instance(object *objp)
12282 {
12283 	model_subsystem	*psub;
12284 	ship		*shipp;
12285 	ship_subsys	*pss;
12286 	int model_instance_num;
12287 
12288 	Assert(objp != NULL);
12289 	Assert(objp->instance >= 0);
12290 	Assert(objp->type == OBJ_SHIP);
12291 
12292 	shipp = &Ships[objp->instance];
12293 	model_instance_num = shipp->model_instance_num;
12294 
12295 	// Then, clear all the angles in the model to zero
12296 	model_clear_submodel_instances(model_instance_num);
12297 
12298 	for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
12299 		psub = pss->system_info;
12300 		switch (psub->type) {
12301 			case SUBSYSTEM_RADAR:
12302 			case SUBSYSTEM_NAVIGATION:
12303 			case SUBSYSTEM_COMMUNICATION:
12304 			case SUBSYSTEM_UNKNOWN:
12305 			case SUBSYSTEM_ENGINE:
12306 			case SUBSYSTEM_SENSORS:
12307 			case SUBSYSTEM_WEAPONS:
12308 			case SUBSYSTEM_SOLAR:
12309 			case SUBSYSTEM_GAS_COLLECT:
12310 			case SUBSYSTEM_ACTIVATION:
12311 				break;
12312 			case SUBSYSTEM_TURRET:
12313 				Assertion( !(psub->flags & MSS_FLAG_ROTATES), "Turret %s on ship %s has the $rotate or $triggered subobject property defined. Please fix the model.\n", psub->name, Ship_info[shipp->ship_info_index].name ); // Turrets can't rotate!!! See John!
12314 				break;
12315 			default:
12316 				Error(LOCATION, "Illegal subsystem type.\n");
12317 		}
12318 
12319 		if ( psub->subobj_num >= 0 )	{
12320 			model_update_instance(model_instance_num, psub->subobj_num, &pss->submodel_info_1 );
12321 		}
12322 
12323 		if ( (psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >= 0) )		{
12324 			model_update_instance(model_instance_num, psub->turret_gun_sobj, &pss->submodel_info_2 );
12325 		}
12326 	}
12327 
12328 	model_instance_dumb_rotation(model_instance_num);
12329 
12330 	// preprocess subobject orientations for collision detection
12331 	model_collide_preprocess(&objp->orient, model_instance_num);
12332 }
12333 
12334 /**
12335  * Finds the number of crew points in a ship
12336  */
ship_find_num_crewpoints(object * objp)12337 int ship_find_num_crewpoints(object *objp)
12338 {
12339 	int n = 0;
12340 	model_subsystem	*psub;
12341 	ship		*shipp;
12342 	ship_subsys	*pss;
12343 
12344 	shipp = &Ships[objp->instance];
12345 
12346 	// Go through all subsystems and record the model angles for all
12347 	// the subsystems that need it.
12348 	for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
12349 		psub = pss->system_info;
12350 		switch (psub->type) {
12351 		case SUBSYSTEM_TURRET:
12352 			if ( psub->flags & MSS_FLAG_CREWPOINT )
12353 				n++; // fall through
12354 
12355 		case SUBSYSTEM_RADAR:
12356 		case SUBSYSTEM_NAVIGATION:
12357 		case SUBSYSTEM_COMMUNICATION:
12358 		case SUBSYSTEM_UNKNOWN:
12359 		case SUBSYSTEM_ENGINE:
12360 		case SUBSYSTEM_GAS_COLLECT:
12361 		case SUBSYSTEM_ACTIVATION:
12362 			break;
12363 		default:
12364 			Error(LOCATION, "Illegal subsystem type.\n");
12365 		}
12366 	}
12367 	return n;
12368 }
12369 
12370 /**
12371  * Finds the number of turrets in a ship
12372  */
ship_find_num_turrets(object * objp)12373 int ship_find_num_turrets(object *objp)
12374 {
12375 	int n = 0;
12376 	model_subsystem	*psub;
12377 	ship		*shipp;
12378 	ship_subsys	*pss;
12379 
12380 	shipp = &Ships[objp->instance];
12381 
12382 	// Go through all subsystems and record the model angles for all
12383 	// the subsystems that need it.
12384 	for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
12385 		psub = pss->system_info;
12386 		switch (psub->type) {
12387 		case SUBSYSTEM_TURRET:
12388 			n++; // drop through
12389 
12390 		case SUBSYSTEM_RADAR:
12391 		case SUBSYSTEM_NAVIGATION:
12392 		case SUBSYSTEM_COMMUNICATION:
12393 		case SUBSYSTEM_UNKNOWN:
12394 		case SUBSYSTEM_ENGINE:
12395 		case SUBSYSTEM_GAS_COLLECT:
12396 		case SUBSYSTEM_ACTIVATION:
12397 			break;
12398 		default:
12399 			Error(LOCATION, "Illegal subsystem type.\n");
12400 		}
12401 	}
12402 	return n;
12403 }
12404 
12405 //WMC
ship_set_eye(object * obj,int eye_index)12406 void ship_set_eye( object *obj, int eye_index)
12407 {
12408 	if(obj->type != OBJ_SHIP)
12409 		return;
12410 
12411 	ship *shipp = &Ships[obj->instance];
12412 
12413 	if(eye_index < 0)
12414 	{
12415 		shipp->current_viewpoint = -1;
12416 		return;
12417 	}
12418 
12419 	ship_info *sip = &Ship_info[shipp->ship_info_index];
12420 	if(sip->model_num < 0)
12421 		return;
12422 
12423 	polymodel *pm = model_get(sip->model_num);
12424 
12425 	if(pm == NULL || eye_index > pm->n_view_positions)
12426 		return;
12427 
12428 	shipp->current_viewpoint = eye_index;
12429 }
12430 
12431 // calculates the eye position for this ship in the global reference frame.  Uses the
12432 // view_positions array in the model.  The 0th element is the normal viewing position.
12433 // the vector of the eye is returned in the parameter 'eye'.  The orientation of the
12434 // eye is returned in orient.  (NOTE: this is kind of bogus for now since non 0th element
12435 // eyes have no defined up vector)
ship_get_eye(vec3d * eye_pos,matrix * eye_orient,object * obj,bool do_slew,bool from_origin)12436 void ship_get_eye( vec3d *eye_pos, matrix *eye_orient, object *obj, bool do_slew , bool from_origin)
12437 {
12438 	ship *shipp = &Ships[obj->instance];
12439 	polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
12440 
12441 	// check to be sure that we have a view eye to look at.....spit out nasty debug message
12442 	if ( shipp->current_viewpoint < 0 || pm->n_view_positions == 0 || shipp->current_viewpoint > pm->n_view_positions) {
12443 		*eye_pos = obj->pos;
12444 		*eye_orient = obj->orient;
12445 		return;
12446 	}
12447 
12448 	// eye points are stored in an array -- the normal viewing position for a ship is the current_eye_index
12449 	// element.
12450 	eye *ep = &(pm->view_positions[Ships[obj->instance].current_viewpoint]);
12451 
12452 	if (ep->parent >= 0 && pm->submodel[ep->parent].can_move) {
12453 		find_submodel_instance_point_orient(eye_pos, eye_orient, obj, ep->parent, &ep->pnt, &vmd_identity_matrix);
12454 		vec3d tvec = *eye_pos;
12455 		vm_vec_unrotate(eye_pos, &tvec, &obj->orient);
12456 		vm_vec_add2(eye_pos, &obj->pos);
12457 
12458 		matrix tempmat = *eye_orient;
12459 		vm_matrix_x_matrix(eye_orient, &obj->orient, &tempmat);
12460 	} else {
12461 		model_find_world_point( eye_pos, &ep->pnt, pm->id, ep->parent, &obj->orient, from_origin ? &vmd_zero_vector : &obj->pos );
12462 		*eye_orient = obj->orient;
12463 	}
12464 
12465 	//	Modify the orientation based on head orientation.
12466 	if ( Viewer_obj == obj && do_slew) {
12467 		// Add the cockpit leaning translation offset
12468 		vm_vec_add2(eye_pos,&leaning_position);
12469 		compute_slew_matrix(eye_orient, &Viewer_slew_angles);
12470 	}
12471 }
12472 
12473 // of attackers to make this decision.
12474 //
12475 // NOTE: This function takes into account how many ships are attacking a subsystem, and will
12476 //			prefer an ignored subsystem over a subsystem that is in line of sight, if the in-sight
12477 //			subsystem is attacked by more than MAX_SUBSYS_ATTACKERS
12478 // input:
12479 //				sp					=>		ship pointer to parent of subsystem
12480 //				subsys_type		=>		what kind of subsystem this is
12481 //				attacker_pos	=>		the world coords of the attacker of this subsystem
12482 //
12483 // returns: pointer to subsystem if one found, NULL otherwise
12484 #define MAX_SUBSYS_ATTACKERS 3
ship_get_best_subsys_to_attack(ship * sp,int subsys_type,vec3d * attacker_pos)12485 ship_subsys *ship_get_best_subsys_to_attack(ship *sp, int subsys_type, vec3d *attacker_pos)
12486 {
12487 	ship_subsys	*ss;
12488 	ship_subsys *best_in_sight_subsys, *lowest_attacker_subsys, *ss_return;
12489 	int			lowest_num_attackers, lowest_in_sight_attackers, num_attackers;
12490 	vec3d		gsubpos;
12491 	ship_obj		*sop;
12492 
12493 	lowest_in_sight_attackers = lowest_num_attackers = 1000;
12494 	ss_return = best_in_sight_subsys = lowest_attacker_subsys = NULL;
12495 
12496 	for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
12497 		if ( (ss->system_info->type == subsys_type) && (ss->current_hits > 0) ) {
12498 
12499 			// get world pos of subsystem
12500 			vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &Objects[sp->objnum].orient);
12501 			vm_vec_add2(&gsubpos, &Objects[sp->objnum].pos);
12502 
12503 			// now find the number of ships attacking this subsystem by iterating through the ships list,
12504 			// and checking if aip->targeted_subsys matches the subsystem we're checking
12505 			num_attackers = 0;
12506 			sop = GET_FIRST(&Ship_obj_list);
12507 			while(sop != END_OF_LIST(&Ship_obj_list)){
12508 				if ( Ai_info[Ships[Objects[sop->objnum].instance].ai_index].targeted_subsys == ss ) {
12509 					num_attackers++;
12510 				}
12511 				sop = GET_NEXT(sop);
12512 			}
12513 
12514 			if ( num_attackers < lowest_num_attackers ) {
12515 				lowest_num_attackers = num_attackers;
12516 				lowest_attacker_subsys = ss;
12517 			}
12518 
12519 			if ( ship_subsystem_in_sight(&Objects[sp->objnum], ss, attacker_pos, &gsubpos) ) {
12520 				if ( num_attackers < lowest_in_sight_attackers ) {
12521 					lowest_in_sight_attackers = num_attackers;
12522 					best_in_sight_subsys = ss;
12523 				}
12524 			}
12525 		}
12526 	}
12527 
12528 	if ( best_in_sight_subsys == NULL ) {
12529 		// no subsystems are in sight, so return the subsystem with the lowest # of attackers
12530 		ss_return =  lowest_attacker_subsys;
12531 	} else {
12532 		if ( lowest_in_sight_attackers > MAX_SUBSYS_ATTACKERS ) {
12533 			ss_return = lowest_attacker_subsys;
12534 		} else {
12535 			ss_return =  best_in_sight_subsys;
12536 		}
12537 	}
12538 
12539 	return ss_return;
12540 }
12541 
12542 // function to return a pointer to the 'nth' ship_subsys structure in a ship's linked list
12543 // of ship_subsys'.
12544 // attacker_pos	=>	world pos of attacker (default value NULL).  If value is non-NULL, try
12545 //							to select the best subsystem to attack of that type (using line-of-sight)
12546 //							and based on the number of ships already attacking the subsystem
ship_get_indexed_subsys(ship * sp,int index,vec3d * attacker_pos)12547 ship_subsys *ship_get_indexed_subsys( ship *sp, int index, vec3d *attacker_pos )
12548 {
12549 	int count;
12550 	ship_subsys *ss;
12551 
12552 	// first, special code to see if the index < 0.  If so, we are looking for one of several possible
12553 	// engines or one of several possible turrets.  If we enter this if statement, we will always return
12554 	// something.
12555 	if ( index < 0 ) {
12556 		int subsys_type;
12557 
12558 		subsys_type = -index;
12559 		if ( sp->subsys_info[subsys_type].aggregate_current_hits <= 0.0f )		// if there are no hits, no subsystem to attack.
12560 			return NULL;
12561 
12562 		if ( attacker_pos != NULL ) {
12563 			ss = ship_get_best_subsys_to_attack(sp, subsys_type, attacker_pos);
12564 			return ss;
12565 		} else {
12566 			// next, scan the list of subsystems and search for the first subsystem of the particular
12567 			// type which has > 0 hits remaining.
12568 			for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
12569 				if ( (ss->system_info->type == subsys_type) && (ss->current_hits > 0) )
12570 					return ss;
12571 			}
12572 		}
12573 
12574 		// maybe we shouldn't get here, but with possible floating point rounding, I suppose we could
12575 		Warning(LOCATION, "Unable to get a nonspecific subsystem of index %d on ship %s!", index, sp->ship_name);
12576 		return NULL;
12577 	}
12578 
12579 
12580 	count = 0;
12581 	ss = GET_FIRST(&sp->subsys_list);
12582 	while ( ss != END_OF_LIST( &sp->subsys_list ) ) {
12583 		if ( count == index )
12584 			return ss;
12585 		count++;
12586 		ss = GET_NEXT( ss );
12587 	}
12588 
12589 	// get allender -- turret ref didn't fixup correctly!!!!
12590 	Warning(LOCATION, "In ship_get_indexed_subsys, unable to get a subsystem of index %d on ship %s, due to a broken subsystem reference!  This is most likely due to a table/model mismatch.", index, sp->ship_name);
12591 	return NULL;
12592 }
12593 
12594 /**
12595  * Given a pointer to a subsystem and an associated object, return the index.
12596  */
ship_get_index_from_subsys(ship_subsys * ssp,int objnum,int error_bypass)12597 int ship_get_index_from_subsys(ship_subsys *ssp, int objnum, int error_bypass)
12598 {
12599 	if (ssp == NULL)
12600 		return -1;
12601 	else {
12602 		int	count;
12603 		ship	*shipp;
12604 		ship_subsys	*ss;
12605 
12606 		Assert(objnum >= 0);
12607 		Assert(Objects[objnum].instance >= 0);
12608 
12609 		shipp = &Ships[Objects[objnum].instance];
12610 
12611 		count = 0;
12612 		ss = GET_FIRST(&shipp->subsys_list);
12613 		while ( ss != END_OF_LIST( &shipp->subsys_list ) ) {
12614 			if ( ss == ssp)
12615 				return count;
12616 			count++;
12617 			ss = GET_NEXT( ss );
12618 		}
12619 		if ( !error_bypass )
12620 			Int3();			// get allender -- turret ref didn't fixup correctly!!!!
12621 		return -1;
12622 	}
12623 }
12624 
12625 /**
12626  * Returns the index number of the ship_subsys parameter
12627  */
ship_get_subsys_index(ship * sp,char * ss_name,int error_bypass)12628 int ship_get_subsys_index(ship *sp, char *ss_name, int error_bypass)
12629 {
12630 	int count;
12631 	ship_subsys *ss;
12632 
12633 	count = 0;
12634 	ss = GET_FIRST(&sp->subsys_list);
12635 	while ( ss != END_OF_LIST( &sp->subsys_list ) ) {
12636 		if ( !subsystem_stricmp(ss->system_info->subobj_name, ss_name) )
12637 			return count;
12638 		count++;
12639 		ss = GET_NEXT( ss );
12640 	}
12641 
12642 	if (!error_bypass)
12643 		Int3();
12644 
12645 	return -1;
12646 }
12647 
12648 // routine to return the strength of a subsystem.  We keep a total hit tally for all subsystems
12649 // which are similar (i.e. a total for all engines).  These routines will return a number between
12650 // 0.0 and 1.0 which is the relative combined strength of the given subsystem type.  The number
12651 // calculated for the engines is slightly different.  Once an engine reaches < 15% of its hits, its
12652 // output drops to that %.  A dead engine has no output.
ship_get_subsystem_strength(ship * shipp,int type)12653 float ship_get_subsystem_strength( ship *shipp, int type )
12654 {
12655 	float strength;
12656 	ship_subsys *ssp;
12657 
12658 	Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
12659 
12660 	//	For a dying ship, all subsystem strengths are zero.
12661 	if (Objects[shipp->objnum].hull_strength <= 0.0f)
12662 		return 0.0f;
12663 
12664 	// short circuit 1
12665 	if (shipp->subsys_info[type].aggregate_max_hits <= 0.0f)
12666 		return 1.0f;
12667 
12668 	// short circuit 0
12669 	if (shipp->subsys_info[type].aggregate_current_hits <= 0.0f)
12670 		return 0.0f;
12671 
12672 	strength = shipp->subsys_info[type].aggregate_current_hits / shipp->subsys_info[type].aggregate_max_hits;
12673 	Assert( strength != 0.0f );
12674 
12675 	if ( (type == SUBSYSTEM_ENGINE) && (strength < 1.0f) ) {
12676 		float percent;
12677 
12678 		percent = 0.0f;
12679 		ssp = GET_FIRST(&shipp->subsys_list);
12680 		while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
12681 
12682 			if ( ssp->system_info->type == SUBSYSTEM_ENGINE ) {
12683 				float ratio;
12684 
12685 				ratio = ssp->current_hits / ssp->max_hits;
12686 				if ( ratio < ENGINE_MIN_STR )
12687 					ratio = ENGINE_MIN_STR;
12688 
12689 				percent += ratio;
12690 			}
12691 			ssp = GET_NEXT( ssp );
12692 		}
12693 		strength = percent / (float)shipp->subsys_info[type].type_count;
12694 	}
12695 
12696 	return strength;
12697 }
12698 
12699 /**
12700  * Set the strength of a subsystem on a given ship.
12701  *
12702  * The strength passed as a parameter is between 0.0 and 1.0
12703  *
12704  * NOTE: this function was made to be called by the debug function dcf_set_subsys().  If
12705  * you want to use this, be sure that you test it for all cases.
12706  */
ship_set_subsystem_strength(ship * shipp,int type,float strength)12707 void ship_set_subsystem_strength( ship *shipp, int type, float strength )
12708 {
12709 	float total_current_hits, diff;
12710 	ship_subsys *ssp;
12711 
12712 	Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
12713 	if ( shipp->subsys_info[type].aggregate_max_hits <= 0.0f )
12714 		return;
12715 
12716 	total_current_hits = 0.0f;
12717 	ssp = GET_FIRST(&shipp->subsys_list);
12718 	while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
12719 
12720 		if ( (ssp->system_info->type == type) && !(ssp->flags & SSF_NO_AGGREGATE) ) {
12721 			ssp->current_hits = strength * ssp->max_hits;
12722 			total_current_hits += ssp->current_hits;
12723 		}
12724 		ssp = GET_NEXT( ssp );
12725 	}
12726 
12727 	// update the objects integrity, needed since we've bashed the strength of a subsysem
12728 	diff = total_current_hits - shipp->subsys_info[type].aggregate_current_hits;
12729 	Objects[shipp->objnum].hull_strength += diff;
12730 	// fix up the shipp->subsys_info[type] aggregate_current_hits value
12731 	shipp->subsys_info[type].aggregate_current_hits = total_current_hits;
12732 }
12733 
12734 #define		SHIELD_REPAIR_RATE	0.20f			//	Percent of shield repaired per second.
12735 #define		HULL_REPAIR_RATE		0.15f			//	Percent of hull repaired per second.
12736 #define		SUBSYS_REPAIR_RATE	0.10f			// Percent of subsystems repaired per second.
12737 
12738 #define REARM_NUM_MISSILES_PER_BATCH 4		// how many missiles are dropped in per load sound
12739 #define REARM_NUM_BALLISTIC_PRIMARIES_PER_BATCH	100	// how many bullets are dropped in per load sound
12740 
12741 /**
12742  * Calculates approximate time in seconds it would take to rearm and repair object.
12743  */
ship_calculate_rearm_duration(object * objp)12744 float ship_calculate_rearm_duration( object *objp )
12745 {
12746 	ship* sp;
12747 	ship_info* sip;
12748 	ship_subsys* ssp;
12749 	ship_weapon* swp;
12750 	weapon_info* wip;
12751 
12752 	float shield_rep_time = 0;
12753 	float subsys_rep_time = 0;
12754 	float hull_rep_time = 0;
12755 	float prim_rearm_time = 0;
12756 	float sec_rearm_time = 0;
12757 
12758 	float max_hull_repair;
12759 	float max_subsys_repair;
12760 
12761 	int i;
12762 	int num_reloads;
12763 
12764 	bool found_first_empty;
12765 
12766 	Assert(objp->type == OBJ_SHIP);
12767 
12768 	sp = &Ships[objp->instance];
12769 	swp = &sp->weapons;
12770 	sip = &Ship_info[sp->ship_info_index];
12771 
12772 	//find out time to repair shields
12773 	if(sip->sup_shield_repair_rate > 0.0f)
12774 		shield_rep_time = (sp->ship_max_shield_strength - shield_get_strength(objp)) / (sp->ship_max_shield_strength * sip->sup_shield_repair_rate);
12775 
12776 	max_hull_repair = sp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f);
12777 	if ((The_mission.flags & MISSION_FLAG_SUPPORT_REPAIRS_HULL) && (max_hull_repair > objp->hull_strength) && (sip->sup_hull_repair_rate > 0.0f))
12778 	{
12779 		hull_rep_time = (max_hull_repair - objp->hull_strength) / (sp->ship_max_hull_strength * sip->sup_hull_repair_rate);
12780 	}
12781 
12782 	//caluclate subsystem repair time
12783 	ssp = GET_FIRST(&sp->subsys_list);
12784 	while (ssp != END_OF_LIST(&sp->subsys_list))
12785 	{
12786 		max_subsys_repair = ssp->max_hits * (The_mission.support_ships.max_subsys_repair_val * 0.01f);
12787 		if ((max_subsys_repair > ssp->current_hits) && (sip->sup_hull_repair_rate > 0.0f))
12788 		{
12789 			subsys_rep_time += (max_subsys_repair - ssp->current_hits) / (ssp->max_hits * sip->sup_subsys_repair_rate);
12790 		}
12791 
12792 		ssp = GET_NEXT( ssp );
12793 	}
12794 
12795 	//now do the primary rearm time
12796 	found_first_empty = false;
12797 	if (sip->flags & SIF_BALLISTIC_PRIMARIES)
12798 	{
12799 		for (i = 0; i < swp->num_primary_banks; i++)
12800 		{
12801 			wip = &Weapon_info[swp->primary_bank_weapons[i]];
12802 			if (wip->wi_flags2 & WIF2_BALLISTIC)
12803 			{
12804 				//check how many full reloads we need
12805 				num_reloads = (swp->primary_bank_start_ammo[i] - swp->primary_bank_ammo[i])/REARM_NUM_BALLISTIC_PRIMARIES_PER_BATCH;
12806 
12807 				//take into account a fractional reload
12808 				if ((swp->primary_bank_start_ammo[i] - swp->primary_bank_ammo[i]) % REARM_NUM_BALLISTIC_PRIMARIES_PER_BATCH != 0)
12809 				{
12810 					num_reloads++;
12811 				}
12812 
12813 				//don't factor in the time it takes for the first reload, since that is loaded instantly
12814 				num_reloads--;
12815 
12816 				if (num_reloads < 0) continue;
12817 
12818 				if (!found_first_empty && (swp->primary_bank_start_ammo[i] - swp->primary_bank_ammo[i]))
12819 				{
12820 					found_first_empty = true;
12821 					prim_rearm_time += (float)snd_get_duration(Snds[SND_MISSILE_START_LOAD].id) / 1000.0f;
12822 				}
12823 
12824 				prim_rearm_time += num_reloads * wip->rearm_rate;
12825 			}
12826 		}
12827 	}
12828 
12829 	//and on to secondary rearm time
12830 	found_first_empty = false;
12831 	for (i = 0; i < swp->num_secondary_banks; i++)
12832 	{
12833 			wip = &Weapon_info[swp->secondary_bank_weapons[i]];
12834 
12835 			//check how many full reloads we need
12836 			num_reloads = (swp->secondary_bank_start_ammo[i] - swp->secondary_bank_ammo[i])/REARM_NUM_MISSILES_PER_BATCH;
12837 
12838 			//take into account a fractional reload
12839 			if ((swp->secondary_bank_start_ammo[i] - swp->secondary_bank_ammo[i]) % REARM_NUM_MISSILES_PER_BATCH != 0)
12840 			{
12841 				num_reloads++;
12842 			}
12843 
12844 			//don't factor in the time it takes for the first reload, since that is loaded instantly
12845 			num_reloads--;
12846 
12847 			if (num_reloads < 0) continue;
12848 
12849 			if (!found_first_empty && (swp->secondary_bank_start_ammo[i] - swp->secondary_bank_ammo[i]))
12850 			{
12851 				found_first_empty = true;
12852 				sec_rearm_time += (float)snd_get_duration(Snds[SND_MISSILE_START_LOAD].id) / 1000.0f;
12853 			}
12854 
12855 			sec_rearm_time += num_reloads * wip->rearm_rate;
12856 	}
12857 
12858 	//sum them up and you've got an estimated rearm time.
12859 	//add 1.2 to compensate for release delay
12860 	return shield_rep_time + hull_rep_time + subsys_rep_time + prim_rearm_time + sec_rearm_time + 1.2f;
12861 }
12862 
12863 
12864 
12865 // ==================================================================================
12866 // ship_do_rearm_frame()
12867 //
12868 // function to rearm a ship.  This function gets called from the ai code ai_do_rearm_frame (or
12869 // some function of a similar name).  Returns 1 when ship is fully repaired and rearmed, 0 otherwise
12870 //
ship_do_rearm_frame(object * objp,float frametime)12871 int ship_do_rearm_frame( object *objp, float frametime )
12872 {
12873 	int			i, banks_full, primary_banks_full, subsys_type, subsys_all_ok, last_ballistic_idx = 0;
12874 	float			shield_str, repair_delta, repair_allocated, max_hull_repair, max_subsys_repair;
12875 	ship			*shipp;
12876 	ship_weapon	*swp;
12877 	ship_info	*sip;
12878 	ship_subsys	*ssp;
12879 	ai_info		*aip;
12880 
12881 	shipp = &Ships[objp->instance];
12882 	swp = &shipp->weapons;
12883 	sip = &Ship_info[shipp->ship_info_index];
12884 	aip = &Ai_info[shipp->ai_index];
12885 
12886 	// AL 10-31-97: Add missing primary weapons to the ship.  This is required since designers
12887 	//              want to have ships that start with no primaries, but can get them through
12888 	//					 rearm/repair
12889 	if ( swp->num_primary_banks < sip->num_primary_banks ) {
12890 		for ( i = swp->num_primary_banks; i < sip->num_primary_banks; i++ ) {
12891 			swp->primary_bank_weapons[i] = sip->primary_bank_weapons[i];
12892 		}
12893 		swp->num_primary_banks = sip->num_primary_banks;
12894 	}
12895 	// AL 12-30-97: Repair broken warp drive
12896 	if ( shipp->flags & SF_WARP_BROKEN ) {
12897 		// TODO: maybe do something here like informing player warp is fixed?
12898 		// like this? -- Goober5000
12899 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Subspace drive repaired.", 1635));
12900 		shipp->flags &= ~SF_WARP_BROKEN;
12901 	}
12902 
12903 	// AL 1-16-97: Replenish countermeasures
12904 	shipp->cmeasure_count = sip->cmeasure_max;
12905 
12906 	// Do shield repair here
12907 	if ( !(objp->flags & OF_NO_SHIELDS) )
12908 	{
12909 		shield_str = shield_get_strength(objp);
12910 		if ( shield_str < shipp->ship_max_shield_strength ) {
12911 			if ( objp == Player_obj ) {
12912 				player_maybe_start_repair_sound();
12913 			}
12914 			shield_str += shipp->ship_max_shield_strength * frametime * sip->sup_shield_repair_rate;
12915 			if ( shield_str > shipp->ship_max_shield_strength ) {
12916 				 shield_str = shipp->ship_max_shield_strength;
12917 			}
12918 			shield_set_strength(objp, shield_str);
12919 		}
12920 	}
12921 
12922 	// Repair the ship integrity (subsystems + hull).  This works by applying the repair points
12923 	// to the subsystems.  Ships integrity is stored is objp->hull_strength, so that always is
12924 	// incremented by repair_allocated
12925 	repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_hull_repair_rate;
12926 
12927 //	AL 11-24-97: remove increase to hull integrity
12928 //	Comments removed by PhReAk; Note that this is toggled on/off with a mission flag
12929 
12930 	//Figure out how much of the ship's hull we can repair
12931 	max_hull_repair = shipp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f);
12932 
12933 	//Don't "reverse-repair" the hull if it's already above the max repair threshold
12934 	if (objp->hull_strength > max_hull_repair)
12935 	{
12936 		max_hull_repair = objp->hull_strength;
12937 	}
12938 
12939 	if(The_mission.flags & MISSION_FLAG_SUPPORT_REPAIRS_HULL)
12940 	{
12941 		objp->hull_strength += repair_allocated;
12942 		if ( objp->hull_strength > max_hull_repair ) {
12943 			objp->hull_strength = max_hull_repair;
12944 		}
12945 
12946 		if ( objp->hull_strength > shipp->ship_max_hull_strength )
12947 		{
12948 			objp->hull_strength = shipp->ship_max_hull_strength;
12949 			repair_allocated -= ( shipp->ship_max_hull_strength - objp->hull_strength);
12950 		}
12951 	}
12952 
12953 	// figure out repairs for subsystems
12954 	if(repair_allocated > 0) {
12955 		if(sip->sup_subsys_repair_rate == 0.0f)
12956 			repair_allocated = 0.0f;
12957 		else if(sip->sup_hull_repair_rate == 0.0f)
12958 			repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_subsys_repair_rate;
12959 		else if(!(sip->sup_hull_repair_rate == sip->sup_subsys_repair_rate))
12960 			repair_allocated = repair_allocated * sip->sup_subsys_repair_rate / sip->sup_hull_repair_rate;
12961 	}
12962 
12963 	// check the subsystems of the ship.
12964 	subsys_all_ok = 1;
12965 	ssp = GET_FIRST(&shipp->subsys_list);
12966 	while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
12967 		//Figure out how much we *can* repair the current subsystem -C
12968 		max_subsys_repair = ssp->max_hits * (The_mission.support_ships.max_subsys_repair_val * 0.01f);
12969 
12970 		if ( ssp->current_hits < max_subsys_repair && repair_allocated > 0 ) {
12971 			subsys_all_ok = 0;
12972 			subsys_type = ssp->system_info->type;
12973 
12974 			if ( objp == Player_obj ) {
12975 				player_maybe_start_repair_sound();
12976 			}
12977 
12978 			repair_delta = max_subsys_repair - ssp->current_hits;
12979 			if ( repair_delta > repair_allocated ) {
12980 				repair_delta = repair_allocated;
12981 			}
12982 			repair_allocated -= repair_delta;
12983 			Assert(repair_allocated >= 0.0f);
12984 
12985 			// add repair to current strength of single subsystem
12986 			ssp->current_hits += repair_delta;
12987 			if ( ssp->current_hits > max_subsys_repair ) {
12988 				ssp->current_hits = max_subsys_repair;
12989 			}
12990 
12991 			// add repair to aggregate strength of subsystems of that type
12992 			if (!(ssp->flags & SSF_NO_AGGREGATE)) {
12993 				shipp->subsys_info[subsys_type].aggregate_current_hits += repair_delta;
12994 				if ( shipp->subsys_info[subsys_type].aggregate_current_hits > shipp->subsys_info[subsys_type].aggregate_max_hits )
12995 					shipp->subsys_info[subsys_type].aggregate_current_hits = shipp->subsys_info[subsys_type].aggregate_max_hits;
12996 			}
12997 
12998 			// check to see if this subsystem was totally non functional before -- if so, then
12999 			// reset the flags
13000 			if ( (ssp->system_info->type == SUBSYSTEM_ENGINE) && (shipp->flags & SF_DISABLED) ) {
13001 				shipp->flags &= ~SF_DISABLED;
13002 				ship_reset_disabled_physics(objp, shipp->ship_info_index);
13003 			}
13004 			break;
13005 		}
13006 		ssp = GET_NEXT( ssp );
13007 	}
13008 
13009 	// now deal with rearming the player.  All secondary weapons have a certain rate at which
13010 	// they can be rearmed.  We can rearm multiple banks at once.
13011 	banks_full = 0;
13012 	primary_banks_full = 0;
13013 	if ( subsys_all_ok )
13014 	{
13015 		for (i = 0; i < swp->num_secondary_banks; i++ )
13016 		{
13017 			// Actual loading of missiles is preceded by a sound effect which is the missile
13018 			// loading equipment moving into place
13019 			if ( aip->rearm_first_missile == TRUE )
13020 			{
13021 				swp->secondary_bank_rearm_time[i] = timestamp(snd_get_duration(Snds[SND_MISSILE_START_LOAD].id));
13022 
13023 				if (i == swp->num_secondary_banks - 1)
13024 					aip->rearm_first_missile = FALSE;
13025 			}
13026 
13027 			if ( swp->secondary_bank_ammo[i] < swp->secondary_bank_start_ammo[i] )
13028 			{
13029 				float rearm_time;
13030 
13031 				if ( objp == Player_obj )
13032 				{
13033 					hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
13034 				}
13035 
13036 				if ( timestamp_elapsed(swp->secondary_bank_rearm_time[i]) )
13037 				{
13038 					rearm_time = Weapon_info[swp->secondary_bank_weapons[i]].rearm_rate;
13039 					swp->secondary_bank_rearm_time[i] = timestamp((int)(rearm_time * 1000.0f));
13040 
13041 					snd_play_3d( &Snds[SND_MISSILE_LOAD], &objp->pos, &View_position );
13042 					if (objp == Player_obj)
13043 						joy_ff_play_reload_effect();
13044 
13045 					swp->secondary_bank_ammo[i] += REARM_NUM_MISSILES_PER_BATCH;
13046 					if ( swp->secondary_bank_ammo[i] > swp->secondary_bank_start_ammo[i] )
13047 					{
13048 						swp->secondary_bank_ammo[i] = swp->secondary_bank_start_ammo[i];
13049 					}
13050 				}
13051 				else
13052 				{
13053 				}
13054 			}
13055 			else
13056 			{
13057 				banks_full++;
13058 			}
13059 
13060 			if ((aip->rearm_first_missile == TRUE) && (i == swp->num_secondary_banks - 1) && (banks_full != swp->num_secondary_banks))
13061 					snd_play_3d( &Snds[SND_MISSILE_START_LOAD], &objp->pos, &View_position );
13062 		}	// end for
13063 
13064 		// rearm ballistic primaries - Goober5000
13065 		if (sip->flags & SIF_BALLISTIC_PRIMARIES)
13066 		{
13067 			if ( aip->rearm_first_ballistic_primary == TRUE)
13068 			{
13069 				for (i = 1; i < swp->num_primary_banks; i++ )
13070 				{
13071 					if ( Weapon_info[swp->primary_bank_weapons[i]].wi_flags2 & WIF2_BALLISTIC )
13072 						last_ballistic_idx = i;
13073 				}
13074 			}
13075 
13076 			for (i = 0; i < swp->num_primary_banks; i++ )
13077 			{
13078 				if ( Weapon_info[swp->primary_bank_weapons[i]].wi_flags2 & WIF2_BALLISTIC )
13079 				{
13080 					// Actual loading of bullets is preceded by a sound effect which is the bullet
13081 					// loading equipment moving into place
13082 					if ( aip->rearm_first_ballistic_primary == TRUE )
13083 					{
13084 						// Goober5000
13085 						int sound_index;
13086 						if (Snds[SND_BALLISTIC_START_LOAD].id >= 0)
13087 							sound_index = SND_BALLISTIC_START_LOAD;
13088 						else
13089 							sound_index = SND_MISSILE_START_LOAD;
13090 
13091 						swp->primary_bank_rearm_time[i] = timestamp(snd_get_duration(Snds[sound_index].id));
13092 
13093 						if (i == last_ballistic_idx)
13094 							aip->rearm_first_ballistic_primary = FALSE;
13095 					}
13096 
13097 					if ( swp->primary_bank_ammo[i] < swp->primary_bank_start_ammo[i] )
13098 					{
13099 						float rearm_time;
13100 
13101 						if ( objp == Player_obj )
13102 						{
13103 							hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
13104 						}
13105 
13106 						if ( timestamp_elapsed(swp->primary_bank_rearm_time[i]) )
13107 						{
13108 							rearm_time = Weapon_info[swp->primary_bank_weapons[i]].rearm_rate;
13109 							swp->primary_bank_rearm_time[i] = timestamp( (int)(rearm_time * 1000.f) );
13110 
13111 							// Goober5000
13112 							int sound_index;
13113 							if (Snds[SND_BALLISTIC_LOAD].id >= 0)
13114 								sound_index = SND_BALLISTIC_LOAD;
13115 							else
13116 								sound_index = SND_MISSILE_LOAD;
13117 
13118 							snd_play_3d( &Snds[sound_index], &objp->pos, &View_position );
13119 
13120 							swp->primary_bank_ammo[i] += REARM_NUM_BALLISTIC_PRIMARIES_PER_BATCH;
13121 							if ( swp->primary_bank_ammo[i] > swp->primary_bank_start_ammo[i] )
13122 							{
13123 								swp->primary_bank_ammo[i] = swp->primary_bank_start_ammo[i];
13124 							}
13125 						}
13126 					}
13127 					else
13128 					{
13129 						primary_banks_full++;
13130 					}
13131 				}
13132 				// if the bank is not a ballistic
13133 				else
13134 				{
13135 					primary_banks_full++;
13136 				}
13137 
13138 				if ((aip->rearm_first_ballistic_primary == TRUE) && (i == swp->num_primary_banks - 1) && (primary_banks_full != swp->num_primary_banks))
13139 				{
13140 					// Goober5000
13141 					int sound_index;
13142 					if (Snds[SND_BALLISTIC_START_LOAD].id >= 0)
13143 						sound_index = SND_BALLISTIC_START_LOAD;
13144 					else
13145 						sound_index = SND_MISSILE_START_LOAD;
13146 
13147 					snd_play_3d( &Snds[sound_index], &objp->pos, &View_position );
13148 				}
13149 			}	// end for
13150 		}	// end if - rearm ballistic primaries
13151 	} // end if (subsys_all_ok)
13152 
13153 	if ( banks_full == swp->num_secondary_banks )
13154 	{
13155 		aip->rearm_first_missile = TRUE;
13156 	}
13157 
13158 	if ( primary_banks_full == swp->num_primary_banks )
13159 	{
13160 		aip->rearm_first_ballistic_primary = TRUE;
13161 	}
13162 
13163 	int shields_full = 0;
13164 	if ( (objp->flags & OF_NO_SHIELDS) ) {
13165 		shields_full = 1;
13166 	} else {
13167 		if ( shield_get_strength(objp) >= shipp->ship_max_shield_strength )
13168 			shields_full = 1;
13169 		if (sip->sup_shield_repair_rate == 0.0f)
13170 			shields_full = 1;
13171 	}
13172 
13173 	int hull_ok = 0;
13174 	if(objp->hull_strength >= max_hull_repair)
13175 		hull_ok = 1;
13176 
13177 	if(sip->sup_hull_repair_rate == 0.0f) {
13178 		subsys_all_ok = 1;
13179 		hull_ok = 1;
13180 	}
13181 
13182 	// return 1 if at end of subsystem list, hull damage at 0, and shields full and all secondary banks full.
13183 	if ( (subsys_all_ok && shields_full && (The_mission.flags & MISSION_FLAG_SUPPORT_REPAIRS_HULL) && hull_ok ) || (subsys_all_ok && shields_full && !(The_mission.flags & MISSION_FLAG_SUPPORT_REPAIRS_HULL) ) )
13184 	{
13185 		if ( objp == Player_obj ) {
13186 			player_stop_repair_sound();
13187 		}
13188 
13189 		if (!aip->rearm_release_delay)
13190 			aip->rearm_release_delay = timestamp(1200);
13191 
13192 		// check both primary and secondary banks are full
13193 		if ( (banks_full == swp->num_secondary_banks) &&
13194 			( !(sip->flags & SIF_BALLISTIC_PRIMARIES) || ((sip->flags & SIF_BALLISTIC_PRIMARIES) && (primary_banks_full == swp->num_primary_banks)) )	)
13195 		{
13196 			if ( timestamp_elapsed(aip->rearm_release_delay) )
13197 				return 1;
13198 		}
13199 		else
13200 		{
13201 			aip->rearm_release_delay = timestamp(1200);
13202 		}
13203 	}
13204 
13205 	if (objp == Player_obj) Player_rearm_eta -= frametime;
13206 
13207 	return 0;
13208 }
13209 
13210 // Goober5000 - modified the logic to clarify the various states
13211 // function which is used to find a repair ship to repair requester_obj.  the way repair ships will work is:
13212 // if no ships in the mission at all, return 0
13213 // if a ship can immediately satisfy a repair request, return 1 and fill in the pointer
13214 // if no ships can satisfy a request, but we haven't reached either the concurrent or cumulative limit, return 2
13215 // if no ships can satisfy a request, and we've reached the limits, but a request can be queued, return 3 and fill in the pointer
13216 // if no ships can satisfy a request, we've reached the limits, and we can't queue anything, we're out of luck -- return 4
ship_find_repair_ship(object * requester_obj,object ** ship_we_found)13217 int ship_find_repair_ship( object *requester_obj, object **ship_we_found )
13218 {
13219 	object *objp;
13220 	int num_support_ships = 0;
13221 	float min_dist = -1.0f;
13222 	object *nearest_support_ship = NULL;
13223 	float min_time_till_available = -1.0f;
13224 	object *soonest_available_support_ship = NULL;
13225 
13226 	Assertion(requester_obj->type == OBJ_SHIP, "requester_obj not a ship. Has type of %08x", requester_obj->type);
13227 	Assertion((requester_obj->instance >= 0) && (requester_obj->instance < MAX_SHIPS),
13228 		"requester_obj does not have a valid pointer to a ship. Pointer is %d, which is smaller than 0 or bigger than %d",
13229 		requester_obj->instance, MAX_SHIPS);
13230 
13231 	ship *requester_ship = &Ships[requester_obj->instance];
13232 	for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
13233 	{
13234 		if ((objp->type == OBJ_SHIP) && !(objp->flags & OF_SHOULD_BE_DEAD))
13235 		{
13236 			ship *shipp;
13237 			ship_info *sip;
13238 			float dist;
13239 
13240 			Assertion((objp->instance >= 0) && (objp->instance < MAX_SHIPS),
13241 				"objp does not have a valid pointer to a ship. Pointer is %d, which is smaller than 0 or bigger than %d",
13242 				objp->instance, MAX_SHIPS);
13243 
13244 			shipp = &Ships[objp->instance];
13245 
13246 			if ( shipp->team != requester_ship->team ) {
13247 				continue;
13248 			}
13249 
13250 			Assertion((shipp->ship_info_index >= 0) && (shipp->ship_info_index < MAX_SHIP_CLASSES),
13251 				"Ship '%s' does not have a valid pointer to a ship class. Pointer is %d, which is smaller than 0 or bigger than %d",
13252 				shipp->ship_name, shipp->ship_info_index, MAX_SHIP_CLASSES);
13253 
13254 			sip = &Ship_info[shipp->ship_info_index];
13255 
13256 			if ( !(sip->flags & SIF_SUPPORT) ) {
13257 				continue;
13258 			}
13259 
13260 			// tally how many support ships actually exist
13261 			num_support_ships++;
13262 
13263 			// don't deal with dying or departing support ships
13264 			if ( shipp->flags & (SF_DYING | SF_DEPARTING) ) {
13265 				continue;
13266 			}
13267 
13268 			// Ship has been ordered to warpout but has not had a chance to process the order.
13269 			Assertion( (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO),
13270 				"Ship '%s' doesn't have a valid ai pointer. Pointer is %d, which is smaller than 0 or larger than %d",
13271 				shipp->ship_name, shipp->ai_index, MAX_AI_INFO);
13272 			ai_info* aip = &(Ai_info[shipp->ai_index]);
13273 			if ( ai_find_goal_index( aip->goals, AI_GOAL_WARP ) != -1 ) {
13274 				continue;
13275 			}
13276 
13277 			dist = vm_vec_dist_quick(&objp->pos, &requester_obj->pos);
13278 
13279 			if (((aip->ai_flags & (AIF_REPAIRING|AIF_AWAITING_REPAIR|AIF_BEING_REPAIRED)) > 0))
13280 			{
13281 				// support ship is already busy, track the one that will be
13282 				// done soonest by estimating how many seconds it will take for the support ship
13283 				// to reach the requester.
13284 				// The estimate is calculated by calculating how many seconds it will take the
13285 				// support ship to travel from its current location to the requester at max velocity
13286 				// We assume that every leg of the support ships journey will take the amount of time
13287 				// for the support ship to fly from its current location to the requester.  This is
13288 				// a bit hacky but it penalizes further away support ships, so a futher support ship
13289 				// will only be called if the closer ones are really busy.  This is just a craps shoot
13290 				// anyway because everything is moving around.
13291 				float howlong = 0;
13292 				for( int i = 0; i < MAX_AI_GOALS; i++ ) {
13293 					if ( aip->goals[i].ai_mode == AI_GOAL_REARM_REPAIR ) {
13294 						howlong += dist * objp->phys_info.max_vel.xyz.z;
13295 					}
13296 				}
13297 				if ( min_time_till_available < 0.0f || howlong < min_time_till_available ) {
13298 					min_time_till_available = howlong;
13299 					soonest_available_support_ship = objp;
13300 				}
13301 			}
13302 			else
13303 			{
13304 				// support ship not already busy, find the closest
13305 				if ( min_dist < 0.0f || dist < min_dist )
13306 				{
13307 					min_dist = dist;
13308 					nearest_support_ship = objp;
13309 				}
13310 			}
13311 		}
13312 	}
13313 
13314 	// no ships present?
13315 	// (be advised we may have an Arriving_support_ship in this case)
13316 	if (num_support_ships == 0) {
13317 		return 0;
13318 	}
13319 	// ship available?
13320 	else if (nearest_support_ship != NULL) {
13321 		// the nearest non-busy support ship is to service request
13322 		if (ship_we_found != NULL)
13323 			*ship_we_found = nearest_support_ship;
13324 		return 1;
13325 	}
13326 	// no ships available; are we below the limits?  (can we bring another ship in? -- and btw an Arriving_support_ship counts as being able to bring one in)
13327 	else if ((num_support_ships < The_mission.support_ships.max_concurrent_ships)
13328 		&& (	(Arriving_support_ship == NULL && The_mission.support_ships.tally < The_mission.support_ships.max_support_ships)
13329 			 || (Arriving_support_ship != NULL && The_mission.support_ships.tally <= The_mission.support_ships.max_support_ships) ))
13330 	{
13331 		// We are allowed more support ships in the mission; request another ship
13332 		// to service this request.
13333 		return 2;
13334 	}
13335 	// we're at the limit, but maybe a ship will become available
13336 	else if (soonest_available_support_ship != NULL) {
13337 		// found more support ships than should be in mission, so I can't ask for more,
13338 		// instead I will give the player the ship that will be done soonest
13339 		if (ship_we_found != NULL)
13340 			*ship_we_found = soonest_available_support_ship;
13341 		return 3;
13342 	}
13343 	// none of the above; we're out of luck
13344 	else {
13345 		return 4;
13346 	}
13347 }
13348 
13349 /**
13350  * Called in game_shutdown() to free malloced memory
13351  *
13352  * NOTE: do not call this function.  It is only called from ::game_shutdown()
13353  */
13354 int CLOAKMAP=-1;
ship_close()13355 void ship_close()
13356 {
13357 	int i, n;
13358 
13359 	for (i=0; i<MAX_SHIPS; i++ )	{
13360 		ship *shipp = &Ships[i];
13361 
13362 		if (shipp->shield_integrity != NULL) {
13363 			vm_free(shipp->shield_integrity);
13364 			shipp->shield_integrity = NULL;
13365 		}
13366 
13367 		if (shipp->ship_replacement_textures != NULL) {
13368 			vm_free(shipp->ship_replacement_textures);
13369 			shipp->ship_replacement_textures = NULL;
13370 		}
13371 
13372 		if(shipp->warpin_effect != NULL)
13373 			delete shipp->warpin_effect;
13374 		shipp->warpin_effect = NULL;
13375 
13376 		if(shipp->warpout_effect != NULL)
13377 			delete shipp->warpout_effect;
13378 		shipp->warpout_effect = NULL;
13379 	}
13380 
13381 	// free this too! -- Goober5000
13382 	ship_clear_subsystems();
13383 
13384 	// free memory alloced for subsystem storage
13385 	for ( i = 0; i < Num_ship_classes; i++ ) {
13386 		if ( Ship_info[i].subsystems != NULL ) {
13387 			for(n = 0; n < Ship_info[i].n_subsystems; n++) {
13388 				if (Ship_info[i].subsystems[n].triggers != NULL) {
13389 					vm_free(Ship_info[i].subsystems[n].triggers);
13390 					Ship_info[i].subsystems[n].triggers = NULL;
13391 				}
13392 			}
13393 
13394 			vm_free(Ship_info[i].subsystems);
13395 			Ship_info[i].subsystems = NULL;
13396 		}
13397 
13398 		// free info from parsed table data
13399 		if (Ship_info[i].type_str != NULL) {
13400 			vm_free(Ship_info[i].type_str);
13401 			Ship_info[i].type_str = NULL;
13402 		}
13403 
13404 		if (Ship_info[i].maneuverability_str != NULL) {
13405 			vm_free(Ship_info[i].maneuverability_str);
13406 			Ship_info[i].maneuverability_str = NULL;
13407 		}
13408 
13409 		if (Ship_info[i].armor_str != NULL) {
13410 			vm_free(Ship_info[i].armor_str);
13411 			Ship_info[i].armor_str = NULL;
13412 		}
13413 
13414 		if (Ship_info[i].manufacturer_str != NULL) {
13415 			vm_free(Ship_info[i].manufacturer_str);
13416 			Ship_info[i].manufacturer_str = NULL;
13417 		}
13418 
13419 		if (Ship_info[i].desc != NULL) {
13420 			vm_free(Ship_info[i].desc);
13421 			Ship_info[i].desc = NULL;
13422 		}
13423 
13424 		if (Ship_info[i].tech_desc != NULL) {
13425 			vm_free(Ship_info[i].tech_desc);
13426 			Ship_info[i].tech_desc = NULL;
13427 		}
13428 
13429 		if (Ship_info[i].ship_length != NULL) {
13430 			vm_free(Ship_info[i].ship_length);
13431 			Ship_info[i].ship_length = NULL;
13432 		}
13433 
13434 		if (Ship_info[i].gun_mounts != NULL) {
13435 			vm_free(Ship_info[i].gun_mounts);
13436 			Ship_info[i].gun_mounts = NULL;
13437 		}
13438 
13439 		if (Ship_info[i].missile_banks != NULL) {
13440 			vm_free(Ship_info[i].missile_banks);
13441 			Ship_info[i].missile_banks = NULL;
13442 		}
13443 	}
13444 
13445 	// free info from parsed table data
13446 	for (i=0; i<MAX_SHIP_CLASSES; i++) {
13447 		if ( Ship_info[i].subsystems != NULL ) {
13448 			for(n = 0; n < Ship_info[i].n_subsystems; n++) {
13449 				if (Ship_info[i].subsystems[n].triggers != NULL) {
13450 					vm_free(Ship_info[i].subsystems[n].triggers);
13451 					Ship_info[i].subsystems[n].triggers = NULL;
13452 				}
13453 			}
13454 
13455 			vm_free(Ship_info[i].subsystems);
13456 			Ship_info[i].subsystems = NULL;
13457 		}
13458 
13459 		if(Ship_info[i].type_str != NULL){
13460 			vm_free(Ship_info[i].type_str);
13461 			Ship_info[i].type_str = NULL;
13462 		}
13463 		if(Ship_info[i].maneuverability_str != NULL){
13464 			vm_free(Ship_info[i].maneuverability_str);
13465 			Ship_info[i].maneuverability_str = NULL;
13466 		}
13467 		if(Ship_info[i].armor_str != NULL){
13468 			vm_free(Ship_info[i].armor_str);
13469 			Ship_info[i].armor_str = NULL;
13470 		}
13471 		if(Ship_info[i].manufacturer_str != NULL){
13472 			vm_free(Ship_info[i].manufacturer_str);
13473 			Ship_info[i].manufacturer_str = NULL;
13474 		}
13475 		if(Ship_info[i].desc != NULL){
13476 			vm_free(Ship_info[i].desc);
13477 			Ship_info[i].desc = NULL;
13478 		}
13479 		if(Ship_info[i].tech_desc != NULL){
13480 			vm_free(Ship_info[i].tech_desc);
13481 			Ship_info[i].tech_desc = NULL;
13482 		}
13483 		if(Ship_info[i].ship_length != NULL){
13484 			vm_free(Ship_info[i].ship_length);
13485 			Ship_info[i].ship_length = NULL;
13486 		}
13487 		if(Ship_info[i].gun_mounts != NULL){
13488 			vm_free(Ship_info[i].gun_mounts);
13489 			Ship_info[i].gun_mounts = NULL;
13490 		}
13491 		if(Ship_info[i].missile_banks != NULL){
13492 			vm_free(Ship_info[i].missile_banks);
13493 			Ship_info[i].missile_banks = NULL;
13494 		}
13495 	}
13496 
13497 	if(CLOAKMAP != -1)
13498 		bm_release(CLOAKMAP);
13499 }
13500 
13501 /**
13502  * Assign object-linked sound to a particular ship
13503  */
ship_assign_sound(ship * sp)13504 void ship_assign_sound(ship *sp)
13505 {
13506 	ship_info	*sip;
13507 	object *objp;
13508 	vec3d engine_pos;
13509 	ship_subsys *moveup;
13510 
13511 	Assert( sp->objnum >= 0 );
13512 	if(sp->objnum < 0){
13513 		return;
13514 	}
13515 
13516 	objp = &Objects[sp->objnum];
13517 	sip = &Ship_info[sp->ship_info_index];
13518 
13519 	if ( sip->engine_snd != -1 ) {
13520 		vm_vec_copy_scale(&engine_pos, &objp->orient.vec.fvec, -objp->radius/2.0f);
13521 
13522 		obj_snd_assign(sp->objnum, sip->engine_snd, &engine_pos, 1);
13523 	}
13524 
13525 	// Do subsystem sounds
13526 	moveup = GET_FIRST(&sp->subsys_list);
13527 	while(moveup != END_OF_LIST(&sp->subsys_list)){
13528 		// Check for any engine sounds
13529 		if(strstr(moveup->system_info->name, "enginelarge")){
13530 			obj_snd_assign(sp->objnum, SND_ENGINE_LOOP_LARGE, &moveup->system_info->pnt, 0);
13531 		} else if(strstr(moveup->system_info->name, "enginehuge")){
13532 			obj_snd_assign(sp->objnum, SND_ENGINE_LOOP_HUGE, &moveup->system_info->pnt, 0);
13533 		}
13534 
13535 		//Do any normal subsystem sounds
13536 		if(moveup->current_hits > 0.0f)
13537 		{
13538 			if(moveup->system_info->alive_snd != -1)
13539 			{
13540 				obj_snd_assign(sp->objnum, moveup->system_info->alive_snd, &moveup->system_info->pnt, 0, OS_SUBSYS_ALIVE, moveup);
13541 				moveup->subsys_snd_flags |= SSSF_ALIVE;
13542 			}
13543 			if(moveup->system_info->turret_base_rotation_snd != -1)
13544 			{
13545 				obj_snd_assign(sp->objnum, moveup->system_info->turret_base_rotation_snd, &moveup->system_info->pnt, 0, OS_TURRET_BASE_ROTATION, moveup);
13546 				moveup->subsys_snd_flags |= SSSF_TURRET_ROTATION;
13547 			}
13548 			if(moveup->system_info->turret_gun_rotation_snd != -1)
13549 			{
13550 				obj_snd_assign(sp->objnum, moveup->system_info->turret_gun_rotation_snd, &moveup->system_info->pnt, 0, OS_TURRET_GUN_ROTATION, moveup);
13551 				moveup->subsys_snd_flags |= SSSF_TURRET_ROTATION;
13552 			}
13553 			if((moveup->system_info->rotation_snd != -1) && (moveup->flags & SSF_ROTATES))
13554 			{
13555 				obj_snd_assign(sp->objnum, moveup->system_info->rotation_snd, &moveup->system_info->pnt, 0, OS_SUBSYS_ROTATION, moveup);
13556 				moveup->subsys_snd_flags |= SSSF_ROTATE;
13557 			}
13558 		}
13559 		else
13560 		{
13561 			if(moveup->system_info->dead_snd != -1)
13562 			{
13563 				obj_snd_assign(sp->objnum, moveup->system_info->dead_snd, &moveup->system_info->pnt, 0, OS_SUBSYS_DEAD, moveup);
13564 				moveup->subsys_snd_flags |= SSSF_DEAD;
13565 			}
13566 		}
13567 
13568 		// next
13569 		moveup = GET_NEXT(moveup);
13570 	}
13571 }
13572 
13573 /**
13574  * Assign object-linked sounds to all ships currently in the obj_used_list
13575  */
ship_assign_sound_all()13576 void ship_assign_sound_all()
13577 {
13578 	object *objp;
13579 	size_t idx;
13580 	int has_sounds;
13581 
13582 	if ( !Sound_enabled )
13583 		return;
13584 
13585 	for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
13586 		if ( objp->type == OBJ_SHIP && Player_obj != objp) {
13587 			has_sounds = 0;
13588 
13589 			// check to make sure this guy hasn't got sounds already assigned to him
13590 			for(idx=0; idx<objp->objsnd_num.size(); idx++){
13591 				if(objp->objsnd_num[idx] != -1){
13592 					// skip
13593 					has_sounds = 1;
13594 					break;
13595 				}
13596 			}
13597 
13598 			// actually assign the sound
13599 			if(!has_sounds){
13600 				ship_assign_sound(&Ships[objp->instance]);
13601 			}
13602 		}
13603 	}
13604 }
13605 
13606 
13607 /**
13608  * Debug console function to set the shield for the player ship
13609  */
13610 DCF(set_shield,"Change player ship shield strength")
13611 {
13612 	if ( Dc_command )	{
13613 		dc_get_arg(ARG_FLOAT|ARG_NONE);
13614 
13615 		if ( Dc_arg_type & ARG_FLOAT ) {
13616             CLAMP(Dc_arg_float, 0.0f, 1.0f);
13617 			shield_set_strength(Player_obj, Dc_arg_float * Player_ship->ship_max_shield_strength);
13618 			dc_printf("Shields set to %.2f\n", shield_get_strength(Player_obj) );
13619 		}
13620 	}
13621 
13622 	if ( Dc_help ) {
13623 		dc_printf ("Usage: set_shield [num]\n");
13624 		dc_printf ("[num] --  shield percentage 0.0 -> 1.0 of max\n");
13625 		dc_printf ("with no parameters, displays shield strength\n");
13626 		Dc_status = 0;
13627 	}
13628 
13629 	if ( Dc_status )	{
13630 		dc_printf( "Shields are currently %.2f", shield_get_strength(Player_obj) );
13631 	}
13632 }
13633 
13634 /**
13635  * Debug console function to set the hull for the player ship
13636  */
13637 DCF(set_hull, "Change player ship hull strength")
13638 {
13639 	if ( Dc_command )	{
13640 		dc_get_arg(ARG_FLOAT|ARG_NONE);
13641 
13642 		if ( Dc_arg_type & ARG_FLOAT ) {
13643 			CLAMP(Dc_arg_float, 0.0f, 1.0f);
13644 			Player_obj->hull_strength = Dc_arg_float * Player_ship->ship_max_hull_strength;
13645 			dc_printf("Hull set to %.2f\n", Player_obj->hull_strength );
13646 		}
13647 	}
13648 
13649 	if ( Dc_help ) {
13650 		dc_printf ("Usage: set_hull [num]\n");
13651 		dc_printf ("[num] --  hull percentage 0.0 -> 1.0 of max\n");
13652 		dc_printf ("with no parameters, displays hull strength\n");
13653 		Dc_status = 0;
13654 	}
13655 
13656 	if ( Dc_status )	{
13657 		dc_printf( "Hull is currently %.2f", Player_obj->hull_strength );
13658 	}
13659 }
13660 
13661 /**
13662  * Debug console function to set the strength of a particular subsystem
13663  */
13664 //XSTR:OFF
13665 DCF(set_subsys, "Set the strength of a particular subsystem on player ship" )
13666 {
13667 	if ( Dc_command )	{
13668 		dc_get_arg(ARG_STRING);
13669 		if ( !subsystem_stricmp( Dc_arg, "weapons" ))	{
13670 			dc_get_arg(ARG_FLOAT);
13671 			if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )	{
13672 				Dc_help = 1;
13673 			} else {
13674 				ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, Dc_arg_float );
13675 			}
13676 		} else if ( !subsystem_stricmp( Dc_arg, "engine" ))	{
13677 			dc_get_arg(ARG_FLOAT);
13678 			if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )	{
13679 				Dc_help = 1;
13680 			} else {
13681 				ship_set_subsystem_strength( Player_ship, SUBSYSTEM_ENGINE, Dc_arg_float );
13682 				if ( Dc_arg_float < ENGINE_MIN_STR )	{
13683 					Player_ship->flags |= SF_DISABLED;				// add the disabled flag
13684 				} else {
13685 					Player_ship->flags &= (~SF_DISABLED);				// add the disabled flag
13686 				}
13687 			}
13688 		} else if ( !subsystem_stricmp( Dc_arg, "sensors" ))	{
13689 			dc_get_arg(ARG_FLOAT);
13690 			if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )	{
13691 				Dc_help = 1;
13692 			} else {
13693 				ship_set_subsystem_strength( Player_ship, SUBSYSTEM_SENSORS, Dc_arg_float );
13694 			}
13695 		} else if ( !subsystem_stricmp( Dc_arg, "communication" ))	{
13696 			dc_get_arg(ARG_FLOAT);
13697 			if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )	{
13698 				Dc_help = 1;
13699 			} else {
13700 				ship_set_subsystem_strength( Player_ship, SUBSYSTEM_COMMUNICATION, Dc_arg_float );
13701 			}
13702 		} else if ( !subsystem_stricmp( Dc_arg, "navigation" ))	{
13703 			dc_get_arg(ARG_FLOAT);
13704 			if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )	{
13705 				Dc_help = 1;
13706 			} else {
13707 				ship_set_subsystem_strength( Player_ship, SUBSYSTEM_NAVIGATION, Dc_arg_float );
13708 			}
13709 		} else if ( !subsystem_stricmp( Dc_arg, "radar" ))	{
13710 			dc_get_arg(ARG_FLOAT);
13711 			if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) )	{
13712 				Dc_help = 1;
13713 			} else {
13714 				ship_set_subsystem_strength( Player_ship, SUBSYSTEM_RADAR, Dc_arg_float );
13715 			}
13716 		} else {
13717 			// print usage
13718 			Dc_help = 1;
13719 		}
13720 	}
13721 
13722 	if ( Dc_help )	{
13723 		dc_printf( "Usage: set_subsys type X\nWhere X is value between 0 and 1.0, and type can be:\n" );
13724 		dc_printf( "weapons\n" );
13725 		dc_printf( "engine\n" );
13726 		dc_printf( "sensors\n" );
13727 		dc_printf( "communication\n" );
13728 		dc_printf( "navigation\n" );
13729 		dc_printf( "radar\n" );
13730 		Dc_status = 0;	// don't print status if help is printed.  Too messy.
13731 	}
13732 }
13733 //XSTR:ON
13734 
13735 // console function to toggle whether auto-repair for subsystems is active
13736 #ifndef NDEBUG
DCF_BOOL(auto_repair,Ship_auto_repair)13737 DCF_BOOL( auto_repair, Ship_auto_repair )
13738 #endif
13739 
13740 // two functions to keep track of counting ships of particular types.  Maybe we should be rolling this
13741 // thing into the stats section??  The first function adds a ship of a particular type to the overall
13742 // count of ships of that type (called from MissionParse.cpp).  The second function adds to the kill total
13743 // of ships of a particular type.  Note that we use the ship_info flags structure member to determine
13744 // what is happening.
13745 
13746 //WMC - ALERT!!!!!!!!!!!
13747 //These two functions did something weird with fighters/bombers. I don't
13748 //think that not doing this will break anything, but it might.
13749 //If it does, get me. OR someone smart.
13750 //G5K - Someone smart to the rescue!  Fixed the functions so they don't
13751 //accidentally overwrite all the information.
13752 
13753 void ship_clear_ship_type_counts()
13754 {
13755 	// resize if we need to
13756 	Ship_type_counts.resize(Ship_types.size());
13757 
13758 	// clear all the stats
13759 	for (size_t i = 0; i < Ship_type_counts.size(); i++)
13760 	{
13761 		Ship_type_counts[i].killed = 0;
13762 		Ship_type_counts[i].total = 0;
13763 	}
13764 }
13765 
ship_add_ship_type_count(int ship_info_index,int num)13766 void ship_add_ship_type_count( int ship_info_index, int num )
13767 {
13768 	int type = ship_class_query_general_type(ship_info_index);
13769 
13770 	//Ship has no type or something
13771 	if(type < 0) {
13772 		return;
13773 	}
13774 
13775 	//Add it
13776 	Ship_type_counts[type].total += num;
13777 }
13778 
ship_add_ship_type_kill_count(int ship_info_index)13779 void ship_add_ship_type_kill_count( int ship_info_index )
13780 {
13781 	int type = ship_class_query_general_type(ship_info_index);
13782 
13783 	//Ship has no type or something
13784 	if(type < 0) {
13785 		return;
13786 	}
13787 
13788 	//Add it
13789 	Ship_type_counts[type].killed++;
13790 }
13791 
ship_query_general_type(int ship)13792 int ship_query_general_type(int ship)
13793 {
13794 	return ship_query_general_type(&Ships[ship]);
13795 }
13796 
ship_query_general_type(ship * shipp)13797 int ship_query_general_type(ship *shipp)
13798 {
13799 	return ship_class_query_general_type(shipp->ship_info_index);
13800 }
13801 
ship_class_query_general_type(int ship_class)13802 int ship_class_query_general_type(int ship_class)
13803 {
13804 	//This is quick
13805 	return Ship_info[ship_class].class_type;
13806 }
13807 
13808 /**
13809  * Returns true
13810  */
ship_docking_valid(int docker,int dockee)13811 int ship_docking_valid(int docker, int dockee)
13812 {
13813 	// Goober5000
13814 	// So many people have asked for this function to be extended that it's making less
13815 	// and less sense to keep it around.  We should probably just let any ship type
13816 	// dock with any other ship type and assume the mission designer is smart enough not to
13817 	// mess things up.
13818 	return 1;
13819 }
13820 
13821 // function to return a random ship in a starting player wing.  Returns -1 if a suitable
13822 // one cannot be found
13823 // input:	max_dist	=>	OPTIONAL PARAMETER (default value 0.0f) max range ship can be from player
13824 // input:   persona  => OPTIONAL PARAMETER (default to -1) which persona to get
ship_get_random_player_wing_ship(int flags,float max_dist,int persona_index,int get_first,int multi_team)13825 int ship_get_random_player_wing_ship( int flags, float max_dist, int persona_index, int get_first, int multi_team )
13826 {
13827 	const int MAX_SIZE = MAX_SHIPS_PER_WING * MAX_SQUADRON_WINGS;
13828 
13829 	int i, j, ship_index, count;
13830 	int slist[MAX_SIZE], which_one;
13831 
13832 	// iterate through starting wings of player.  Add ship indices of ships which meet
13833 	// given criteria
13834 	count = 0;
13835 	for (i = 0; i < Num_wings; i++ ) {
13836 		if (count >= MAX_SIZE)
13837 			break;
13838 
13839 		int wingnum = -1;
13840 
13841 		// multi-team?
13842 		if(multi_team >= 0){
13843 			if( i == TVT_wings[multi_team] ) {
13844 				wingnum = i;
13845 			} else {
13846 				continue;
13847 			}
13848 		} else {
13849 			// first check for a player starting wing
13850 			for ( j = 0; j < MAX_STARTING_WINGS; j++ ) {
13851 				if ( i == Starting_wings[j] ) {
13852 					wingnum = i;
13853 					break;
13854 				}
13855 			}
13856 
13857 			// if not found, then check all squad wings (Goober5000)
13858 			if ( wingnum == -1 ) {
13859 				for ( j = 0; j < MAX_SQUADRON_WINGS; j++ ) {
13860 					if ( i == Squadron_wings[j] ) {
13861 						wingnum = i;
13862 						break;
13863 					}
13864 				}
13865 			}
13866 
13867 			if ( wingnum == -1 ){
13868 				continue;
13869 			}
13870 		}
13871 
13872 		for ( j = 0; j < Wings[wingnum].current_count; j++ ) {
13873 			if (count >= MAX_SIZE)
13874 				break;
13875 
13876 			ship_index = Wings[wingnum].ship_index[j];
13877 			Assert( ship_index != -1 );
13878 
13879 			if ( Ships[ship_index].flags & SF_DYING ) {
13880 				continue;
13881 			}
13882 			// see if ship meets our criterea
13883 			if ( (flags == SHIP_GET_NO_PLAYERS || flags == SHIP_GET_UNSILENCED) && (Objects[Ships[ship_index].objnum].flags & OF_PLAYER_SHIP) ){
13884 				continue;
13885 			}
13886 
13887 			if ( (flags == SHIP_GET_UNSILENCED) && (Ships[ship_index].flags2 & SF2_NO_BUILTIN_MESSAGES) )
13888 			{
13889 				continue;
13890 			}
13891 
13892 			// don't process ships on a different team
13893 			if(multi_team < 0){
13894 				if ( Player_ship->team != Ships[ship_index].team ){
13895 					continue;
13896 				}
13897 			}
13898 
13899 			// see if ship is within max_dist units
13900 			if ( (max_dist > 1.0f) && (multi_team < 0) ) {
13901 				float dist;
13902 				dist = vm_vec_dist_quick(&Objects[Ships[ship_index].objnum].pos, &Player_obj->pos);
13903 				if ( dist > max_dist ) {
13904 					continue;
13905 				}
13906 			}
13907 
13908 			// if we should be checking persona's, then don't add ships that don't have the proper persona
13909 			if ( persona_index != -1 ) {
13910 				if ( Ships[ship_index].persona_index != persona_index ){
13911 					continue;
13912 				}
13913 			}
13914 
13915 			// return the first ship with correct persona
13916 			if (get_first) {
13917 				return ship_index;
13918 			}
13919 
13920 			slist[count] = ship_index;
13921 			count++;
13922 		}
13923 	}
13924 
13925 	if ( count == 0 ){
13926 		return -1;
13927 	}
13928 
13929 	// now get a random one from the list
13930 	which_one = (rand() % count);
13931 	ship_index = slist[which_one];
13932 
13933 	Assert ( Ships[ship_index].objnum != -1 );
13934 
13935 	return ship_index;
13936 }
13937 
13938 // like above function, but returns a random ship in the given wing -- no restrictions
13939 // input:	max_dist	=>	OPTIONAL PARAMETER (default value 0.0f) max range ship can be from player
ship_get_random_ship_in_wing(int wingnum,int flags,float max_dist,int get_first)13940 int ship_get_random_ship_in_wing(int wingnum, int flags, float max_dist, int get_first)
13941 {
13942 	int i, ship_index, slist[MAX_SHIPS_PER_WING], count, which_one;
13943 
13944 	count = 0;
13945 	for ( i = 0; i < Wings[wingnum].current_count; i++ ) {
13946 		ship_index = Wings[wingnum].ship_index[i];
13947 		Assert( ship_index != -1 );
13948 
13949 		if ( Ships[ship_index].flags & SF_DYING ) {
13950 			continue;
13951 		}
13952 
13953 		// see if ship meets our criterea
13954 		if ( (flags == SHIP_GET_NO_PLAYERS || flags == SHIP_GET_UNSILENCED) && (Objects[Ships[ship_index].objnum].flags & OF_PLAYER_SHIP) )
13955 			continue;
13956 
13957 		if ( (flags == SHIP_GET_UNSILENCED) && (Ships[ship_index].flags2 & SF2_NO_BUILTIN_MESSAGES) )
13958 		{
13959 			continue;
13960 		}
13961 
13962 		// see if ship is within max_dist units
13963 		if ( max_dist > 0 ) {
13964 			float dist;
13965 			dist = vm_vec_dist_quick(&Objects[Ships[ship_index].objnum].pos, &Player_obj->pos);
13966 			if ( dist > max_dist ) {
13967 				continue;
13968 			}
13969 		}
13970 
13971 		// return the first ship in wing
13972 		if (get_first) {
13973 			return ship_index;
13974 		}
13975 
13976 		slist[count] = ship_index;
13977 		count++;
13978 	}
13979 
13980 	if ( count == 0 ) {
13981 		return -1;
13982 	}
13983 
13984 	// now get a random one from the list
13985 	which_one = (rand() % count);
13986 	ship_index = slist[which_one];
13987 
13988 	Assert ( Ships[ship_index].objnum != -1 );
13989 
13990 	return ship_index;
13991 }
13992 
13993 
13994 // this function returns a random index into the Ship array of a ship of the given team
13995 // cargo containers are not counted as ships for the purposes of this function.  Why???
13996 // because now it is only used for getting a random ship for a message and cargo containers
13997 // can't send mesages.  This function is an example of kind of bad coding :-(
13998 // input:	max_dist	=>	OPTIONAL PARAMETER (default value 0.0f) max range ship can be from player
ship_get_random_team_ship(int team_mask,int flags,float max_dist)13999 int ship_get_random_team_ship(int team_mask, int flags, float max_dist )
14000 {
14001 	int num, which_one;
14002 	object *objp, *obj_list[MAX_SHIPS];
14003 
14004 	// for any allied, go through the ships list and find all of the ships on that team
14005 	num = 0;
14006 	for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
14007 		if ( objp->type != OBJ_SHIP )
14008 			continue;
14009 
14010 		// series of conditionals one per line for easy reading
14011 		// don't process ships on wrong team
14012 		// don't process cargo's or navbuoys
14013 		// don't process player ships if flags are set
14014 		if (!iff_matches_mask(Ships[objp->instance].team, team_mask))
14015 			continue;
14016 		else if ( Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_NOT_FLYABLE )
14017 			continue;
14018 		else if ( (flags == SHIP_GET_NO_PLAYERS) && (objp->flags & OF_PLAYER_SHIP) )
14019 			continue;
14020 		else if ( (flags == SHIP_GET_ONLY_PLAYERS) && !(objp->flags & OF_PLAYER_SHIP) )
14021 			continue;
14022 
14023 		if ( Ships[objp->instance].flags & SF_DYING ) {
14024 			continue;
14025 		}
14026 
14027 		// see if ship is within max_dist units
14028 		if ( max_dist > 0 ) {
14029 			float dist;
14030 			dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
14031 			if ( dist > max_dist ) {
14032 				continue;
14033 			}
14034 		}
14035 
14036 		obj_list[num] = objp;
14037 		num++;
14038 	}
14039 
14040 	if ( num == 0 )
14041 		return -1;
14042 
14043 	which_one = (rand() % num);
14044 	objp = obj_list[which_one];
14045 
14046 	Assert ( objp->instance != -1 );
14047 
14048 	return objp->instance;
14049 }
14050 
14051 // -----------------------------------------------------------------------
14052 // ship_secondary_bank_has_ammo()
14053 //
14054 // check if currently selected secondary bank has ammo
14055 //
14056 // input:	shipnum	=>	index into Ships[] array for ship to check
14057 //
ship_secondary_bank_has_ammo(int shipnum)14058 int ship_secondary_bank_has_ammo(int shipnum)
14059 {
14060 	ship_weapon	*swp;
14061 
14062 	Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
14063 	swp = &Ships[shipnum].weapons;
14064 
14065 	if ( swp->current_secondary_bank == -1 )
14066 		return 0;
14067 
14068 	Assert(swp->current_secondary_bank >= 0 && swp->current_secondary_bank < MAX_SHIP_SECONDARY_BANKS );
14069 	if ( swp->secondary_bank_ammo[swp->current_secondary_bank] <= 0 )
14070 		return 0;
14071 
14072 	return 1;
14073 }
14074 
14075 // ship_primary_bank_has_ammo()
14076 //
14077 // check if currently selected primary bank has ammo
14078 //
14079 // input:	shipnum	=>	index into Ships[] array for ship to check
14080 //
ship_primary_bank_has_ammo(int shipnum)14081 int ship_primary_bank_has_ammo(int shipnum)
14082 {
14083 	ship_weapon	*swp;
14084 
14085 	Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
14086 	swp = &Ships[shipnum].weapons;
14087 
14088 	if ( swp->current_primary_bank == -1 )
14089 	{
14090 		return 0;
14091 	}
14092 
14093 	Assert(swp->current_primary_bank >= 0 && swp->current_primary_bank < MAX_SHIP_PRIMARY_BANKS );
14094 
14095 	return ( primary_out_of_ammo(swp, swp->current_primary_bank) == 0 );
14096 }
14097 
14098 // see if there is enough engine power to allow the ship to warp
14099 // returns 1 if ship is able to warp, otherwise return 0
ship_engine_ok_to_warp(ship * sp)14100 int ship_engine_ok_to_warp(ship *sp)
14101 {
14102 	// disabled ships can't warp
14103 	if (sp->flags & SF_DISABLED)
14104 		return 0;
14105 
14106 	if (sp->flags & SF_WARP_BROKEN || sp->flags & SF_WARP_NEVER)
14107 		return 0;
14108 
14109 	float engine_strength = ship_get_subsystem_strength(sp, SUBSYSTEM_ENGINE);
14110 
14111 	// if at 0% strength, can't warp
14112 	if (engine_strength <= 0.0f)
14113 		return 0;
14114 
14115 	// player ships playing above Very Easy can't warp when below a threshold
14116 	if ((sp == Player_ship) && (Game_skill_level > 0) && (engine_strength < SHIP_MIN_ENGINES_TO_WARP))
14117 		return 0;
14118 
14119 	// otherwise, warp is allowed
14120 	return 1;
14121 }
14122 
14123 // Goober5000
14124 // see if there is enough navigation power to allow the ship to warp
14125 // returns 1 if ship is able to warp, otherwise return 0
ship_navigation_ok_to_warp(ship * sp)14126 int ship_navigation_ok_to_warp(ship *sp)
14127 {
14128 	// if not using the special flag, warp is always allowed
14129 	if (!(The_mission.ai_profile->flags & AIPF_NAVIGATION_SUBSYS_GOVERNS_WARP))
14130 		return 1;
14131 
14132 	float navigation_strength = ship_get_subsystem_strength(sp, SUBSYSTEM_NAVIGATION);
14133 
14134 	// if at 0% strength, can't warp
14135 	if (navigation_strength <= 0.0f)
14136 		return 0;
14137 
14138 	// player ships playing above Very Easy can't warp when below a threshold
14139 	if ((sp == Player_ship) && (Game_skill_level > 0) && (navigation_strength < SHIP_MIN_NAV_TO_WARP))
14140 		return 0;
14141 
14142 	// otherwise, warp is allowed
14143 	return 1;
14144 }
14145 
14146 // Calculate the normal vector from a subsystem position and its first path point
14147 // input:	sp	=>	pointer to ship that is parent of subsystem
14148 //				ss =>	pointer to subsystem of interest
14149 //				norm	=> output parameter... vector from subsys to first path point
14150 //
14151 //	exit:		0	=>	a valid vector was placed in norm
14152 //				!0	=> an path normal could not be calculated
14153 //
ship_return_subsys_path_normal(ship * shipp,ship_subsys * ss,vec3d * gsubpos,vec3d * norm)14154 int ship_return_subsys_path_normal(ship *shipp, ship_subsys *ss, vec3d *gsubpos, vec3d *norm)
14155 {
14156 	if ( ss->system_info->path_num >= 0 ) {
14157 		polymodel	*pm = NULL;
14158 		model_path	*mp;
14159 		vec3d		*path_point;
14160 		vec3d		gpath_point;
14161 		pm = model_get(Ship_info[shipp->ship_info_index].model_num);
14162 		Assert( pm != NULL );
14163 
14164 		// possibly a bad model?
14165 		Assertion(ss->system_info->path_num <= pm->n_paths, "Too many paths in '%s'!  Max is %i and the requested path was %i for subsystem '%s'!\n", pm->filename, pm->n_paths, ss->system_info->path_num, ss->system_info->subobj_name);
14166 		if (ss->system_info->path_num > pm->n_paths)
14167 			return 1;
14168 
14169 		mp = &pm->paths[ss->system_info->path_num];
14170 		if ( mp->nverts >= 2 ) {
14171 			path_point = &mp->verts[0].pos;
14172 			// get path point in world coords
14173 			vm_vec_unrotate(&gpath_point, path_point, &Objects[shipp->objnum].orient);
14174 			vm_vec_add2(&gpath_point, &Objects[shipp->objnum].pos);
14175 			// get unit vector pointing from subsys pos to first path point
14176 			vm_vec_normalized_dir(norm, &gpath_point, gsubpos);
14177 			return 0;
14178 		}
14179 	}
14180 	return 1;
14181 }
14182 
14183 
14184 //	Determine if the subsystem can be viewed from eye_pos.  The method is to check where the
14185 // vector from eye_pos to the subsystem hits the ship.  If distance from the hit position and
14186 // the center of the subsystem is within a range (currently the subsystem radius) it is considered
14187 // in view (return true).  If not in view, return false.
14188 //
14189 // input:	objp		=>		object that is the ship with the subsystem on it
14190 //				subsys	=>		pointer to the subsystem of interest
14191 //				eye_pos	=>		world coord for the eye looking at the subsystem
14192 //				subsys_pos			=>	world coord for the center of the subsystem of interest
14193 //				do_facing_check	=>	OPTIONAL PARAMETER (default value is 1), do a dot product check to see if subsystem fvec is facing
14194 //											towards the eye position
14195 //				dot_out	=>		OPTIONAL PARAMETER, output parameter, will return dot between subsys fvec and subsys_to_eye_vec
14196 //									(only filled in if do_facing_check is true)
14197 //				vec_out	=>		OPTIONAL PARAMETER, vector from eye_pos to absolute subsys_pos.  (only filled in if do_facing_check is true)
ship_subsystem_in_sight(object * objp,ship_subsys * subsys,vec3d * eye_pos,vec3d * subsys_pos,int do_facing_check,float * dot_out,vec3d * vec_out)14198 int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vec3d *eye_pos, vec3d* subsys_pos, int do_facing_check, float *dot_out, vec3d *vec_out)
14199 {
14200 	float		dist, dot;
14201 	mc_info	mc;
14202 	vec3d	terminus, eye_to_pos, subsys_fvec, subsys_to_eye_vec;
14203 
14204 	if (objp->type != OBJ_SHIP)
14205 		return 0;
14206 
14207 	// See if we are at least facing the subsystem
14208 	if ( do_facing_check ) {
14209 		if ( ship_return_subsys_path_normal(&Ships[objp->instance], subsys, subsys_pos, &subsys_fvec) ) {
14210 			// non-zero return value means that we couldn't generate a normal from path info... so use inaccurate method
14211 			vm_vec_normalized_dir(&subsys_fvec, subsys_pos, &objp->pos);
14212 		}
14213 
14214 		vm_vec_normalized_dir(&subsys_to_eye_vec, eye_pos, subsys_pos);
14215 		dot = vm_vec_dot(&subsys_fvec, &subsys_to_eye_vec);
14216 		if ( dot_out ) {
14217 			*dot_out = dot;
14218 		}
14219 
14220 		if (vec_out) {
14221 			*vec_out = subsys_to_eye_vec;
14222 			vm_vec_negate(vec_out);
14223 		}
14224 
14225 		if ( dot < 0 )
14226 			return 0;
14227 	}
14228 
14229 	// See if ray from eye to subsystem actually hits close enough to the subsystem position
14230 	vm_vec_normalized_dir(&eye_to_pos, subsys_pos, eye_pos);
14231 	vm_vec_scale_add(&terminus, eye_pos, &eye_to_pos, 100000.0f);
14232 
14233 	mc_info_init(&mc);
14234 	mc.model_instance_num = Ships[objp->instance].model_instance_num;
14235 	mc.model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;			// Fill in the model to check
14236 	mc.orient = &objp->orient;										// The object's orientation
14237 	mc.pos = &objp->pos;												// The object's position
14238 	mc.p0 = eye_pos;													// Point 1 of ray to check
14239 	mc.p1 = &terminus;												// Point 2 of ray to check
14240 	mc.flags = MC_CHECK_MODEL;
14241 
14242 	model_collide(&mc);
14243 
14244 	if ( !mc.num_hits ) {
14245 		return 0;
14246 	}
14247 
14248 	// determine if hitpos is close enough to subsystem
14249 	dist = vm_vec_dist(&mc.hit_point_world, subsys_pos);
14250 
14251 	if ( dist <= subsys->system_info->radius ) {
14252 		return 1;
14253 	}
14254 
14255 	return 0;
14256 }
14257 
14258 /**
14259  * Find a subsystem matching 'type' inside the ship, and that is not destroyed.
14260  * @return If cannot find one, return NULL.
14261  */
ship_return_next_subsys(ship * shipp,int type,vec3d * attacker_pos)14262 ship_subsys *ship_return_next_subsys(ship *shipp, int type, vec3d *attacker_pos)
14263 {
14264 	ship_subsys	*ssp;
14265 
14266 	Assert ( type >= 0 && type < SUBSYSTEM_MAX );
14267 
14268 	// If aggregate total is 0, that means no subsystem is alive of that type
14269 	if ( shipp->subsys_info[type].aggregate_max_hits <= 0.0f )
14270 		return NULL;
14271 
14272 	// loop through all the subsystems, if we find a match that has some strength, return it
14273 	ssp = ship_get_best_subsys_to_attack(shipp, type, attacker_pos);
14274 
14275 	return ssp;
14276 }
14277 
14278 // Returns the closest subsystem of specified type that is in line of sight.
14279 // Returns null if all subsystems of that type are destroyed or none is in sight.
ship_get_closest_subsys_in_sight(ship * sp,int subsys_type,vec3d * attacker_pos)14280 ship_subsys *ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos)
14281 {
14282 	Assert ( subsys_type >= 0 && subsys_type < SUBSYSTEM_MAX );
14283 
14284 	// If aggregate total is 0, that means no subsystem is alive of that type
14285 	if ( sp->subsys_info[subsys_type].aggregate_max_hits <= 0.0f )
14286 		return NULL;
14287 
14288 	ship_subsys	*closest_in_sight_subsys;
14289 	ship_subsys	*ss;
14290 	vec3d		gsubpos;
14291 	float		closest_dist;
14292 	float		ss_dist;
14293 
14294 	closest_in_sight_subsys = NULL;
14295 	closest_dist = FLT_MAX;
14296 
14297 	for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
14298 		if ( (ss->system_info->type == subsys_type) && (ss->current_hits > 0) ) {
14299 
14300 			// get world pos of subsystem
14301 			vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &Objects[sp->objnum].orient);
14302 			vm_vec_add2(&gsubpos, &Objects[sp->objnum].pos);
14303 
14304 			if ( ship_subsystem_in_sight(&Objects[sp->objnum], ss, attacker_pos, &gsubpos) ) {
14305 				ss_dist = vm_vec_dist_squared(attacker_pos, &gsubpos);
14306 
14307 				if ( ss_dist < closest_dist ) {
14308 					closest_dist = ss_dist;
14309 					closest_in_sight_subsys = ss;
14310 				}
14311 			}
14312 		}
14313 	}
14314 
14315 	return closest_in_sight_subsys;
14316 }
14317 
ship_subsys_get_name(ship_subsys * ss)14318 char *ship_subsys_get_name(ship_subsys *ss)
14319 {
14320 	if( ss->sub_name[0] != '\0' )
14321 		return ss->sub_name;
14322 	else
14323 		return ss->system_info->name;
14324 }
14325 
ship_subsys_has_instance_name(ship_subsys * ss)14326 bool ship_subsys_has_instance_name(ship_subsys *ss)
14327 {
14328 	if( ss->sub_name[0] != '\0' )
14329 		return true;
14330 	else
14331 		return false;
14332 }
14333 
ship_subsys_set_name(ship_subsys * ss,char * n_name)14334 void ship_subsys_set_name(ship_subsys *ss, char* n_name)
14335 {
14336 	strncpy(ss->sub_name, n_name, NAME_LENGTH-1);
14337 }
14338 
14339 // Return the shield strength in the quadrant hit on hit_objp, based on global hitpos
14340 //
14341 // input:	hit_objp	=>	object pointer to ship getting hit
14342 //				hitpos	=> global position of impact
14343 //
14344 // exit:		strength of shields in the quadrant that was hit as a percentage, between 0 and 1.0
14345 //
14346 // Assumes: that hitpos is a valid global hit position
ship_quadrant_shield_strength(object * hit_objp,vec3d * hitpos)14347 float ship_quadrant_shield_strength(object *hit_objp, vec3d *hitpos)
14348 {
14349 	int			quadrant_num, i;
14350 	float			max_quadrant;
14351 	vec3d		tmpv1, tmpv2;
14352 
14353 	// If ship doesn't have shield mesh, then return
14354 	if ( hit_objp->flags & OF_NO_SHIELDS ) {
14355 		return 0.0f;
14356 	}
14357 
14358 	// Check if all the shield quadrants are all already 0, if so return 0
14359 	for ( i = 0; i < 4; i++ ) {
14360 		if ( hit_objp->shield_quadrant[i] > 0 )
14361 			break;
14362 	}
14363 
14364 	if ( i == 4 ) {
14365 		return 0.0f;
14366 	}
14367 
14368 	// convert hitpos to position in model coordinates
14369 	vm_vec_sub(&tmpv1, hitpos, &hit_objp->pos);
14370 	vm_vec_rotate(&tmpv2, &tmpv1, &hit_objp->orient);
14371 	quadrant_num = get_quadrant(&tmpv2, hit_objp);
14372 
14373 	if ( quadrant_num < 0 )
14374 		quadrant_num = 0;
14375 
14376 	max_quadrant = get_max_shield_quad(hit_objp);
14377 	if ( max_quadrant <= 0 ) {
14378 		return 0.0f;
14379 	}
14380 
14381 	if(hit_objp->shield_quadrant[quadrant_num] > max_quadrant)
14382 		mprintf((LOCATION, "Warning: \"%s\" has shield quadrant strength of %f out of %f\n", Ships[hit_objp->instance].ship_name, hit_objp->shield_quadrant[quadrant_num], max_quadrant));
14383 
14384 	return hit_objp->shield_quadrant[quadrant_num]/max_quadrant;
14385 }
14386 
14387 // Determine if a ship is threatened by any dumbfire projectiles (laser or missile)
14388 // input:	sp	=>	pointer to ship that might be threatened
14389 // exit:		0 =>	no dumbfire threats
14390 //				1 =>	at least one dumbfire threat
14391 //
14392 // NOTE: Currently this function is only called periodically from the HUD code for the
14393 //       player ship.
ship_dumbfire_threat(ship * sp)14394 int ship_dumbfire_threat(ship *sp)
14395 {
14396 	if ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER) ) {
14397 		return 0;
14398 	}
14399 
14400 	if (ai_endangered_by_weapon(&Ai_info[sp->ai_index]) > 0) {
14401 		return 1;
14402 	}
14403 
14404 	return 0;
14405 }
14406 
14407 // Return !0 if there is a missile in the air homing on shipp
ship_has_homing_missile_locked(ship * shipp)14408 int ship_has_homing_missile_locked(ship *shipp)
14409 {
14410 	object		*locked_objp, *A;
14411 	weapon		*wp;
14412 	weapon_info	*wip;
14413 	missile_obj	*mo;
14414 
14415 	Assert(shipp->objnum >= 0 && shipp->objnum < MAX_OBJECTS);
14416 	locked_objp = &Objects[shipp->objnum];
14417 
14418 	// check for currently locked missiles (highest precedence)
14419 	for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
14420 		Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
14421 		A = &Objects[mo->objnum];
14422 
14423 		if (A->type != OBJ_WEAPON)
14424 			continue;
14425 
14426 		Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
14427 		wp = &Weapons[A->instance];
14428 		wip = &Weapon_info[wp->weapon_info_index];
14429 
14430 		if ( wip->subtype != WP_MISSILE )
14431 			continue;
14432 
14433 		if ( !(wip->wi_flags & WIF_HOMING ) )
14434 			continue;
14435 
14436 		if (wp->homing_object == locked_objp) {
14437 			return 1;
14438 		}
14439 	}	// end for
14440 
14441 	return 0;
14442 }
14443 
14444 // Return !0 if there is some ship attempting to lock onto shipp
ship_is_getting_locked(ship * shipp)14445 int ship_is_getting_locked(ship *shipp)
14446 {
14447 	ship_obj	*so;
14448 	object	*objp;
14449 	ai_info	*aip;
14450 
14451 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14452 		objp = &Objects[so->objnum];
14453 		aip = &Ai_info[Ships[objp->instance].ai_index];
14454 
14455 		if ( aip->target_objnum == shipp->objnum ) {
14456 			if ( aip->aspect_locked_time > 0.1f ) {
14457 				float dist, wep_range;
14458 				dist = vm_vec_dist_quick(&objp->pos, &Objects[shipp->objnum].pos);
14459 				wep_range = ship_get_secondary_weapon_range(&Ships[objp->instance]);
14460 				if ( wep_range > dist ) {
14461 					nprintf(("Alan","AI ship is seeking lock\n"));
14462 					return 1;
14463 				}
14464 			}
14465 		}
14466 	}
14467 
14468 	return 0;
14469 }
14470 
14471 // Determine if a ship is threatened by attempted lock or actual lock
14472 // input:	sp	=>	pointer to ship that might be threatened
14473 // exit:		0 =>	no lock threats of any kind
14474 //				1 =>	at least one attempting lock (no actual locks)
14475 //				2 =>	at least one lock (possible other attempting locks)
14476 //
14477 // NOTE: Currently this function is only called periodically from the HUD code for the
14478 //       player ship.
ship_lock_threat(ship * sp)14479 int ship_lock_threat(ship *sp)
14480 {
14481 	if ( ship_has_homing_missile_locked(sp) ) {
14482 		return 2;
14483 	}
14484 
14485 	if ( ship_is_getting_locked(sp) ) {
14486 		return 1;
14487 	}
14488 
14489 	return 0;
14490 }
14491 
14492 // converts a bitmask, such as 0x08, into the bit number this would be (3 in this case)
14493 // NOTE: Should move file to something like Math_utils.
bitmask_2_bitnum(int num)14494 int bitmask_2_bitnum(int num)
14495 {
14496 	int i;
14497 
14498 	for (i=0; i<32; i++)
14499 		if (num & (1 << i))
14500 			return i;
14501 
14502 	return -1;
14503 }
14504 
14505 // Get a text description of a ships orders.
14506 //
14507 //	input:	outbuf	=>		buffer to hold orders string
14508 //				sp			=>		ship pointer to extract orders from
14509 //
14510 // exit:		NULL		=>		printable orders are not applicable
14511 //				non-NULL	=>		pointer to string that was passed in originally
14512 //
14513 // This function is called from HUD code to get a text description
14514 // of what a ship's orders are.  Feel free to use this function if
14515 // it suits your needs for something.
14516 //
ship_return_orders(char * outbuf,ship * sp)14517 char *ship_return_orders(char *outbuf, ship *sp)
14518 {
14519 	ai_info	*aip;
14520 	ai_goal	*aigp;
14521 	const char		*order_text;
14522 	char ship_name[NAME_LENGTH];
14523 
14524 	Assert(sp->ai_index >= 0);
14525 	aip = &Ai_info[sp->ai_index];
14526 
14527 	// The active goal is always in the first element of aip->goals[]
14528 	aigp = &aip->goals[0];
14529 
14530 	if ( aigp->ai_mode < 0 )
14531 		return NULL;
14532 
14533 	order_text = Ai_goal_text(bitmask_2_bitnum(aigp->ai_mode));
14534 	if ( order_text == NULL )
14535 		return NULL;
14536 
14537 	strcpy(outbuf, order_text);
14538 
14539 	if ( aigp->target_name ) {
14540 		strcpy_s(ship_name, aigp->target_name);
14541 		end_string_at_first_hash_symbol(ship_name);
14542 	}
14543 	switch (aigp->ai_mode ) {
14544 
14545 		case AI_GOAL_FORM_ON_WING:
14546 		case AI_GOAL_GUARD_WING:
14547 		case AI_GOAL_CHASE_WING:
14548 			if ( aigp->target_name ) {
14549 				strcat(outbuf, ship_name);
14550 				strcat(outbuf, XSTR( "'s Wing", 494));
14551 			} else {
14552 				strcpy(outbuf, XSTR( "no orders", 495));
14553 			}
14554 			break;
14555 
14556 		case AI_GOAL_CHASE:
14557 		case AI_GOAL_DOCK:
14558 		case AI_GOAL_UNDOCK:
14559 		case AI_GOAL_GUARD:
14560 		case AI_GOAL_DISABLE_SHIP:
14561 		case AI_GOAL_DISARM_SHIP:
14562 		case AI_GOAL_EVADE_SHIP:
14563 		case AI_GOAL_REARM_REPAIR:
14564 			if ( aigp->target_name ) {
14565 				strcat(outbuf, ship_name);
14566 			} else {
14567 				strcpy(outbuf, XSTR( "no orders", 495));
14568 			}
14569 			break;
14570 
14571 		case AI_GOAL_DESTROY_SUBSYSTEM: {
14572 			if ( aip->targeted_subsys != NULL ) {
14573 				char subsys_name[NAME_LENGTH];
14574 				strcpy_s(subsys_name, aip->targeted_subsys->system_info->subobj_name);
14575 				hud_targetbox_truncate_subsys_name(subsys_name);
14576 				sprintf(outbuf, XSTR( "atk %s %s", 496), ship_name, subsys_name);
14577 			} else {
14578 				strcpy(outbuf, XSTR( "no orders", 495) );
14579 			}
14580 			break;
14581 		}
14582 
14583 		case AI_GOAL_WAYPOINTS:
14584 		case AI_GOAL_WAYPOINTS_ONCE:
14585 			// don't do anything, all info is in order_text
14586 			break;
14587 
14588 		case AI_GOAL_FLY_TO_SHIP:
14589 			strcpy(outbuf, "Flying to ship");
14590 			break;
14591 
14592 
14593 		default:
14594 			return NULL;
14595 	}
14596 
14597 	return outbuf;
14598 }
14599 
14600 // return the amount of time until ship reaches its goal (in MM:SS format)
14601 //	input:	outbuf	=>		buffer to hold orders string
14602 //				sp			=>		ship pointer to extract orders from
14603 //
14604 // exit:		NULL		=>		printable orders are not applicable
14605 //				non-NULL	=>		pointer to string that was passed in originally
14606 //
14607 // This function is called from HUD code to get a text description
14608 // of what a ship's orders are.  Feel free to use this function if
14609 // it suits your needs for something.
ship_return_time_to_goal(char * outbuf,ship * sp)14610 char *ship_return_time_to_goal(char *outbuf, ship *sp)
14611 {
14612 	ai_info	*aip;
14613 	int		time, seconds, minutes;
14614 	float		dist = 0.0f;
14615 	object	*objp;
14616 	float		min_speed, max_speed;
14617 
14618 	objp = &Objects[sp->objnum];
14619 	aip = &Ai_info[sp->ai_index];
14620 
14621 	min_speed = objp->phys_info.speed;
14622 
14623 	// Goober5000 - handle cap
14624 	if (aip->waypoint_speed_cap >= 0)
14625 		max_speed = MIN(sp->current_max_speed, aip->waypoint_speed_cap);
14626 	else
14627 		max_speed = sp->current_max_speed;
14628 
14629 	if ( aip->mode == AIM_WAYPOINTS ) {
14630 		min_speed = 0.9f * max_speed;
14631 		if (aip->wp_list != NULL) {
14632 			Assert(aip->wp_index != INVALID_WAYPOINT_POSITION);
14633 			dist += vm_vec_dist_quick(&objp->pos, aip->wp_list->get_waypoints()[aip->wp_index].get_pos());
14634 
14635 			SCP_vector<waypoint>::iterator ii;
14636 			vec3d *prev_vec = NULL;
14637 			for (ii = (aip->wp_list->get_waypoints().begin() + aip->wp_index); ii != aip->wp_list->get_waypoints().end(); ++ii) {
14638 				if (prev_vec != NULL) {
14639 					dist += vm_vec_dist_quick(ii->get_pos(), prev_vec);
14640 				}
14641 				prev_vec = ii->get_pos();
14642 			}
14643 		}
14644 
14645 		if ( dist < 1.0f) {
14646 			return NULL;
14647 		}
14648 
14649 		if ( (Objects[sp->objnum].phys_info.speed <= 0) || (max_speed <= 0.0f) ) {
14650 			time = -1;
14651 		} else {
14652 			float	speed;
14653 
14654 			speed = objp->phys_info.speed;
14655 
14656 			if (speed < min_speed)
14657 				speed = min_speed;
14658 			time = fl2i(dist/speed);
14659 		}
14660 
14661 	} else if ( (aip->mode == AIM_DOCK) && (aip->submode < AIS_DOCK_4) ) {
14662 		time = hud_get_dock_time( objp );
14663 	} else {
14664 		// don't return anytime for time to except for waypoints and actual docking.
14665 		return NULL;
14666 	}
14667 
14668 	if ( time >= 0 ) {
14669 		minutes = time/60;
14670 		seconds = time%60;
14671 		if ( minutes > 99 ) {
14672 			minutes = 99;
14673 			seconds = 99;
14674 		}
14675 		sprintf(outbuf, NOX("%02d:%02d"), minutes, seconds);
14676 	} else {
14677 		strcpy( outbuf, XSTR( "Unknown", 497) );
14678 	}
14679 
14680 	return outbuf;
14681 }
14682 
14683 /* Karajorma - V decided not to use this function so I've commented it out so it isn't confused with code
14684 +that is actually in use. Someone might want to get it working using AI_Profiles at some point so I didn't
14685 +simply delete it.
14686 
14687 // Called to check if any AI ships might reveal the cargo of any cargo containers.
14688 //
14689 // This is called once a frame, but a global timer 'Ship_cargo_check_timer' will limit this
14690 // function to being called every SHIP_CARGO_CHECK_INTERVAL ms.  I think that should be sufficient.
14691 //
14692 // NOTE: This function uses CARGO_REVEAL_DISTANCE from the HUD code... which is a multiple of
14693 //       the ship radius that is used to determine when cargo is detected.  AI ships do not
14694 //       have to have the ship targeted to reveal cargo.  The player is ignored in this function.
14695 #define SHIP_CARGO_CHECK_INTERVAL	1000
14696 void ship_check_cargo_all()
14697 {
14698 	object	*cargo_objp;
14699 	ship_obj	*cargo_so, *ship_so;
14700 	ship		*cargo_sp, *ship_sp;
14701 	float		dist_squared, limit_squared;
14702 
14703 	// I don't want to do this check every frame, so I made a global timer to limit check to
14704 	// every SHIP_CARGO_CHECK_INTERVAL ms.
14705 	if ( !timestamp_elapsed(Ship_cargo_check_timer) ) {
14706 		return;
14707 	} else {
14708 		Ship_cargo_check_timer = timestamp(SHIP_CARGO_CHECK_INTERVAL);
14709 	}
14710 
14711 	// Check all friendly fighter/bombers against all non-friendly cargo containers that don't have
14712 	// cargo revealed
14713 
14714 	// for now just locate a capital ship on the same team:
14715 	cargo_so = GET_FIRST(&Ship_obj_list);
14716 	while(cargo_so != END_OF_LIST(&Ship_obj_list)){
14717 		cargo_sp = &Ships[Objects[cargo_so->objnum].instance];
14718 		if ( (Ship_info[cargo_sp->ship_info_index].flags & SIF_CARGO) && (cargo_sp->team != Player_ship->team) ) {
14719 
14720 			// If the cargo is revealed, continue on to next hostile cargo
14721 			if ( cargo_sp->flags & SF_CARGO_REVEALED ) {
14722 				goto next_cargo;
14723 			}
14724 
14725 			// check against friendly fighter/bombers + cruiser/freighter/transport
14726 			// IDEA: could cull down to fighter/bomber if we want this to run a bit quicker
14727 			for ( ship_so=GET_FIRST(&Ship_obj_list); ship_so != END_OF_LIST(&Ship_obj_list); ship_so=GET_NEXT(ship_so) )
14728 			{
14729 				ship_sp = &Ships[Objects[ship_so->objnum].instance];
14730 				// only consider friendly ships
14731 				if (ship_sp->team != Player_ship->team) {
14732 					continue;
14733 				}
14734 
14735 				// ignore the player
14736 				if ( ship_so->objnum == OBJ_INDEX(Player_obj) ) {
14737 					continue;
14738 				}
14739 
14740 				// if this ship is a small or big ship
14741 				if ( Ship_info[ship_sp->ship_info_index].flags & (SIF_SMALL_SHIP|SIF_BIG_SHIP) ) {
14742 					cargo_objp = &Objects[cargo_sp->objnum];
14743 					// use square of distance, faster than getting real distance (which will use sqrt)
14744 					dist_squared = vm_vec_dist_squared(&cargo_objp->pos, &Objects[ship_sp->objnum].pos);
14745 					limit_squared = (cargo_objp->radius+CARGO_RADIUS_DELTA)*(cargo_objp->radius+CARGO_RADIUS_DELTA);
14746 					if ( dist_squared <= MAX(limit_squared, CARGO_REVEAL_MIN_DIST*CARGO_REVEAL_MIN_DIST) ) {
14747 						ship_do_cargo_revealed( cargo_sp );
14748 						break;	// break out of for loop, move on to next hostile cargo
14749 					}
14750 				}
14751 			} // end for
14752 		}
14753 next_cargo:
14754 		cargo_so = GET_NEXT(cargo_so);
14755 	} // end while
14756 }
14757 */
14758 
14759 
14760 // Maybe warn player about this attacking ship.  This is called once per frame, and the
14761 // information about the closest attacking ship comes for free, since this function is called
14762 // from HUD code which has already determined the closest enemy attacker and the distance.
14763 //
14764 // input:	enemy_sp	=>	ship pointer to the TEAM_ENEMY ship attacking the player
14765 //				dist		=>	the distance of the enemy to the player
14766 //
14767 // NOTE: there are no filters on enemy_sp, so it could be any ship type
14768 //
14769 #define PLAYER_CHECK_WARN_INTERVAL		300		// how often we check for warnings
14770 #define PLAYER_MIN_WARN_DIST				100		// minimum distance attacking ship can be from player and still allow warning
14771 #define PLAYER_MAX_WARN_DIST				1000		// maximum distance attacking ship can be from plyaer and still allow warning
14772 
ship_maybe_warn_player(ship * enemy_sp,float dist)14773 void ship_maybe_warn_player(ship *enemy_sp, float dist)
14774 {
14775 	float		fdot; //, rdot, udot;
14776 	vec3d	vec_to_target;
14777 	int		msg_type; //, on_right;
14778 
14779 	// First check if the player has reached the maximum number of warnings for a mission
14780 	if ((Builtin_messages[MESSAGE_CHECK_6].max_count > -1) && ( Player->warn_count >= Builtin_messages[MESSAGE_CHECK_6].max_count )) {
14781 		return;
14782 	}
14783 
14784 	// Check if enough time has elapsed since last warning, if not - leave
14785 	if ( !timestamp_elapsed(Player->allow_warn_timestamp) ) {
14786 		return;
14787 	}
14788 
14789 	// Check to see if check timer has elapsed.  Necessary, since we don't want to check each frame
14790 	if ( !timestamp_elapsed(Player->check_warn_timestamp ) ) {
14791 		return;
14792 	}
14793 	Player->check_warn_timestamp = timestamp(PLAYER_CHECK_WARN_INTERVAL);
14794 
14795 	// only allow warnings if within a certain distance range
14796 	if ( dist < PLAYER_MIN_WARN_DIST || dist > PLAYER_MAX_WARN_DIST ) {
14797 		return;
14798 	}
14799 
14800 	// only warn if a fighter or bomber is attacking the player
14801 	if ( !(Ship_info[enemy_sp->ship_info_index].flags & SIF_SMALL_SHIP) ) {
14802 		return;
14803 	}
14804 
14805 	// get vector from player to target
14806 	vm_vec_normalized_dir(&vec_to_target, &Objects[enemy_sp->objnum].pos, &Eye_position);
14807 
14808 	// ensure that enemy fighter is oriented towards player
14809 	fdot = vm_vec_dot(&Objects[enemy_sp->objnum].orient.vec.fvec, &vec_to_target);
14810 	if ( fdot > -0.7 ) {
14811 		return;
14812 	}
14813 
14814 	fdot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
14815 
14816 	msg_type = -1;
14817 
14818 	// check if attacking ship is on six.  return if not far enough behind player.
14819 	if ( fdot > -0.7 )
14820 		return;
14821 
14822 	msg_type = MESSAGE_CHECK_6;
14823 
14824 	if ( msg_type != -1 ) {
14825 		int ship_index;
14826 
14827 		// multiplayer tvt - this is client side.
14828 		if(MULTI_TEAM && (Net_player != NULL)){
14829 			ship_index = ship_get_random_player_wing_ship( SHIP_GET_UNSILENCED, 0.0f, -1, 0, Net_player->p_info.team );
14830 		} else {
14831 			ship_index = ship_get_random_player_wing_ship( SHIP_GET_UNSILENCED );
14832 		}
14833 
14834 		if ( ship_index >= 0 ) {
14835 			// multiplayer - make sure I just send to myself
14836 			if(Game_mode & GM_MULTIPLAYER){
14837 				message_send_builtin_to_player(msg_type, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, MY_NET_PLAYER_NUM, -1);
14838 			} else {
14839 				message_send_builtin_to_player(msg_type, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14840 			}
14841 			Player->allow_warn_timestamp = timestamp(Builtin_messages[MESSAGE_CHECK_6].min_delay);
14842 			Player->warn_count++;
14843 		}
14844 	}
14845 }
14846 
14847 // player has just killed a ship, maybe offer send a 'good job' message
ship_maybe_praise_player(ship * deader_sp)14848 void ship_maybe_praise_player(ship *deader_sp)
14849 {
14850 	if ( myrand()&1 ) {
14851 		return;
14852 	}
14853 
14854 	// First check if the player has reached the maximum number of praises for a mission
14855 	if ((Builtin_messages[MESSAGE_PRAISE].max_count > -1) && (Player->praise_count >= Builtin_messages[MESSAGE_PRAISE].max_count )) {
14856 		return;
14857 	}
14858 
14859 	// Check if enough time has elapsed since last praise, if not - leave
14860 	if ( !timestamp_elapsed(Player->allow_praise_timestamp) ) {
14861 		return;
14862 	}
14863 
14864 	// make sure player is not a traitor
14865 	if (Player_ship->team == Iff_traitor) {
14866 		return;
14867 	}
14868 
14869 	// only praise if killing an enemy!
14870 	if ( deader_sp->team == Player_ship->team ) {
14871 		return;
14872 	}
14873 
14874 	// don't praise the destruction of navbuoys, cargo or other non-flyable ship types
14875 	if ( (Ship_info[deader_sp->ship_info_index].class_type > 0) && !(Ship_types[Ship_info[deader_sp->ship_info_index].class_type].message_bools & STI_MSG_PRAISE_DESTRUCTION) ) {
14876 		return;
14877 	}
14878 
14879 	// There is already a praise pending
14880 	if ( Player->praise_delay_timestamp ) {
14881 		return;
14882 	}
14883 
14884 	// We don't want to praise the player right away.. it is more realistic to wait a moment
14885 	Player->praise_delay_timestamp = timestamp_rand(1000, 2000);
14886 }
14887 
ship_maybe_praise_self(ship * deader_sp,ship * killer_sp)14888 void ship_maybe_praise_self(ship *deader_sp, ship *killer_sp)
14889 {
14890 	int j;
14891 	bool wingman = false;
14892 
14893 	if ( (int)(frand()*100) > Builtin_messages[MESSAGE_PRAISE_SELF].occurrence_chance ) {
14894 		return;
14895 	}
14896 
14897 	if (Game_mode & GM_MULTIPLAYER) {
14898 		return;
14899 	}
14900 
14901 	if ((Builtin_messages[MESSAGE_PRAISE_SELF].max_count > -1) && (Player->praise_self_count >= Builtin_messages[MESSAGE_PRAISE_SELF].max_count)) {
14902 		return;
14903 	}
14904 
14905 	if (!timestamp_elapsed(Player->praise_self_timestamp)) {
14906 		return;
14907 	}
14908 
14909 	// only praise if killing an enemy so check they both attack each other!
14910 	if (!((iff_x_attacks_y(deader_sp->team, killer_sp->team)) && (iff_x_attacks_y(killer_sp->team, deader_sp->team ))) ) {
14911 		return;
14912 	}
14913 
14914 
14915 	// only send messages from the player's wingmen
14916 	if (killer_sp->wingnum == -1) {
14917 		return;
14918 	}
14919 	for ( j = 0; j < MAX_STARTING_WINGS; j++ ) {
14920 		if ( Starting_wings[j] == killer_sp->wingnum) {
14921 			wingman = true;
14922 			break;
14923 		}
14924 	}
14925 
14926 	if (!wingman) {
14927 		return;
14928 	}
14929 
14930 	// don't praise the destruction of navbuoys, cargo or other non-flyable ship types
14931 	if ( (Ship_info[deader_sp->ship_info_index].class_type > 0) && !(Ship_types[Ship_info[deader_sp->ship_info_index].class_type].message_bools & STI_MSG_PRAISE_DESTRUCTION) ) {
14932 		return;
14933 	}
14934 
14935 	// ensure the ship isn't silenced
14936 	if ( killer_sp->flags2 & SF2_NO_BUILTIN_MESSAGES ) {
14937 		return;
14938 	}
14939 
14940 	message_send_builtin_to_player(MESSAGE_PRAISE_SELF, killer_sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, -1, -1);
14941 	Player->praise_self_timestamp = timestamp(Builtin_messages[MESSAGE_PRAISE_SELF].min_delay);
14942 	Player->praise_self_count++;
14943 }
14944 
14945 #define ASK_HELP_SHIELD_PERCENT			0.1		// percent shields at which ship will ask for help
14946 #define ASK_HELP_HULL_PERCENT				0.3		// percent hull at which ship will ask for help
14947 #define AWACS_HELP_HULL_HI					0.75		// percent hull at which ship will ask for help
14948 #define AWACS_HELP_HULL_LOW				0.25		// percent hull at which ship will ask for help
14949 
14950 // -----------------------------------------------------------------------------
awacs_maybe_ask_for_help(ship * sp,int multi_team_filter)14951 void awacs_maybe_ask_for_help(ship *sp, int multi_team_filter)
14952 {
14953 	// Goober5000 - bail if not in main fs2 campaign
14954 	// (stupid coders... it's the FREDder's responsibility to add this message)
14955 	if (stricmp(Campaign.filename, "freespace2") || !(Game_mode & GM_CAMPAIGN_MODE))
14956 		return;
14957 
14958 	object *objp;
14959 	int message = -1;
14960 	objp = &Objects[sp->objnum];
14961 
14962 	if ( objp->hull_strength < ( (AWACS_HELP_HULL_LOW + 0.01f *(static_rand(objp-Objects) & 5)) * sp->ship_max_hull_strength) ) {
14963 		// awacs ship below 25 + (0-4) %
14964 		if (!(sp->awacs_warning_flag & AWACS_WARN_25)) {
14965 			message = MESSAGE_AWACS_25;
14966 			sp->awacs_warning_flag |=  AWACS_WARN_25;
14967 		}
14968 	} else if ( objp->hull_strength < ( (AWACS_HELP_HULL_HI + 0.01f*(static_rand(objp-Objects) & 5)) * sp->ship_max_hull_strength) ) {
14969 		// awacs ship below 75 + (0-4) %
14970 		if (!(sp->awacs_warning_flag & AWACS_WARN_75)) {
14971 			message = MESSAGE_AWACS_75;
14972 			sp->awacs_warning_flag |=  AWACS_WARN_75;
14973 		}
14974 	}
14975 
14976 	if (message >= 0) {
14977 		message_send_builtin_to_player(message, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
14978 		Player->allow_ask_help_timestamp = timestamp(Builtin_messages[MESSAGE_HELP].min_delay);
14979 		Player->ask_help_count++;
14980 	}
14981 }
14982 
14983 // -----------------------------------------------------------------------------
ship_maybe_ask_for_help(ship * sp)14984 void ship_maybe_ask_for_help(ship *sp)
14985 {
14986 	object *objp;
14987 	int multi_team_filter = -1;
14988 
14989 	// First check if the player has reached the maximum number of ask_help's for a mission
14990 	if ((Builtin_messages[MESSAGE_HELP].max_count > -1) && (Player->ask_help_count >= Builtin_messages[MESSAGE_HELP].max_count))
14991 		return;
14992 
14993 	// Check if enough time has elapsed since last help request, if not - leave
14994 	if (!timestamp_elapsed(Player->allow_ask_help_timestamp))
14995 		return;
14996 
14997 	// make sure player is on their team and not a traitor
14998 	if ((Player_ship->team != sp->team) || (Player_ship->team == Iff_traitor))
14999 		return;
15000 
15001 	objp = &Objects[sp->objnum];
15002 
15003 	// don't let the player ask for help!
15004 	if (objp->flags & OF_PLAYER_SHIP)
15005 		return;
15006 
15007 	// determine team filter if TvT
15008 	if(MULTI_TEAM)
15009 		multi_team_filter = sp->team;
15010 
15011 	// handle awacs ship as a special case
15012 	if (Ship_info[sp->ship_info_index].flags & SIF_HAS_AWACS)
15013 	{
15014 		awacs_maybe_ask_for_help(sp, multi_team_filter);
15015 		return;
15016 	}
15017 
15018 	// for now, only have wingman ships request help
15019 	if (!(sp->flags & SF_FROM_PLAYER_WING))
15020 		return;
15021 
15022 	// first check if hull is at a critical level
15023 	if (objp->hull_strength < ASK_HELP_HULL_PERCENT * sp->ship_max_hull_strength)
15024 		goto play_ask_help;
15025 
15026 	// check if shields are near critical level
15027 	if (objp->flags & OF_NO_SHIELDS)
15028 		return;	// no shields on ship, no don't check shield levels
15029 
15030 	if (shield_get_strength(objp) > (ASK_HELP_SHIELD_PERCENT * sp->ship_max_shield_strength))
15031 		return;
15032 
15033 play_ask_help:
15034 
15035 	if (!(Ship_info[sp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER))) //If we're still here, only continue if we're a fighter or bomber.
15036 		return;
15037 
15038 	if (!(sp->flags2 & SF2_NO_BUILTIN_MESSAGES)) // Karajorma - Only unsilenced ships should ask for help
15039 	{
15040 	message_send_builtin_to_player(MESSAGE_HELP, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
15041 	Player->allow_ask_help_timestamp = timestamp(Builtin_messages[MESSAGE_HELP].min_delay);
15042 
15043 	// prevent overlap with death message
15044 	if (timestamp_until(Player->allow_scream_timestamp) < 15000)
15045 		Player->allow_scream_timestamp = timestamp(15000);
15046 
15047 	Player->ask_help_count++;
15048 	}
15049 }
15050 
15051 /**
15052  * The player has just entered death roll, maybe have wingman mourn the loss of the player
15053  */
ship_maybe_lament()15054 void ship_maybe_lament()
15055 {
15056 	int ship_index;
15057 
15058 	// no. because in multiplayer, its funny
15059 	if (Game_mode & GM_MULTIPLAYER)
15060 		return;
15061 
15062 	if (rand() % 4 == 0)
15063 	{
15064 		ship_index = ship_get_random_player_wing_ship(SHIP_GET_UNSILENCED);
15065 		if (ship_index >= 0)
15066 			message_send_builtin_to_player(MESSAGE_PLAYER_DIED, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
15067 	}
15068 }
15069 
15070 /**
15071  * Play a death scream for a ship
15072  */
ship_scream(ship * sp)15073 void ship_scream(ship *sp)
15074 {
15075 	int multi_team_filter = -1;
15076 
15077 	// bogus
15078 	if (sp == NULL)
15079 		return;
15080 
15081 	// multiplayer tvt
15082 	if (MULTI_TEAM)
15083 		multi_team_filter = sp->team;
15084 
15085 	// Bail if the ship is silenced
15086 	if (sp->flags2 & SF2_NO_BUILTIN_MESSAGES)
15087 	{
15088 		return;
15089 	}
15090 
15091 	message_send_builtin_to_player(MESSAGE_WINGMAN_SCREAM, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
15092 	Player->allow_scream_timestamp = timestamp(Builtin_messages[MESSAGE_WINGMAN_SCREAM].min_delay);
15093 	Player->scream_count++;
15094 
15095 	sp->flags |= SF_SHIP_HAS_SCREAMED;
15096 
15097 	// prevent overlap with help messages
15098 	if (timestamp_until(Player->allow_ask_help_timestamp) < 15000)
15099 		Player->allow_ask_help_timestamp = timestamp(15000);
15100 }
15101 
15102 /**
15103  * Ship has just died, maybe play a scream.
15104  */
ship_maybe_scream(ship * sp)15105 void ship_maybe_scream(ship *sp)
15106 {
15107 	// bail if screaming is disabled
15108 	if (sp->flags2 & SF2_NO_DEATH_SCREAM)
15109 		return;
15110 
15111 	// if screaming is enabled, skip all checks
15112 	if (!(sp->flags2 & SF2_ALWAYS_DEATH_SCREAM))
15113 	{
15114 		// only scream x% of the time
15115 		if ( (int)(frand()*100) > Builtin_messages[MESSAGE_WINGMAN_SCREAM].occurrence_chance ) {
15116 			return;
15117 		}
15118 
15119 		// check if enough time has elapsed since last scream; if not, leave
15120 		if (!timestamp_elapsed(Player->allow_scream_timestamp))
15121 			return;
15122 
15123 		// for WCSaga, only do a subset of the checks
15124 		if (!(The_mission.ai_profile->flags2 & AIPF2_PERFORM_FEWER_SCREAM_CHECKS))
15125 		{
15126 			// bail if this ship isn't from the player wing
15127 			if (!(sp->flags & SF_FROM_PLAYER_WING))
15128 				return;
15129 
15130 			// first check if the player has reached the maximum number of screams for a mission
15131 			if ((Builtin_messages[MESSAGE_WINGMAN_SCREAM].max_count > -1) && (Player->scream_count >= Builtin_messages[MESSAGE_WINGMAN_SCREAM].max_count)) {
15132 				return;
15133 			}
15134 
15135 			// if on different teams (i.e. team v. team games in multiplayer), no scream
15136 			if (Player_ship->team != sp->team)
15137 				return;
15138 		}
15139 	}
15140 
15141 	ship_scream(sp);
15142 }
15143 
15144 // maybe tell player that we're running low on ammo
15145 #define PLAYER_LOW_AMMO_MSG_INTERVAL		250000
15146 #define PLAYER_REQUEST_REPAIR_MSG_INTERVAL	240000
15147 #define PLAYER_MAX_LOW_AMMO_MSGS			5
15148 
15149 /**
15150  * This function is only for notifying the player that the ship's ammo is low, without necessarily requesting support (e.g. if support
15151  * is disallowed for this mission).  It is not called if support was successfully requested in maybe_request_support.
15152  */
ship_maybe_tell_about_low_ammo(ship * sp)15153 void ship_maybe_tell_about_low_ammo(ship *sp)
15154 {
15155 	weapon_info *wip;
15156 	int i;
15157 	ship_weapon *swp;
15158 	int multi_team_filter = -1;
15159 
15160 	// we don't want a ship complaining about low ammo after it has just complained about needing support
15161 	if (!timestamp_elapsed(Player->request_repair_timestamp))
15162 		return;
15163 
15164 	if (!timestamp_elapsed(Player->allow_ammo_timestamp))
15165 		return;
15166 
15167 	if (Player_ship->team == Iff_traitor)
15168 		return;
15169 
15170 	// Silent ships should remain just that
15171 	if (sp->flags2 & SF2_NO_BUILTIN_MESSAGES) {
15172 		return;
15173 	}
15174 
15175 	// don't mention low ammo if we're docked, for the same reason as in maybe_request_support
15176 	if (object_is_docked(&Objects[sp->objnum]))
15177 		return;
15178 
15179 	// for now, each ship can only complain about low ammo once a mission to stop it getting repetitive
15180 	if (sp->ammo_low_complaint_count) {
15181 		return;
15182 	}
15183 
15184 	if (Player->low_ammo_complaint_count >= PLAYER_MAX_LOW_AMMO_MSGS) {
15185 		return;
15186 	}
15187 
15188 	swp = &sp->weapons;
15189 
15190 	// stole the code for this from ship_maybe_tell_about_rearm()
15191 	if (sp->flags & SIF_BALLISTIC_PRIMARIES)
15192 	{
15193 		for (i = 0; i < swp->num_primary_banks; i++)
15194 		{
15195 			wip = &Weapon_info[swp->primary_bank_weapons[i]];
15196 
15197 			if (wip->wi_flags2 & WIF2_BALLISTIC)
15198 			{
15199 				if (swp->primary_bank_start_ammo[i] > 0)
15200 				{
15201 					if (swp->primary_bank_ammo[i] / swp->primary_bank_start_ammo[i] < 0.3f)
15202 					{
15203 						// multiplayer tvt
15204 						if(MULTI_TEAM) {
15205 							multi_team_filter = sp->team;
15206 						}
15207 
15208 						message_send_builtin_to_player(MESSAGE_PRIMARIES_LOW, sp, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter);
15209 
15210 						Player->allow_ammo_timestamp = timestamp(PLAYER_LOW_AMMO_MSG_INTERVAL);
15211 
15212 						// better reset this one too
15213 						Player->request_repair_timestamp = timestamp(PLAYER_REQUEST_REPAIR_MSG_INTERVAL);
15214 
15215 						Player->low_ammo_complaint_count++;
15216 						sp->ammo_low_complaint_count++;
15217 						break;
15218 					}
15219 				}
15220 			}
15221 		}
15222 	}
15223 }
15224 
15225 
15226 /**
15227  * Tell player that we've requested a support ship
15228  */
ship_maybe_tell_about_rearm(ship * sp)15229 void ship_maybe_tell_about_rearm(ship *sp)
15230 {
15231 	weapon_info *wip;
15232 
15233 	if (!timestamp_elapsed(Player->request_repair_timestamp))
15234 		return;
15235 
15236 	if (Player_ship->team == Iff_traitor)
15237 		return;
15238 
15239 	// Silent ships should remain just that
15240 	if (sp->flags2 & SF2_NO_BUILTIN_MESSAGES)
15241 		return;
15242 
15243 	// AL 1-4-98:	If ship integrity is low, tell player you want to get repaired.  Otherwise, tell
15244 	// the player you want to get re-armed.
15245 
15246 	int message_type = -1;
15247 	int heavily_damaged = (get_hull_pct(&Objects[sp->objnum]) < 0.4);
15248 
15249 	if (heavily_damaged || (sp->flags & SF_DISABLED))
15250 	{
15251 		message_type = MESSAGE_REPAIR_REQUEST;
15252 	}
15253 	else
15254 	{
15255 		int i;
15256 		ship_weapon *swp;
15257 
15258 		swp = &sp->weapons;
15259 		for (i = 0; i < swp->num_secondary_banks; i++)
15260 		{
15261 			if (swp->secondary_bank_start_ammo[i] > 0)
15262 			{
15263 				if (swp->secondary_bank_ammo[i] / swp->secondary_bank_start_ammo[i] < 0.5f)
15264 				{
15265 					message_type = MESSAGE_REARM_REQUEST;
15266 					break;
15267 				}
15268 			}
15269 		}
15270 
15271 		// also check ballistic primaries - Goober5000
15272 		if (sp->flags & SIF_BALLISTIC_PRIMARIES)
15273 		{
15274 			for (i = 0; i < swp->num_primary_banks; i++)
15275 			{
15276 				wip = &Weapon_info[swp->primary_bank_weapons[i]];
15277 
15278 				if (wip->wi_flags2 & WIF2_BALLISTIC)
15279 				{
15280 					if (swp->primary_bank_start_ammo[i] > 0)
15281 					{
15282 						if (swp->primary_bank_ammo[i] / swp->primary_bank_start_ammo[i] < 0.3f)
15283 						{
15284 							message_type = MESSAGE_REARM_PRIMARIES;
15285 							break;
15286 						}
15287 					}
15288 				}
15289 			}
15290 		}
15291 	}
15292 
15293 	int multi_team_filter = -1;
15294 
15295 	// multiplayer tvt
15296 	if(MULTI_TEAM)
15297 		multi_team_filter = sp->team;
15298 
15299 
15300 	if (message_type >= 0)
15301 	{
15302 		if (rand() & 1)
15303 			message_send_builtin_to_player(message_type, sp, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter);
15304 
15305 		Player->request_repair_timestamp = timestamp(PLAYER_REQUEST_REPAIR_MSG_INTERVAL);
15306 	}
15307 }
15308 
15309 // The current primary weapon or link status for a ship has changed.. notify clients if multiplayer
15310 //
15311 // input:	sp			=>	pointer to ship that modified primaries
ship_primary_changed(ship * sp)15312 void ship_primary_changed(ship *sp)
15313 {
15314 	int i;
15315 	ship_weapon	*swp;
15316 	swp = &sp->weapons;
15317 
15318 
15319 	if (sp->flags & SF_PRIMARY_LINKED) {
15320 		// if we are linked now find any body who is down and flip them up
15321 		for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++) {
15322 			if (swp->primary_animation_position[i] == MA_POS_NOT_SET) {
15323 				if ( model_anim_start_type(sp, TRIGGER_TYPE_PRIMARY_BANK, i, 1) ) {
15324 					swp->primary_animation_done_time[i] = model_anim_get_time_type(sp, TRIGGER_TYPE_PRIMARY_BANK, i);
15325 					swp->primary_animation_position[i] = MA_POS_SET;
15326 				} else {
15327 					swp->primary_animation_position[i] = MA_POS_READY;
15328 				}
15329 			}
15330 		}
15331 	} else {
15332 		// find anything that is up that shouldn't be
15333 		for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++) {
15334 			if (i == swp->current_primary_bank) {
15335 				// if the current bank is down raise it up
15336 				if (swp->primary_animation_position[i] == MA_POS_NOT_SET) {
15337 					if ( model_anim_start_type(sp, TRIGGER_TYPE_PRIMARY_BANK, i, 1) ) {
15338 						swp->primary_animation_done_time[i] = model_anim_get_time_type(sp, TRIGGER_TYPE_PRIMARY_BANK, i);
15339 						swp->primary_animation_position[i] = MA_POS_SET;
15340 					} else {
15341 						swp->primary_animation_position[i] = MA_POS_READY;
15342 					}
15343 				}
15344 			} else {
15345 				// everyone else should be down, if they are not make them so
15346 				if (swp->primary_animation_position[i] != MA_POS_NOT_SET) {
15347 					model_anim_start_type(sp, TRIGGER_TYPE_PRIMARY_BANK, i, -1);
15348 					swp->primary_animation_position[i] = MA_POS_NOT_SET;
15349 				}
15350 			}
15351 		}
15352 	}
15353 
15354 #if 0
15355 	// we only need to deal with multiplayer issues for now, so bail it not multiplayer
15356 	if ( !(Game_mode & GM_MULTIPLAYER) )
15357 		return;
15358 
15359 	Assert(sp);
15360 
15361 	if ( MULTIPLAYER_MASTER )
15362 		send_ship_weapon_change( sp, MULTI_PRIMARY_CHANGED, swp->current_primary_bank, (sp->flags & SF_PRIMARY_LINKED)?1:0 );
15363 #endif
15364 }
15365 
15366 // The current secondary weapon or dual-fire status for a ship has changed.. notify clients if multiplayer
15367 //
15368 // input:	sp					=>	pointer to ship that modified secondaries
ship_secondary_changed(ship * sp)15369 void ship_secondary_changed(ship *sp)
15370 {
15371 	Assert( sp != NULL );
15372 
15373 	int i;
15374 	ship_weapon	*swp = &sp->weapons;
15375 
15376 	// find anything that is up that shouldn't be
15377 	if (timestamp() > 10) {
15378 		for (i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++) {
15379 			if (i == swp->current_secondary_bank) {
15380 				// if the current bank is down raise it up
15381 				if (swp->secondary_animation_position[i] == MA_POS_NOT_SET) {
15382 					if ( model_anim_start_type(sp, TRIGGER_TYPE_SECONDARY_BANK, i, 1) ) {
15383 						swp->secondary_animation_done_time[i] = model_anim_get_time_type(sp, TRIGGER_TYPE_SECONDARY_BANK, i);
15384 						swp->secondary_animation_position[i] = MA_POS_SET;
15385 					} else {
15386 						swp->secondary_animation_position[i] = MA_POS_READY;
15387 					}
15388 				}
15389 			} else {
15390 				// everyone else should be down, if they are not make them so
15391 				if (swp->secondary_animation_position[i] != MA_POS_NOT_SET) {
15392 					model_anim_start_type(sp, TRIGGER_TYPE_SECONDARY_BANK, i, -1);
15393 					swp->secondary_animation_position[i] = MA_POS_NOT_SET;
15394 				}
15395 			}
15396 		}
15397 	}
15398 
15399 #if 0
15400 	// we only need to deal with multiplayer issues for now, so bail it not multiplayer
15401 	if ( !(Game_mode & GM_MULTIPLAYER) ){
15402 		return;
15403 	}
15404 
15405 	Assert(sp);
15406 
15407 	if ( MULTIPLAYER_MASTER )
15408 		send_ship_weapon_change( sp, MULTI_SECONDARY_CHANGED, swp->current_secondary_bank, (sp->flags & SF_SECONDARY_DUAL_FIRE)?1:0 );
15409 #endif
15410 }
15411 
ship_get_SIF(ship * shipp)15412 int ship_get_SIF(ship *shipp)
15413 {
15414 	return Ship_info[shipp->ship_info_index].flags;
15415 }
15416 
ship_get_SIF(int sh)15417 int ship_get_SIF(int sh)
15418 {
15419 	return Ship_info[Ships[sh].ship_info_index].flags;
15420 }
15421 
ship_get_by_signature(int signature)15422 int ship_get_by_signature(int signature)
15423 {
15424 	ship_obj *so;
15425 
15426 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
15427 		// if we found a matching ship object signature
15428 		if((Objects[so->objnum].signature == signature) && (Objects[so->objnum].type == OBJ_SHIP)){
15429 			return Objects[so->objnum].instance;
15430 		}
15431 	}
15432 
15433 	// couldn't find the ship
15434 	return -1;
15435 }
15436 
ship_get_type_info(object * objp)15437 ship_type_info *ship_get_type_info(object *objp)
15438 {
15439 	Assert(objp != NULL);
15440 	Assert(objp->type == OBJ_SHIP);
15441 	Assert(objp->instance > -1);
15442 	Assert(Ships[objp->instance].ship_info_index > -1);
15443 	Assert(Ship_info[Ships[objp->instance].ship_info_index].class_type > -1);
15444 
15445 	return &Ship_types[Ship_info[Ships[objp->instance].ship_info_index].class_type];
15446 }
15447 
15448 /**
15449  * Called when the cargo of a ship is revealed.
15450  *
15451  * Happens at two different locations (at least when this function was written), one for the player, and one for AI ships.
15452  * Need to send stuff to clients in multiplayer game.
15453  */
ship_do_cargo_revealed(ship * shipp,int from_network)15454 void ship_do_cargo_revealed( ship *shipp, int from_network )
15455 {
15456 	// don't do anything if we already know the cargo
15457 	if ( shipp->flags & SF_CARGO_REVEALED ){
15458 		return;
15459 	}
15460 
15461 	nprintf(("Network", "Revealing cargo for %s\n", shipp->ship_name));
15462 
15463 	// send the packet if needed
15464 	if ( (Game_mode & GM_MULTIPLAYER) && !from_network ){
15465 		send_cargo_revealed_packet( shipp );
15466 	}
15467 
15468 	shipp->flags |= SF_CARGO_REVEALED;
15469 	shipp->time_cargo_revealed = Missiontime;
15470 
15471 	// if the cargo is something other than "nothing", then make a log entry
15472 	if ( stricmp(Cargo_names[shipp->cargo1 & CARGO_INDEX_MASK], NOX("nothing")) ){
15473 		mission_log_add_entry(LOG_CARGO_REVEALED, shipp->ship_name, NULL, (shipp->cargo1 & CARGO_INDEX_MASK) );
15474 	}
15475 }
15476 
ship_do_cap_subsys_cargo_revealed(ship * shipp,ship_subsys * subsys,int from_network)15477 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network )
15478 {
15479 	// don't do anything if we already know the cargo
15480 	if (subsys->flags & SSF_CARGO_REVEALED) {
15481 		return;
15482 	}
15483 
15484 	nprintf(("Network", "Revealing cap ship subsys cargo for %s\n", shipp->ship_name));
15485 
15486 	// send the packet if needed
15487 	if ( (Game_mode & GM_MULTIPLAYER) && !from_network ){
15488 		int subsystem_index = ship_get_index_from_subsys(subsys, shipp->objnum);
15489 		send_subsystem_cargo_revealed_packet( shipp, subsystem_index );
15490 	}
15491 
15492 	subsys->flags |= SSF_CARGO_REVEALED;
15493 	subsys->time_subsys_cargo_revealed = Missiontime;
15494 
15495 	// if the cargo is something other than "nothing", then make a log entry
15496 	if ( stricmp(Cargo_names[subsys->subsys_cargo_name & CARGO_INDEX_MASK], NOX("nothing")) ){
15497 		mission_log_add_entry(LOG_CAP_SUBSYS_CARGO_REVEALED, shipp->ship_name, subsys->system_info->subobj_name, (subsys->subsys_cargo_name & CARGO_INDEX_MASK) );
15498 	}
15499 }
15500 
15501 /**
15502  * alled when the cargo of a ship is hidden by the sexp.
15503  *
15504  * Need to send stuff to clients in multiplayer game.
15505  */
ship_do_cargo_hidden(ship * shipp,int from_network)15506 void ship_do_cargo_hidden( ship *shipp, int from_network )
15507 {
15508 	// don't do anything if the cargo is already hidden
15509 	if ( !(shipp->flags & SF_CARGO_REVEALED) )
15510 	{
15511 		return;
15512 	}
15513 
15514 	nprintf(("Network", "Hiding cargo for %s\n", shipp->ship_name));
15515 
15516 	// send the packet if needed
15517 	if ( (Game_mode & GM_MULTIPLAYER) && !from_network ){
15518 		send_cargo_hidden_packet( shipp );
15519 	}
15520 
15521 	shipp->flags &= ~SF_CARGO_REVEALED;
15522 
15523 	// don't log that the cargo was hidden and don't reset the time cargo revealed
15524 }
15525 
ship_do_cap_subsys_cargo_hidden(ship * shipp,ship_subsys * subsys,int from_network)15526 void ship_do_cap_subsys_cargo_hidden( ship *shipp, ship_subsys *subsys, int from_network )
15527 {
15528 	// don't do anything if the cargo is already hidden
15529 	if (!(subsys->flags & SSF_CARGO_REVEALED))
15530 	{
15531 		return;
15532 	}
15533 
15534 	nprintf(("Network", "Hiding cap ship subsys cargo for %s\n", shipp->ship_name));
15535 
15536 	// send the packet if needed
15537 	if ( (Game_mode & GM_MULTIPLAYER) && !from_network ){
15538 		int subsystem_index = ship_get_index_from_subsys(subsys, shipp->objnum);
15539 		send_subsystem_cargo_hidden_packet( shipp, subsystem_index );
15540 	}
15541 
15542 	subsys->flags &= ~SSF_CARGO_REVEALED;
15543 
15544 	// don't log that the cargo was hidden and don't reset the time cargo revealed
15545 }
15546 
15547 /**
15548  * Return the range of the currently selected secondary weapon
15549  *
15550  * NOTE: If there is no missiles left in the current bank, range returned is 0
15551  *
15552  * @param shipp Pointer to ship from which currently selected secondary weapon will be ranged
15553  */
ship_get_secondary_weapon_range(ship * shipp)15554 float ship_get_secondary_weapon_range(ship *shipp)
15555 {
15556 	float srange=0.0f;
15557 
15558 	ship_weapon	*swp;
15559 	swp = &shipp->weapons;
15560 	if ( swp->current_secondary_bank >= 0 ) {
15561 		weapon_info	*wip;
15562 		int bank=swp->current_secondary_bank;
15563 		if (swp->secondary_bank_weapons[bank] >= 0) {
15564 			wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
15565 			if ( swp->secondary_bank_ammo[bank] > 0 ) {
15566 				srange = wip->max_speed * wip->lifetime;
15567 			}
15568 		}
15569 	}
15570 
15571 	return srange;
15572 }
15573 
15574 // Goober5000 - added for ballistic primaries
15575 /**
15576  * Determine the number of primary ammo units allowed max for a ship
15577  */
get_max_ammo_count_for_primary_bank(int ship_class,int bank,int ammo_type)15578 int get_max_ammo_count_for_primary_bank(int ship_class, int bank, int ammo_type)
15579 {
15580 	float capacity, size;
15581 
15582 	if (!(Ship_info[ship_class].flags & SIF_BALLISTIC_PRIMARIES))
15583 	{
15584 		return 0;
15585 	}
15586 
15587 	capacity = (float) Ship_info[ship_class].primary_bank_ammo_capacity[bank];
15588 	size = (float) Weapon_info[ammo_type].cargo_size;
15589 	return  fl2i((capacity / size)+0.5f);
15590 }
15591 
15592 /**
15593  * Determine the number of secondary ammo units (missile/bomb) allowed max for a ship
15594  */
get_max_ammo_count_for_bank(int ship_class,int bank,int ammo_type)15595 int get_max_ammo_count_for_bank(int ship_class, int bank, int ammo_type)
15596 {
15597 	float capacity, size;
15598 
15599 	if (ship_class < 0 || bank < 0 || ammo_type < 0) {
15600 		return 0;
15601 	} else {
15602 		capacity = (float) Ship_info[ship_class].secondary_bank_ammo_capacity[bank];
15603 		size = (float) Weapon_info[ammo_type].cargo_size;
15604 		return (int) (capacity / size);
15605 	}
15606 }
15607 
15608 /**
15609  * Page in bitmaps for all the ships in this level
15610  */
ship_page_in()15611 void ship_page_in()
15612 {
15613 	int i, j, k;
15614 	int num_subsystems_needed = 0;
15615 
15616 	int *ship_class_used = NULL;
15617 
15618 	ship_class_used = new int[Num_ship_classes];
15619 
15620 	Verify( ship_class_used != NULL );
15621 
15622 	// Mark all ship classes as not used
15623 	memset( ship_class_used, 0, Num_ship_classes * sizeof(int) );
15624 
15625 	// Mark any support ship types as used
15626 	for (i = 0; i < Num_ship_classes; i++)	{
15627 		if (Ship_info[i].flags & SIF_SUPPORT) {
15628 			ship_info *sip = &Ship_info[i];
15629 			nprintf(( "Paging", "Found support ship '%s'\n", sip->name ));
15630 			ship_class_used[i]++;
15631 
15632 			num_subsystems_needed += sip->n_subsystems;
15633 
15634 			// load the darn model and page in textures
15635 			sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
15636 
15637 			if (sip->model_num >= 0) {
15638 				model_page_in_textures(sip->model_num, i);
15639 			}
15640 		}
15641 	}
15642 
15643 	// Mark any ships in the mission as used
15644 	for (i = 0; i < MAX_SHIPS; i++)	{
15645 		if (Ships[i].objnum < 0)
15646 			continue;
15647 
15648 		nprintf(( "Paging","Found ship '%s'\n", Ships[i].ship_name ));
15649 		ship_class_used[Ships[i].ship_info_index]++;
15650 
15651 		// check if we are going to use a Knossos device and make sure the special warp ani gets pre-loaded
15652 		if ( Ship_info[Ships[i].ship_info_index].flags & SIF_KNOSSOS_DEVICE )
15653 			Knossos_warp_ani_used = 1;
15654 
15655 		// mark any weapons as being used, saves memory and time if we don't load them all
15656 		ship_weapon *swp = &Ships[i].weapons;
15657 
15658 		for (j = 0; j < swp->num_primary_banks; j++)
15659 			weapon_mark_as_used(swp->primary_bank_weapons[j]);
15660 
15661 		for (j = 0; j < swp->num_secondary_banks; j++)
15662 			weapon_mark_as_used(swp->secondary_bank_weapons[j]);
15663 
15664 		// get weapons for all capship subsystems (turrets)
15665 		ship_subsys *ptr = GET_FIRST(&Ships[i].subsys_list);
15666 
15667 		while (ptr != END_OF_LIST(&Ships[i].subsys_list)) {
15668 			for (k = 0; k < MAX_SHIP_PRIMARY_BANKS; k++)
15669 				weapon_mark_as_used(ptr->weapons.primary_bank_weapons[k]);
15670 
15671 			for (k = 0; k < MAX_SHIP_SECONDARY_BANKS; k++)
15672 				weapon_mark_as_used(ptr->weapons.secondary_bank_weapons[k]);
15673 
15674 			ptr = GET_NEXT(ptr);
15675 		}
15676 
15677 		ship_info *sip = &Ship_info[Ships[i].ship_info_index];
15678 
15679 		// page in all of the textures if the model is already loaded
15680 		if (sip->model_num >= 0) {
15681 			nprintf(( "Paging", "Paging in textures for ship '%s'\n", Ships[i].ship_name ));
15682 			model_page_in_textures(sip->model_num, Ships[i].ship_info_index);
15683 			// need to make sure and do this again, after we are sure that all of the textures are ready
15684 			ship_init_afterburners( &Ships[i] );
15685 		}
15686 
15687 		//WMC - Since this is already in-mission, ignore the warpin effect.
15688 		Ships[i].warpout_effect->pageIn();
15689 
15690 		// don't need this one anymore, it's already been accounted for
15691 	//	num_subsystems_needed += Ship_info[Ships[i].ship_info_index].n_subsystems;
15692 	}
15693 
15694 	// Mark any ships that might warp in in the future as used
15695 	for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp)) {
15696 		nprintf(( "Paging", "Found future arrival ship '%s'\n", p_objp->name ));
15697 		ship_class_used[p_objp->ship_class]++;
15698 
15699 		// This will go through Subsys_index[] and grab all weapons: primary, secondary, and turrets
15700 		for (i = p_objp->subsys_index; i < (p_objp->subsys_index + p_objp->subsys_count); i++) {
15701 			for (j = 0; j < MAX_SHIP_PRIMARY_BANKS; j++) {
15702 				if (Subsys_status[i].primary_banks[j] >= 0)
15703 					weapon_mark_as_used(Subsys_status[i].primary_banks[j]);
15704 			}
15705 
15706 			for (j = 0; j < MAX_SHIP_SECONDARY_BANKS; j++) {
15707 				if (Subsys_status[i].secondary_banks[j] >= 0)
15708 					weapon_mark_as_used(Subsys_status[i].secondary_banks[j]);
15709 			}
15710 		}
15711 
15712 		// page in any replacement textures
15713 		if (Ship_info[p_objp->ship_class].model_num >= 0) {
15714 			nprintf(( "Paging", "Paging in textures for future arrival ship '%s'\n", p_objp->name ));
15715 			model_page_in_textures(Ship_info[p_objp->ship_class].model_num, p_objp->ship_class);
15716 		}
15717 
15718 		num_subsystems_needed += Ship_info[p_objp->ship_class].n_subsystems;
15719 	}
15720 
15721 	// pre-allocate the subsystems, this really only needs to happen for ships
15722 	// which don't exist yet (ie, ships NOT in Ships[])
15723 	if (!ship_allocate_subsystems(num_subsystems_needed, true)) {
15724 		Error(LOCATION, "Attempt to page in new subsystems subsystems failed because mission file contains more than %d subsystems", (NUM_SHIP_SUBSYSTEM_SETS* NUM_SHIP_SUBSYSTEMS_PER_SET));
15725 	}
15726 
15727 	mprintf(("About to page in ships!\n"));
15728 
15729 	// Page in all the ship classes that are used on this level
15730 	int num_ship_types_used = 0;
15731 	int test_id = -1;
15732 
15733 	memset( fireball_used, 0, sizeof(int) * MAX_FIREBALL_TYPES );
15734 
15735 	for (i = 0; i < Num_ship_classes; i++) {
15736 		if ( !ship_class_used[i] )
15737 			continue;
15738 
15739 		ship_info *sip = &Ship_info[i];
15740 		int model_previously_loaded = -1;
15741 		int ship_previously_loaded = -1;
15742 
15743 		num_ship_types_used++;
15744 
15745 		// Page in the small hud icons for each ship
15746 		hud_ship_icon_page_in(sip);
15747 
15748 		// See if this model was previously loaded by another ship
15749 		for (j = 0; j < Num_ship_classes; j++) {
15750 			if ( (Ship_info[j].model_num > -1) && !stricmp(sip->pof_file, Ship_info[j].pof_file) ) {
15751 				// Model already loaded
15752 				model_previously_loaded = Ship_info[j].model_num;
15753 
15754 				if ((sip->n_subsystems > 0) && (sip->subsystems[0].model_num > -1)) {
15755 					ship_previously_loaded = j;
15756 				}
15757 
15758 				// the model should already be loaded so this wouldn't take long, but
15759 				// we need to make sure that the load count for the model is correct
15760 				test_id = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
15761 				Assert( test_id == model_previously_loaded );
15762 
15763 				break;
15764 			}
15765 		}
15766 
15767 		// If the model is previously loaded...
15768 		if (model_previously_loaded >= 0) {
15769 			// If previously loaded model isn't the same ship class...)
15770 			if (ship_previously_loaded != i) {
15771 				// update the model number.
15772 				sip->model_num = model_previously_loaded;
15773 
15774 				for (j = 0; j < sip->n_subsystems; j++)
15775 					sip->subsystems[j].model_num = -1;
15776 
15777 				ship_copy_subsystem_fixup(sip);
15778 
15779 #ifndef NDEBUG
15780 				for (j = 0; j < sip->n_subsystems; j++) {
15781 					if (sip->subsystems[j].model_num != sip->model_num) {
15782 						polymodel *sip_pm = (sip->model_num >= 0) ? model_get(sip->model_num) : NULL;
15783 						polymodel *subsys_pm = (sip->subsystems[j].model_num >= 0) ? model_get(sip->subsystems[j].model_num) : NULL;
15784 						Warning(LOCATION, "After ship_copy_subsystem_fixup, ship '%s' does not have subsystem '%s' linked into the model file, '%s'.\n\n(Ship_info model is '%s' and subsystem model is '%s'.)", sip->name, sip->subsystems[j].subobj_name, sip->pof_file, (sip_pm != NULL) ? sip_pm->filename : "NULL", (subsys_pm != NULL) ? subsys_pm->filename : "NULL");
15785 					}
15786 				}
15787 #endif
15788 			} else {
15789 				// Just to be safe (I mean to check that my code works...)
15790 				Assert( sip->model_num >= 0 );
15791 				Assert( sip->model_num == model_previously_loaded );
15792 
15793 #ifndef NDEBUG
15794 				for (j = 0; j < sip->n_subsystems; j++) {
15795 					if (sip->subsystems[j].model_num != sip->model_num) {
15796 						polymodel *sip_pm = (sip->model_num >= 0) ? model_get(sip->model_num) : NULL;
15797 						polymodel *subsys_pm = (sip->subsystems[j].model_num >= 0) ? model_get(sip->subsystems[j].model_num) : NULL;
15798 						Warning(LOCATION, "Without ship_copy_subsystem_fixup, ship '%s' does not have subsystem '%s' linked into the model file, '%s'.\n\n(Ship_info model is '%s' and subsystem model is '%s'.)", sip->name, sip->subsystems[j].subobj_name, sip->pof_file, (sip_pm != NULL) ? sip_pm->filename : "NULL", (subsys_pm != NULL) ? subsys_pm->filename : "NULL");
15799 					}
15800 				}
15801 #endif
15802 			}
15803 		} else {
15804 			// Model not loaded, so load it
15805 			sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
15806 
15807 			Assert( sip->model_num >= 0 );
15808 
15809 #ifndef NDEBUG
15810 			// Verify that all the subsystem model numbers are updated
15811 			for (j = 0; j < sip->n_subsystems; j++)
15812 				Assertion( sip->subsystems[j].model_num == sip->model_num, "Model reference for subsystem %s (model num: %d) on model %s (model num: %d) is invalid.\n", sip->subsystems[j].name, sip->subsystems[j].model_num, sip->pof_file, sip->model_num );	// JAS
15813 #endif
15814 		}
15815 
15816 		// more weapon marking, the weapon info in Ship_info[] is the default
15817 		// loadout which isn't specified by missionparse unless it's different
15818 		for (j = 0; j < sip->num_primary_banks; j++)
15819 			weapon_mark_as_used(sip->primary_bank_weapons[j]);
15820 
15821 		for (j = 0; j < sip->num_secondary_banks; j++)
15822 			weapon_mark_as_used(sip->secondary_bank_weapons[j]);
15823 
15824 		weapon_mark_as_used(sip->cmeasure_type);
15825 
15826 		for (j = 0; j < sip->n_subsystems; j++) {
15827 			model_subsystem *msp = &sip->subsystems[j];
15828 
15829 			for (k = 0; k < MAX_SHIP_PRIMARY_BANKS; k++)
15830 				weapon_mark_as_used(msp->primary_banks[k]);
15831 
15832 			for (k = 0; k < MAX_SHIP_SECONDARY_BANKS; k++)
15833 				weapon_mark_as_used(msp->secondary_banks[k]);
15834 		}
15835 
15836 		// Page in the shockwave stuff. -C
15837 		shockwave_create_info_load(&sip->shockwave);
15838 		if(sip->explosion_bitmap_anims.size() > 0) {
15839 			int num_fireballs = sip->explosion_bitmap_anims.size();
15840 			for(j = 0; j < num_fireballs; j++){
15841 				fireball_used[sip->explosion_bitmap_anims[j]] = 1;
15842 			}
15843 		} else if(sip->class_type >= 0 && Ship_types[sip->class_type].explosion_bitmap_anims.size() > 0) {
15844 			int num_fireballs = Ship_types[sip->class_type].explosion_bitmap_anims.size();
15845 			for(j = 0; j < num_fireballs; j++){
15846 				fireball_used[Ship_types[sip->class_type].explosion_bitmap_anims[j]] = 1;
15847 			}
15848 		}
15849 	}
15850 
15851 	nprintf(( "Paging", "There are %d ship classes used in this mission.\n", num_ship_types_used ));
15852 
15853 
15854 	// Page in the thruster effects
15855 	// Make sure thrusters are loaded
15856 	if (!Thrust_anim_inited)
15857 		ship_init_thrusters();
15858 
15859 	thrust_info *thruster;
15860 	for (i = 0; i < (int)Species_info.size(); i++) {
15861 		thruster = &Species_info[i].thruster_info;
15862 
15863 		bm_page_in_texture( thruster->flames.normal.first_frame, thruster->flames.normal.num_frames );
15864 		bm_page_in_texture( thruster->flames.afterburn.first_frame, thruster->flames.afterburn.num_frames );
15865 
15866 		// glows are really not anims
15867 		bm_page_in_texture( thruster->glow.normal.first_frame );
15868 		bm_page_in_texture( thruster->glow.afterburn.first_frame );
15869 	}
15870 
15871 	// page in insignia bitmaps
15872 	if(Game_mode & GM_MULTIPLAYER){
15873 		for(i=0; i<MAX_PLAYERS; i++){
15874 			if(MULTI_CONNECTED(Net_players[i]) && (Net_players[i].m_player != NULL) && (Net_players[i].m_player->insignia_texture >= 0)){
15875 				bm_page_in_xparent_texture(Net_players[i].m_player->insignia_texture);
15876 			}
15877 		}
15878 	} else {
15879 		if((Player != NULL) && (Player->insignia_texture >= 0)){
15880 			bm_page_in_xparent_texture(Player->insignia_texture);
15881 		}
15882 	}
15883 
15884 	// page in wing insignia bitmaps - Goober5000
15885 	for (i = 0; i < MAX_WINGS; i++)
15886 	{
15887 		if (Wings[i].wing_insignia_texture >= 0)
15888 			bm_page_in_xparent_texture(Wings[i].wing_insignia_texture);
15889 	}
15890 
15891 	// page in replacement textures - Goober5000
15892 	for (i = 0; i < MAX_SHIPS; i++)
15893 	{
15894 		// is this a valid ship?
15895 		if (Ships[i].objnum >= 0)
15896 		{
15897 			// do we have any textures?
15898 			if (Ships[i].ship_replacement_textures != NULL)
15899 			{
15900 				// page in replacement textures
15901 				for (j=0; j<MAX_REPLACEMENT_TEXTURES; j++)
15902 				{
15903 					if (Ships[i].ship_replacement_textures[j] > -1)
15904 					{
15905 						bm_page_in_texture( Ships[i].ship_replacement_textures[j] );
15906 					}
15907 				}
15908 			}
15909 		}
15910 	}
15911 
15912 	// should never be NULL, this entire function wouldn't work
15913 	delete[] ship_class_used;
15914 	ship_class_used = NULL;
15915 
15916 }
15917 
15918 // Goober5000 - called from ship_page_in()
ship_page_in_textures(int ship_index)15919 void ship_page_in_textures(int ship_index)
15920 {
15921 	int i;
15922 	ship_info *sip;
15923 
15924 	if ( (ship_index < 0) || (ship_index > Num_ship_classes) )
15925 		return;
15926 
15927 
15928 	sip = &Ship_info[ship_index];
15929 
15930 	// afterburner
15931 	if ( !generic_bitmap_load(&sip->afterburner_trail) )
15932 		bm_page_in_texture(sip->afterburner_trail.bitmap_id);
15933 
15934 	// Wanderer - just copying over Bobboau's code...
15935 	if ( !generic_anim_load(&sip->thruster_flame_info.normal) )
15936 		bm_page_in_texture(sip->thruster_flame_info.normal.first_frame);
15937 
15938 	if ( !generic_anim_load(&sip->thruster_flame_info.afterburn) )
15939 		bm_page_in_texture(sip->thruster_flame_info.afterburn.first_frame);
15940 
15941 	// Bobboau's thruster bitmaps
15942 	// the first set has to be loaded a special way
15943 	if ( !thruster_glow_anim_load(&sip->thruster_glow_info.normal) )
15944 		bm_page_in_texture(sip->thruster_glow_info.normal.first_frame);
15945 
15946 	if ( !thruster_glow_anim_load(&sip->thruster_glow_info.afterburn) )
15947 		bm_page_in_texture(sip->thruster_glow_info.afterburn.first_frame);
15948 
15949 	// everything else is loaded normally
15950 	if ( !generic_bitmap_load(&sip->thruster_secondary_glow_info.normal) )
15951 		bm_page_in_texture(sip->thruster_secondary_glow_info.normal.bitmap_id);
15952 
15953 	if ( !generic_bitmap_load(&sip->thruster_secondary_glow_info.afterburn) )
15954 		bm_page_in_texture(sip->thruster_secondary_glow_info.afterburn.bitmap_id);
15955 
15956 	if ( !generic_bitmap_load(&sip->thruster_tertiary_glow_info.normal) )
15957 		bm_page_in_texture(sip->thruster_tertiary_glow_info.normal.bitmap_id);
15958 
15959 	if ( !generic_bitmap_load(&sip->thruster_tertiary_glow_info.afterburn) )
15960 		bm_page_in_texture(sip->thruster_tertiary_glow_info.afterburn.bitmap_id);
15961 
15962 	// splodeing bitmap
15963 	if ( VALID_FNAME(sip->splodeing_texture_name) ) {
15964 		sip->splodeing_texture = bm_load(sip->splodeing_texture_name);
15965 		bm_page_in_texture(sip->splodeing_texture);
15966 	}
15967 
15968 	// thruster/particle bitmaps
15969 	for (i = 0; i < (int)sip->normal_thruster_particles.size(); i++) {
15970 		generic_anim_load(&sip->normal_thruster_particles[i].thruster_bitmap);
15971 		bm_page_in_texture(sip->normal_thruster_particles[i].thruster_bitmap.first_frame);
15972 	}
15973 
15974 	for (i = 0; i < (int)sip->afterburner_thruster_particles.size(); i++) {
15975 		generic_anim_load(&sip->afterburner_thruster_particles[i].thruster_bitmap);
15976 		bm_page_in_texture(sip->afterburner_thruster_particles[i].thruster_bitmap.first_frame);
15977 	}
15978 }
15979 
15980 
15981 #define PAGE_OUT_TEXTURE(x) {	\
15982 	if ( (x) >= 0 ) {	\
15983 		if (release) {	\
15984 			bm_release( (x) );	\
15985 			(x) = -1;	\
15986 		} else {	\
15987 			bm_unload( (x) );	\
15988 		}	\
15989 	}	\
15990 }
15991 
15992 /**
15993  * Unload all textures for a given ship
15994  */
ship_page_out_textures(int ship_index,bool release)15995 void ship_page_out_textures(int ship_index, bool release)
15996 {
15997 	int i;
15998 	ship_info *sip;
15999 
16000 	if ( (ship_index < 0) || (ship_index > Num_ship_classes) )
16001 		return;
16002 
16003 
16004 	sip = &Ship_info[ship_index];
16005 
16006 	// afterburner
16007 	PAGE_OUT_TEXTURE(sip->afterburner_trail.bitmap_id);
16008 
16009 	// thruster bitmaps
16010 	PAGE_OUT_TEXTURE(sip->thruster_flame_info.normal.first_frame);
16011 	PAGE_OUT_TEXTURE(sip->thruster_flame_info.afterburn.first_frame);
16012 	PAGE_OUT_TEXTURE(sip->thruster_glow_info.normal.first_frame);
16013 	PAGE_OUT_TEXTURE(sip->thruster_glow_info.afterburn.first_frame);
16014 	PAGE_OUT_TEXTURE(sip->thruster_secondary_glow_info.normal.bitmap_id);
16015 	PAGE_OUT_TEXTURE(sip->thruster_secondary_glow_info.afterburn.bitmap_id);
16016 	PAGE_OUT_TEXTURE(sip->thruster_tertiary_glow_info.normal.bitmap_id);
16017 	PAGE_OUT_TEXTURE(sip->thruster_tertiary_glow_info.afterburn.bitmap_id);
16018 
16019 	// slodeing bitmap
16020 	PAGE_OUT_TEXTURE(sip->splodeing_texture);
16021 
16022 	// thruster/particle bitmaps
16023 	for (i = 0; i < (int)sip->normal_thruster_particles.size(); i++)
16024 		PAGE_OUT_TEXTURE(sip->normal_thruster_particles[i].thruster_bitmap.first_frame);
16025 
16026 	for (i = 0; i < (int)sip->afterburner_thruster_particles.size(); i++)
16027 		PAGE_OUT_TEXTURE(sip->afterburner_thruster_particles[i].thruster_bitmap.first_frame);
16028 }
16029 
16030 // function to return true if support ships are allowed in the mission for the given object.
16031 //	In single player, must be friendly and not Shivan. (Goober5000 - Shivans can now have support)
16032 //	In multiplayer -- to be coded by Mark Allender after 5/4/98 -- MK, 5/4/98
is_support_allowed(object * objp,bool do_simple_check)16033 int is_support_allowed(object *objp, bool do_simple_check)
16034 {
16035 	int result = -1;
16036 
16037 	// check updated mission conditions to allow support
16038 
16039 	// If running under autopilot support is not allowed
16040 	if ( AutoPilotEngaged )
16041 		return 0;
16042 
16043 	// none allowed
16044 	if (The_mission.support_ships.max_support_ships == 0)
16045 		return 0;
16046 
16047 	// ship_find_repair_ship is a little expensive, so let's not do it every frame
16048 	if (!do_simple_check)
16049 	{
16050 		// check if all support ships are departing or dying
16051 		result = ship_find_repair_ship(objp);
16052 		if (result == 4) {
16053 			return 0;
16054 		}
16055 
16056 		// restricted number allowed
16057 		if (The_mission.support_ships.max_support_ships > 0)
16058 		{
16059 			// if all the allowed ships have been used up, can't rearm unless something's available in-mission or arriving
16060 			if ((The_mission.support_ships.tally >= The_mission.support_ships.max_support_ships))
16061 			{
16062 				// this shouldn't happen because we've reached one of the limits
16063 				Assert(result != 2);
16064 
16065 				// nothing arriving and no ships available in mission
16066 				if ((Arriving_support_ship == NULL) && (result == 0 || result == 3))
16067 					return 0;
16068 			}
16069 		}
16070 	}
16071 
16072 	ship *shipp = &Ships[objp->instance];
16073 
16074 	// this also looks a little more expensive
16075 	if (!do_simple_check)
16076 	{
16077 		// make sure, if exiting from bay, that parent ship is in the mission!
16078 		if ((result == 0 || result == 2) && (The_mission.support_ships.arrival_location == ARRIVE_FROM_DOCK_BAY))
16079 		{
16080 			Assert(The_mission.support_ships.arrival_anchor != -1);
16081 
16082 			// ensure it's in-mission
16083 			int temp = ship_name_lookup(Parse_names[The_mission.support_ships.arrival_anchor]);
16084 			if (temp < 0)
16085 			{
16086 				return 0;
16087 			}
16088 
16089 			// make sure it's not leaving or blowing up
16090 			if (Ships[temp].flags & (SF_DYING | SF_DEPARTING))
16091 			{
16092 				return 0;
16093 			}
16094 
16095 			// also make sure that parent ship's fighterbay hasn't been destroyed
16096 			if (ship_fighterbays_all_destroyed(&Ships[temp]))
16097 			{
16098 				return 0;
16099 			}
16100 		}
16101 	}
16102 
16103 	if (Game_mode & GM_NORMAL)
16104 	{
16105 		if ( !(Iff_info[shipp->team].flags & IFFF_SUPPORT_ALLOWED) )
16106 		{
16107 			return 0;
16108 		}
16109 	}
16110 	else
16111 	{
16112 		// multiplayer version behaves differently.  Depending on mode:
16113 		// 1) coop mode -- only available to friendly
16114 		// 2) team v team mode -- availble to either side
16115 		// 3) dogfight -- never
16116 
16117 		if(Netgame.type_flags & NG_TYPE_DOGFIGHT)
16118 		{
16119 			return 0;
16120 		}
16121 
16122 		if (IS_MISSION_MULTI_COOP)
16123 		{
16124 			if ( !(Iff_info[shipp->team].flags & IFFF_SUPPORT_ALLOWED) )
16125 			{
16126 				return 0;
16127 			}
16128 		}
16129 	}
16130 
16131 	// Goober5000 - extra check for existence of support ship
16132 	if ( (The_mission.support_ships.ship_class < 0) &&
16133 		!(The_mission.support_ships.support_available_for_species & (1 << Ship_info[shipp->ship_info_index].species)) )
16134 	{
16135 		return 0;
16136 	}
16137 
16138 // this is a mod problem, so let's only do it in debug mode
16139 #ifndef NDEBUG
16140 	// Goober5000 - extra check to make sure this guy has a rearming dockpoint
16141 	if (model_find_dock_index(Ship_info[shipp->ship_info_index].model_num, DOCK_TYPE_REARM) < 0)
16142 	{
16143 		Warning(LOCATION, "Support not allowed for %s because its model lacks a rearming dockpoint!", shipp->ship_name);
16144 		return 0;
16145 	}
16146 #endif
16147 
16148 	// Goober5000 - if we got this far, we can request support
16149 	return 1;
16150 }
16151 
16152 // returns random index of a visible ship
16153 // if no visible ships are generated in num_ships iterations, it returns -1
ship_get_random_targetable_ship()16154 int ship_get_random_targetable_ship()
16155 {
16156 	int rand_ship;
16157 	int idx = 0, target_list[MAX_SHIPS];
16158 	ship_obj *so;
16159 
16160 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
16161 		// make sure the instance is valid
16162 		if ( (Objects[so->objnum].instance < 0) || (Objects[so->objnum].instance >= MAX_SHIPS) )
16163 			continue;
16164 
16165 		// skip if we aren't supposed to target it
16166 		if ( Ships[Objects[so->objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS )
16167 			continue;
16168 
16169 		if (idx >= MAX_SHIPS) {
16170 			idx = MAX_SHIPS;
16171 			break;
16172 		}
16173 
16174 		target_list[idx] = Objects[so->objnum].instance;
16175 		idx++;
16176 	}
16177 
16178 	if (idx == 0)
16179 		return -1;
16180 
16181 	rand_ship = (rand() % idx);
16182 
16183 	return target_list[rand_ship];
16184 }
16185 
16186 /**
16187  * Forcible jettison cargo from a ship
16188  */
object_jettison_cargo(object * objp,object * cargo_objp)16189 void object_jettison_cargo(object *objp, object *cargo_objp)
16190 {
16191 	// make sure we are docked
16192 	Assert((objp != NULL) && (cargo_objp != NULL));
16193 	Assert(dock_check_find_direct_docked_object(objp, cargo_objp));
16194 
16195 	vec3d impulse, pos;
16196 	ship *shipp = &Ships[objp->instance];
16197 	ship *cargo_shipp = &Ships[cargo_objp->instance];
16198 	int docker_index = dock_find_dockpoint_used_by_object(objp, cargo_objp);
16199 	int dockee_index = dock_find_dockpoint_used_by_object(cargo_objp, objp);
16200 
16201 	// undo all the docking animations
16202 	model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
16203 	model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
16204 	model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
16205 	model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_1, docker_index, -1);
16206 	model_anim_start_type(cargo_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
16207 	model_anim_start_type(cargo_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
16208 	model_anim_start_type(cargo_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
16209 	model_anim_start_type(cargo_shipp, TRIGGER_TYPE_DOCKING_STAGE_1, dockee_index, -1);
16210 
16211 	// undock the objects
16212 	ai_do_objects_undocked_stuff(objp, cargo_objp);
16213 
16214 	// Goober5000 - add log
16215 	mission_log_add_entry(LOG_SHIP_UNDOCKED, shipp->ship_name, cargo_shipp->ship_name);
16216 
16217 	// physics stuff
16218 	vm_vec_sub(&pos, &cargo_objp->pos, &objp->pos);
16219 	impulse = pos;
16220 	vm_vec_scale(&impulse, 100.0f);
16221 	vm_vec_normalize(&pos);
16222 
16223 	// whack the ship
16224 	physics_apply_whack(&impulse, &pos, &cargo_objp->phys_info, &cargo_objp->orient, cargo_objp->phys_info.mass);
16225 }
16226 
ship_get_exp_damage(object * objp)16227 float ship_get_exp_damage(object* objp)
16228 {
16229 	Assert(objp->type == OBJ_SHIP);
16230 	float damage;
16231 
16232 	ship *shipp = &Ships[objp->instance];
16233 
16234 	if (shipp->special_exp_damage >= 0) {
16235 		damage = i2fl(shipp->special_exp_damage);
16236 	} else {
16237 		damage = Ship_info[shipp->ship_info_index].shockwave.damage;
16238 	}
16239 
16240 	return damage;
16241 }
16242 
ship_get_exp_propagates(ship * sp)16243 int ship_get_exp_propagates(ship *sp)
16244 {
16245 	return Ship_info[sp->ship_info_index].explosion_propagates;
16246 }
16247 
ship_get_exp_outer_rad(object * ship_objp)16248 float ship_get_exp_outer_rad(object *ship_objp)
16249 {
16250 	float outer_rad;
16251 	Assert(ship_objp->type == OBJ_SHIP);
16252 
16253 	if (Ships[ship_objp->instance].special_exp_outer == -1) {
16254 		outer_rad = Ship_info[Ships[ship_objp->instance].ship_info_index].shockwave.outer_rad;
16255 	} else {
16256 		outer_rad = (float)Ships[ship_objp->instance].special_exp_outer;
16257 	}
16258 
16259 	return outer_rad;
16260 }
16261 
valid_cap_subsys_cargo_list(char * subsys)16262 int valid_cap_subsys_cargo_list(char *subsys)
16263 {
16264 	// Return 1 for all subsystems now, due to Mantis #2524.
16265 	return 1;
16266 	/*
16267 	if (stristr(subsys, "nav")
16268 		|| stristr(subsys, "comm")
16269 		|| stristr(subsys, "engine")
16270 		|| stristr(subsys, "fighter")	// fighter bays
16271 		|| stristr(subsys, "sensors")
16272 		|| stristr(subsys, "weapons")) {
16273 
16274 		return 1;
16275 	}
16276 
16277 	return 0;
16278 	*/
16279 }
16280 
16281 /**
16282  * Determine turret status of a given subsystem
16283  *
16284  * @return 0 for no turret, 1 for "fixed turret", 2 for "rotating" turret
16285  */
ship_get_turret_type(ship_subsys * subsys)16286 int ship_get_turret_type(ship_subsys *subsys)
16287 {
16288 	// not a turret at all
16289 	if(subsys->system_info->type != SUBSYSTEM_TURRET){
16290 		return 0;
16291 	}
16292 
16293 	// if it rotates
16294 	if(subsys->system_info->turret_turning_rate > 0.0f){
16295 		return 2;
16296 	}
16297 
16298 	// if its fixed
16299 	return 1;
16300 }
16301 
ship_get_subsys(ship * shipp,char * subsys_name)16302 ship_subsys *ship_get_subsys(ship *shipp, char *subsys_name)
16303 {
16304 	// sanity checks
16305 	if ((shipp == NULL) || (subsys_name == NULL)) {
16306 		return NULL;
16307 	}
16308 
16309 	ship_subsys *ss = GET_FIRST(&shipp->subsys_list);
16310 	while (ss != END_OF_LIST(&shipp->subsys_list)) {
16311 		// check subsystem name
16312 		if (!subsystem_stricmp(ss->system_info->subobj_name, subsys_name)) {
16313 			return ss;
16314 		}
16315 
16316 		// next
16317 		ss = GET_NEXT(ss);
16318 	}
16319 
16320 	// didn't find it
16321 	return NULL;
16322 }
16323 
ship_get_num_subsys(ship * shipp)16324 int ship_get_num_subsys(ship *shipp)
16325 {
16326 	Assert(shipp != NULL);
16327 
16328 	return Ship_info[shipp->ship_info_index].n_subsystems;
16329 }
16330 
16331 // returns 0 if no conflict, 1 if conflict, -1 on some kind of error with wing struct
wing_has_conflicting_teams(int wing_index)16332 int wing_has_conflicting_teams(int wing_index)
16333 {
16334 	int first_team, idx;
16335 
16336 	// sanity checks
16337 	Assert((wing_index >= 0) && (wing_index < Num_wings) && (Wings[wing_index].wave_count > 0));
16338 	if((wing_index < 0) || (wing_index >= Num_wings) || (Wings[wing_index].wave_count <= 0)){
16339 		return -1;
16340 	}
16341 
16342 	// check teams
16343 	Assert(Wings[wing_index].ship_index[0] >= 0);
16344 	if(Wings[wing_index].ship_index[0] < 0){
16345 		return -1;
16346 	}
16347 	first_team = Ships[Wings[wing_index].ship_index[0]].team;
16348 	for(idx=1; idx<Wings[wing_index].wave_count; idx++){
16349 		// more sanity checks
16350 		Assert(Wings[wing_index].ship_index[idx] >= 0);
16351 		if(Wings[wing_index].ship_index[idx] < 0){
16352 			return -1;
16353 		}
16354 
16355 		// if we've got a team conflict
16356 		if(first_team != Ships[Wings[wing_index].ship_index[idx]].team){
16357 			return 1;
16358 		}
16359 	}
16360 
16361 	// no conflict
16362 	return 0;
16363 }
16364 
16365 /**
16366  * Get the team of a reinforcement item
16367  */
ship_get_reinforcement_team(int r_index)16368 int ship_get_reinforcement_team(int r_index)
16369 {
16370 	int wing_index;
16371 	p_object *p_objp;
16372 
16373 	// sanity checks
16374 	Assert((r_index >= 0) && (r_index < Num_reinforcements));
16375 	if ((r_index < 0) || (r_index >= Num_reinforcements))
16376 		return -1;
16377 
16378 	// if the reinforcement is a ship
16379 	p_objp = mission_parse_get_arrival_ship(Reinforcements[r_index].name);
16380 	if (p_objp != NULL)
16381 		return p_objp->team;
16382 
16383 	// if the reinforcement is a ship
16384 	wing_index = wing_lookup(Reinforcements[r_index].name);
16385 	if (wing_index >= 0)
16386 	{
16387 		// go through the ship arrival list and find any ship in this wing
16388 		for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
16389 		{
16390 			// check by wingnum
16391 			if (p_objp->wingnum == wing_index)
16392 				return p_objp->team;
16393 		}
16394 	}
16395 
16396 	// no team ?
16397 	return -1;
16398 }
16399 
16400 /**
16401  * Determine if the given texture is used by a ship type. return ship info index, or -1 if not used by a ship
16402  */
ship_get_texture(int bitmap)16403 int ship_get_texture(int bitmap)
16404 {
16405 	int idx;
16406 
16407 	// check all ship types
16408 	for(idx=0; idx<Num_ship_classes; idx++){
16409 		if((Ship_info[idx].model_num >= 0) && model_find_texture(Ship_info[idx].model_num, bitmap) == 1){
16410 			return idx;
16411 		}
16412 	}
16413 
16414 	// couldn't find the texture
16415 	return -1;
16416 }
16417 
16418 // update artillery lock info
16419 #define CLEAR_ARTILLERY_AND_CONTINUE()	{ if(aip != NULL){ aip->artillery_objnum = -1; aip->artillery_sig = -1;	aip->artillery_lock_time = 0.0f;} continue; }
16420 float artillery_dist = 10.0f;
16421 DCF(art, "")
16422 {
16423 	dc_get_arg(ARG_FLOAT);
16424 	artillery_dist = Dc_arg_float;
16425 }
ship_update_artillery_lock()16426 void ship_update_artillery_lock()
16427 {
16428 	ai_info *aip = NULL;
16429 	mc_info *cinfo = NULL;
16430 	int c_objnum;
16431 	vec3d temp, local_hit;
16432 	ship *shipp;
16433 	ship_obj *so;
16434 
16435 	// update all ships
16436 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ){
16437 		// get the ship
16438 		if((so->objnum >= 0) && (Objects[so->objnum].type == OBJ_SHIP) && (Objects[so->objnum].instance >= 0)){
16439 			shipp = &Ships[Objects[so->objnum].instance];
16440 		} else {
16441 			continue;
16442 		}
16443 
16444 		// get ai info
16445 		if(shipp->ai_index >= 0){
16446 			aip = &Ai_info[shipp->ai_index];
16447 		}
16448 
16449 		// if the ship has no targeting laser firing
16450 		if((shipp->targeting_laser_objnum < 0) || (shipp->targeting_laser_bank < 0)){
16451 			CLEAR_ARTILLERY_AND_CONTINUE();
16452 		}
16453 
16454 		// if he didn't hit any objects this frame
16455 		if(beam_get_num_collisions(shipp->targeting_laser_objnum) <= 0){
16456 			CLEAR_ARTILLERY_AND_CONTINUE();
16457 		}
16458 
16459 		// get weapon info for the targeting laser he's firing
16460 		Assert((shipp->weapons.current_primary_bank >= 0) && (shipp->weapons.current_primary_bank < 2));
16461 		if((shipp->weapons.current_primary_bank < 0) || (shipp->weapons.current_primary_bank >= 2)){
16462 			continue;
16463 		}
16464 		Assert(shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] >= 0);
16465 		if(shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] < 0){
16466 			continue;
16467 		}
16468 		Assert((Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags & WIF_BEAM) && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].b_info.beam_type == BEAM_TYPE_C));
16469 		if(!(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags & WIF_BEAM) || (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].b_info.beam_type != BEAM_TYPE_C)){
16470 			continue;
16471 		}
16472 
16473 		// get collision info
16474 		if(!beam_get_collision(shipp->targeting_laser_objnum, 0, &c_objnum, &cinfo)){
16475 			CLEAR_ARTILLERY_AND_CONTINUE();
16476 		}
16477 		if((c_objnum < 0) || (cinfo == NULL)){
16478 			CLEAR_ARTILLERY_AND_CONTINUE();
16479 		}
16480 
16481 		// get the position we hit this guy with in his local coords
16482 		vm_vec_sub(&temp, &cinfo->hit_point_world, &Objects[c_objnum].pos);
16483 		vm_vec_rotate(&local_hit, &temp, &Objects[c_objnum].orient);
16484 
16485 		// if we are hitting a different guy now, reset the lock
16486 		if((c_objnum != aip->artillery_objnum) || (Objects[c_objnum].signature != aip->artillery_sig)){
16487 			aip->artillery_objnum = c_objnum;
16488 			aip->artillery_sig = Objects[c_objnum].signature;
16489 			aip->artillery_lock_time = 0.0f;
16490 			aip->artillery_lock_pos = local_hit;
16491 
16492 			// done
16493 			continue;
16494 		}
16495 
16496 		// otherwise we're hitting the same guy. check to see if we've strayed too far
16497 		if(vm_vec_dist_quick(&local_hit, &aip->artillery_lock_pos) > artillery_dist){
16498 			// hmmm. reset lock time, but don't reset the lock itself
16499 			aip->artillery_lock_time = 0.0f;
16500 			continue;
16501 		}
16502 
16503 		// finally - just increment the lock time
16504 		aip->artillery_lock_time += flFrametime;
16505 
16506 		// TEST CODE
16507 		if(aip->artillery_lock_time >= 2.0f){
16508 			ssm_create(&Objects[aip->artillery_objnum], &cinfo->hit_point_world, 0, NULL, shipp->team);
16509 
16510 			// reset the artillery
16511 			aip->artillery_lock_time = 0.0f;
16512 		}
16513 	}
16514 }
16515 
16516 /**
16517  * Checks if a world point is inside the extended bounding box of a ship
16518  *
16519  * May not work if delta box is large and negative (ie, adjusted box crosses over on itself - min > max)
16520  */
check_world_pt_in_expanded_ship_bbox(vec3d * world_pt,object * objp,float delta_box)16521 int check_world_pt_in_expanded_ship_bbox(vec3d *world_pt, object *objp, float delta_box)
16522 {
16523 	Assert(objp->type == OBJ_SHIP);
16524 
16525 	vec3d temp, ship_pt;
16526 	polymodel *pm;
16527 	vm_vec_sub(&temp, world_pt, &objp->pos);
16528 	vm_vec_rotate(&ship_pt, &temp, &objp->orient);
16529 
16530 	pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
16531 
16532 	return (
16533 			(ship_pt.xyz.x > pm->mins.xyz.x - delta_box) && (ship_pt.xyz.x < pm->maxs.xyz.x + delta_box)
16534 		&& (ship_pt.xyz.y > pm->mins.xyz.y - delta_box) && (ship_pt.xyz.y < pm->maxs.xyz.y + delta_box)
16535 		&& (ship_pt.xyz.z > pm->mins.xyz.z - delta_box) && (ship_pt.xyz.z < pm->maxs.xyz.z + delta_box)
16536 	);
16537 }
16538 
16539 
16540 /**
16541  * Returns true when objp is ship and is tagged
16542  */
ship_is_tagged(object * objp)16543 int ship_is_tagged(object *objp)
16544 {
16545 	ship *shipp;
16546 	if (objp->type == OBJ_SHIP) {
16547 		shipp = &Ships[objp->instance];
16548 		if ( (shipp->tag_left > 0) || (shipp->level2_tag_left > 0) ) {
16549 			return 1;
16550 		}
16551 	}
16552 
16553 	return 0;
16554 }
16555 
16556 /**
16557  * Get maximum ship speed (when not warping in or out)
16558  */
ship_get_max_speed(ship * shipp)16559 float ship_get_max_speed(ship *shipp)
16560 {
16561 	float max_speed;
16562 	ship_info *sip = &Ship_info[shipp->ship_info_index];
16563 
16564 	// Goober5000 - maybe we're using cap-waypoint-speed
16565 	ai_info *aip = &Ai_info[shipp->ai_index];
16566 	if ((aip->mode == AIM_WAYPOINTS || aip->mode == AIM_FLY_TO_SHIP) && aip->waypoint_speed_cap > 0)
16567 		return i2fl(aip->waypoint_speed_cap);
16568 
16569 	// max overclock
16570 	max_speed = sip->max_overclocked_speed;
16571 
16572 	// normal max speed
16573 	max_speed = MAX(max_speed, sip->max_vel.xyz.z);
16574 
16575 	// afterburn if not locked
16576 	if (!(shipp->flags2 & SF2_AFTERBURNER_LOCKED)) {
16577 		max_speed = MAX(max_speed, sip->afterburner_max_vel.xyz.z);
16578 	}
16579 
16580 	return max_speed;
16581 }
16582 
16583 /**
16584  * Determine warpout speed of ship
16585  */
ship_get_warpout_speed(object * objp)16586 float ship_get_warpout_speed(object *objp)
16587 {
16588 	Assert(objp->type == OBJ_SHIP);
16589 
16590 	ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
16591 	//WMC - Any speed is good for in place anims (aka BSG FTL effect)
16592 	if(sip->warpout_type == WT_IN_PLACE_ANIM && sip->warpout_speed <= 0.0f)
16593 	{
16594 		return objp->phys_info.speed;
16595 	}
16596 	else if(sip->warpout_type == WT_SWEEPER || sip->warpout_type == WT_IN_PLACE_ANIM)
16597 	{
16598 		return sip->warpout_speed;
16599 	}
16600 	else if(sip->warpout_type == WT_HYPERSPACE)
16601 	{
16602 		if (objp->phys_info.speed > sip->warpout_speed)
16603 			return objp->phys_info.speed;
16604 		else
16605 			return sip->warpout_speed;
16606 	}
16607 
16608 	return shipfx_calculate_warp_dist(objp) / shipfx_calculate_warp_time(objp, WD_WARP_OUT);
16609 }
16610 
16611 /**
16612  * Returns true if ship is beginning to speed up in warpout
16613  */
ship_is_beginning_warpout_speedup(object * objp)16614 int ship_is_beginning_warpout_speedup(object *objp)
16615 {
16616 	Assert(objp->type == OBJ_SHIP);
16617 
16618 	ai_info *aip;
16619 
16620 	aip = &Ai_info[Ships[objp->instance].ai_index];
16621 
16622 	if (aip->mode == AIM_WARP_OUT) {
16623 		if ( (aip->submode == AIS_WARP_3) || (aip->submode == AIS_WARP_4) || (aip->submode == AIS_WARP_5) ) {
16624 			return 1;
16625 		}
16626 	}
16627 
16628 	return 0;
16629 }
16630 
16631 /**
16632  * Return the length of a ship
16633  */
ship_class_get_length(ship_info * sip)16634 float ship_class_get_length(ship_info *sip)
16635 {
16636 	Assert(sip->model_num >= 0);
16637 	polymodel *pm = model_get(sip->model_num);
16638 	return (pm->maxs.xyz.z - pm->mins.xyz.z);
16639 }
16640 
16641 // Goober5000
ship_set_new_ai_class(int ship_num,int new_ai_class)16642 void ship_set_new_ai_class(int ship_num, int new_ai_class)
16643 {
16644 	Assert(ship_num >= 0 && ship_num < MAX_SHIPS);
16645 	Assert(new_ai_class >= 0);
16646 
16647 	ai_info *aip = &Ai_info[Ships[ship_num].ai_index];
16648 
16649 	// we hafta change a bunch of stuff here...
16650 	aip->ai_class = new_ai_class;
16651 	aip->behavior = AIM_NONE;
16652 	init_aip_from_class_and_profile(aip, &Ai_classes[new_ai_class], The_mission.ai_profile);
16653 
16654 	Ships[ship_num].weapons.ai_class = new_ai_class;
16655 
16656 	// I think that's everything!
16657 }
16658 
16659 // Goober5000
ship_subsystem_set_new_ai_class(int ship_num,char * subsystem,int new_ai_class)16660 void ship_subsystem_set_new_ai_class(int ship_num, char *subsystem, int new_ai_class)
16661 {
16662 	Assert(ship_num >= 0 && ship_num < MAX_SHIPS);
16663 	Assert(subsystem);
16664 	Assert(new_ai_class >= 0);
16665 
16666 	ship_subsys *ss;
16667 
16668 	// find the ship subsystem by searching ship's subsys_list
16669 	ss = GET_FIRST( &Ships[ship_num].subsys_list );
16670 	while ( ss != END_OF_LIST( &Ships[ship_num].subsys_list ) )
16671 	{
16672 		// if we found the subsystem
16673 		if ( !subsystem_stricmp(ss->system_info->subobj_name, subsystem))
16674 		{
16675 			// set ai class
16676 			ss->weapons.ai_class = new_ai_class;
16677 			return;
16678 		}
16679 
16680 		ss = GET_NEXT( ss );
16681 	}
16682 }
16683 
16684 // Goober5000 - will attempt to load an insignia bitmap and set it as active for the wing
16685 // copied more or less from managepilot.cpp
wing_load_squad_bitmap(wing * w)16686 void wing_load_squad_bitmap(wing *w)
16687 {
16688 	// sanity check
16689 	if(w == NULL)
16690 	{
16691 		return;
16692 	}
16693 
16694 	// make sure one is not already set?!?
16695 	Assert (w->wing_insignia_texture == -1);
16696 
16697 	// try and set the new one
16698 	if( w->wing_squad_filename[0] != '\0' )
16699 	{
16700 		// load duplicate because it might be the same as the player's squad,
16701 		// and we don't want to overlap and breed nasty errors when we unload
16702 		w->wing_insignia_texture = bm_load_duplicate(w->wing_squad_filename);
16703 
16704 		// lock is as a transparent texture
16705 		if(w->wing_insignia_texture != -1)
16706 		{
16707 			bm_lock(w->wing_insignia_texture, 16, BMP_TEX_XPARENT);
16708 			bm_unlock(w->wing_insignia_texture);
16709 		}
16710 	}
16711 }
16712 
16713 // Goober5000 - needed by new hangar depart code
16714 // check whether this ship has a docking bay
ship_has_dock_bay(int shipnum)16715 bool ship_has_dock_bay(int shipnum)
16716 {
16717 	Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
16718 
16719 	polymodel *pm;
16720 
16721 	pm = model_get( Ship_info[Ships[shipnum].ship_info_index].model_num );
16722 	Assert( pm );
16723 
16724 	return ( pm->ship_bay && (pm->ship_bay->num_paths > 0) );
16725 }
16726 
16727 // Goober5000
ship_useful_for_departure(int shipnum,int path_mask)16728 bool ship_useful_for_departure(int shipnum, int path_mask)
16729 {
16730 	Assert( shipnum >= 0 && shipnum < MAX_SHIPS );
16731 
16732 	// not valid if dying or departing
16733 	if (Ships[shipnum].flags & (SF_DYING | SF_DEPARTING))
16734 		return false;
16735 
16736 	// no dockbay, can't depart to it
16737 	if (!ship_has_dock_bay(shipnum))
16738 		return false;
16739 
16740 	// make sure that the bays are not all destroyed
16741 	if (ship_fighterbays_all_destroyed(&Ships[shipnum]))
16742 		return false;
16743 
16744 	// in the future, we might want to check bay paths against destroyed fighterbays,
16745 	// but that capability doesn't currently exist
16746 	// (note, a mask of 0 indicates all paths are valid)
16747 	//if (path_mask != 0)
16748 	//{
16749 	//}
16750 
16751 	// ship is valid
16752 	return true;
16753 }
16754 
16755 // Goober5000 - needed by new hangar depart code
16756 // get first ship in ship list with docking bay
ship_get_ship_for_departure(int team)16757 int ship_get_ship_for_departure(int team)
16758 {
16759 	for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
16760 	{
16761 		Assert(so->objnum >= 0);
16762 		int shipnum = Objects[so->objnum].instance;
16763 		Assert(shipnum >= 0);
16764 
16765 		if ( (Ships[shipnum].team == team) && ship_useful_for_departure(shipnum) )
16766 			return shipnum;
16767 	}
16768 
16769 	// we didn't find anything
16770 	return -1;
16771 }
16772 
16773 // Goober5000 - check if all fighterbays on a ship have been destroyed
ship_fighterbays_all_destroyed(ship * shipp)16774 bool ship_fighterbays_all_destroyed(ship *shipp)
16775 {
16776 	Assert(shipp);
16777 	ship_subsys *subsys;
16778 	int num_fighterbay_subsystems = 0;
16779 
16780 	// check all fighterbay systems
16781 	subsys = GET_FIRST(&shipp->subsys_list);
16782 	while(subsys != END_OF_LIST(&shipp->subsys_list))
16783 	{
16784 		// look for fighterbays
16785 		if (ship_subsys_is_fighterbay(subsys))
16786 		{
16787 			num_fighterbay_subsystems++;
16788 
16789 			// if fighterbay doesn't take damage, we're good
16790 			if (!ship_subsys_takes_damage(subsys))
16791 				return false;
16792 
16793 			// if fighterbay isn't destroyed, we're good
16794 			if (subsys->current_hits > 0)
16795 				return false;
16796 		}
16797 
16798 		// next item
16799 		subsys = GET_NEXT(subsys);
16800 	}
16801 
16802 	// if the ship has no fighterbay subsystems at all, it must be an unusual case,
16803 	// like the Faustus, so pretend it's okay...
16804 	if (num_fighterbay_subsystems == 0)
16805 		return false;
16806 
16807 	// if we got this far, the ship has at least one fighterbay subsystem,
16808 	// and all the ones it has are destroyed
16809 	return true;
16810 }
16811 
16812 // moved here by Goober5000
ship_subsys_is_fighterbay(ship_subsys * ss)16813 bool ship_subsys_is_fighterbay(ship_subsys *ss)
16814 {
16815 	Assert(ss);
16816 
16817 	if ( !strnicmp(NOX("fighter"), ss->system_info->name, 7) ) {
16818 		return true;
16819 	}
16820 
16821 	return false;
16822 }
16823 
16824 // Goober5000
ship_subsys_takes_damage(ship_subsys * ss)16825 bool ship_subsys_takes_damage(ship_subsys *ss)
16826 {
16827 	Assert(ss);
16828 
16829 	return (ss->max_hits > SUBSYS_MAX_HITS_THRESHOLD);
16830 }
16831 
16832 // Goober5000
ship_do_submodel_rotation(ship * shipp,model_subsystem * psub,ship_subsys * pss)16833 void ship_do_submodel_rotation(ship *shipp, model_subsystem *psub, ship_subsys *pss)
16834 {
16835 	Assert(shipp);
16836 	Assert(psub);
16837 	Assert(pss);
16838 
16839 	// check if we actually can rotate
16840 	if ( !(pss->flags & SSF_ROTATES) ){
16841 		return;
16842 	}
16843 
16844 	if (psub->flags & MSS_FLAG_TRIGGERED && pss->triggered_rotation_index >= 0) {
16845 		Triggered_rotations[pss->triggered_rotation_index].process_queue();
16846 		model_anim_submodel_trigger_rotate(psub, pss );
16847 		return;
16848 
16849 	}
16850 
16851 	// check for rotating artillery
16852 	if ( psub->flags & MSS_FLAG_ARTILLERY )
16853 	{
16854 		ship_weapon *swp = &shipp->weapons;
16855 
16856 		// rotate only if trigger is down
16857 		if ( !(shipp->flags & SF_TRIGGER_DOWN) )
16858 			return;
16859 
16860 		// check linked
16861 		if ( shipp->flags & SF_PRIMARY_LINKED )
16862 		{
16863 			int i, ammo_tally = 0;
16864 
16865 			// calculate ammo
16866 			for (i=0; i<swp->num_primary_banks; i++)
16867 				ammo_tally += swp->primary_bank_ammo[i];
16868 
16869 			// do not rotate if out of ammo
16870 			if (ammo_tally <= 0)
16871 				return;
16872 		}
16873 		// check unlinked
16874 		else
16875 		{
16876 			// do not rotate if this is not the firing bank or if we have no ammo in this bank
16877 			if ((psub->weapon_rotation_pbank != swp->current_primary_bank) || (swp->primary_bank_ammo[swp->current_primary_bank] <= 0))
16878 				return;
16879 		}
16880 	}
16881 
16882 	// if we got this far, we can rotate - so choose which method to use
16883 	if (psub->flags & MSS_FLAG_STEPPED_ROTATE	) {
16884 		submodel_stepped_rotate(psub, &pss->submodel_info_1);
16885 	} else {
16886 		submodel_rotate(psub, &pss->submodel_info_1 );
16887 	}
16888 }
16889 
16890 // Goober5000
ship_has_energy_weapons(ship * shipp)16891 int ship_has_energy_weapons(ship *shipp)
16892 {
16893 	// (to avoid round-off errors, weapon reserve is not tested for zero)
16894 	return (Ship_info[shipp->ship_info_index].max_weapon_reserve > WEAPON_RESERVE_THRESHOLD);
16895 }
16896 
16897 // Goober5000
ship_has_engine_power(ship * shipp)16898 int ship_has_engine_power(ship *shipp)
16899 {
16900 	return (Ship_info[shipp->ship_info_index].max_speed > 0 );
16901 }
16902 
16903 // Goober5000
ship_starting_wing_lookup(const char * wing_name)16904 int ship_starting_wing_lookup(const char *wing_name)
16905 {
16906 	for (int i = 0; i < MAX_STARTING_WINGS; i++)
16907 	{
16908 		if (!stricmp(Starting_wing_names[i], wing_name))
16909 			return i;
16910 	}
16911 
16912 	return -1;
16913 }
16914 
16915 // Goober5000
ship_squadron_wing_lookup(const char * wing_name)16916 int ship_squadron_wing_lookup(const char *wing_name)
16917 {
16918 	// TvT uses a different set of wing names from everything else
16919 	if (MULTI_TEAM)
16920 	{
16921 		for (int i = 0; i < MAX_TVT_WINGS; i++)
16922 		{
16923 			if (!stricmp(TVT_wing_names[i], wing_name))
16924 				return i;
16925 		}
16926 	}
16927 	else
16928 	{
16929 		for (int i = 0; i < MAX_SQUADRON_WINGS; i++)
16930 		{
16931 			if (!stricmp(Squadron_wing_names[i], wing_name))
16932 				return i;
16933 		}
16934 	}
16935 
16936 	return -1;
16937 }
16938 
16939 // Goober5000
ship_tvt_wing_lookup(const char * wing_name)16940 int ship_tvt_wing_lookup(const char *wing_name)
16941 {
16942 	for (int i = 0; i < MAX_TVT_WINGS; i++)
16943 	{
16944 		if (!stricmp(TVT_wing_names[i], wing_name))
16945 			return i;
16946 	}
16947 
16948 	return -1;
16949 }
16950 
16951 // Goober5000
ship_class_get_priority(int ship_class)16952 int ship_class_get_priority(int ship_class)
16953 {
16954 	ship_info *sip = &Ship_info[ship_class];
16955 
16956 	// biggest to smallest
16957 	if (sip->flags & SIF_KNOSSOS_DEVICE)
16958 		return 1;
16959 	else if (sip->flags & SIF_SUPERCAP)
16960 		return 2;
16961 	else if (sip->flags & SIF_DRYDOCK)
16962 		return 3;
16963 	else if (sip->flags & SIF_CAPITAL)
16964 		return 4;
16965 	else if (sip->flags & SIF_CORVETTE)
16966 		return 5;
16967 	else if (sip->flags & SIF_CRUISER)
16968 		return 6;
16969 	else if (sip->flags & SIF_GAS_MINER)
16970 		return 7;
16971 	else if (sip->flags & SIF_AWACS)
16972 		return 8;
16973 	else if (sip->flags & SIF_FREIGHTER)
16974 		return 9;
16975 	else if (sip->flags & SIF_TRANSPORT)
16976 		return 10;
16977 	else if (sip->flags & SIF_BOMBER)
16978 		return 11;
16979 	else if (sip->flags & (SIF_FIGHTER | SIF_STEALTH))
16980 		return 12;
16981 	else if (sip->flags & SIF_ESCAPEPOD)
16982 		return 13;
16983 	else if (sip->flags & SIF_SENTRYGUN)
16984 		return 14;
16985 	else if (sip->flags & SIF_CARGO)
16986 		return 15;
16987 	else if (sip->flags & SIF_NAVBUOY)
16988 		return 16;
16989 
16990 	Warning(LOCATION, "Unknown priority for ship class '%s'!", sip->name);
16991 	return 17 + ship_class;
16992 }
16993 
16994 // Goober5000
ship_class_compare(int ship_class_1,int ship_class_2)16995 int ship_class_compare(int ship_class_1, int ship_class_2)
16996 {
16997 	// grab priorities
16998 	int priority1 = ship_class_get_priority(ship_class_1);
16999 	int priority2 = ship_class_get_priority(ship_class_2);
17000 
17001 	// standard compare
17002 	if (priority1 < priority2)
17003 		return -1;
17004 	else if (priority1 > priority2)
17005 		return 1;
17006 	else
17007 		return 0;
17008 }
17009 
17010 /**
17011  * Gives the index into the Damage_types[] vector of a specified damage type name
17012  * @return -1 if not found
17013  */
damage_type_get_idx(char * name)17014 int damage_type_get_idx(char *name)
17015 {
17016 	//This should never be bigger than INT_MAX anyway
17017 	for(int i = 0; i < (int)Damage_types.size(); i++)
17018 	{
17019 		if(!stricmp(name, Damage_types[i].name))
17020 			return i;
17021 	}
17022 
17023 	return -1;
17024 }
17025 
17026 /**
17027  * Either loads a new damage type, or returns the index of one with the same name as given
17028  */
damage_type_add(char * name)17029 int damage_type_add(char *name)
17030 {
17031 	int i = damage_type_get_idx(name);
17032 	if(i != -1)
17033 		return i;
17034 
17035 	DamageTypeStruct dts;
17036 
17037 	strncpy(dts.name, name, NAME_LENGTH-1);
17038 
17039 	if(strlen(name) > NAME_LENGTH - 1)
17040 	{
17041 		Warning(LOCATION, "Damage type name '%s' is too long and has been truncated to '%s'", name, dts.name);
17042 	}
17043 
17044 	Damage_types.push_back(dts);
17045 	return Damage_types.size()-1;
17046 }
17047 
clear()17048 void ArmorDamageType::clear()
17049 {
17050 	DamageTypeIndex = -1;
17051 
17052 	Calculations.clear();
17053 	Arguments.clear();
17054 	altArguments.clear();  // Nuke: don't forget to delete it
17055 }
17056 
17057 //************
17058 // Wanderer - beam piercing type
17059 //************
17060 
17061 flag_def_list	PiercingTypes[] = {
17062 	{	"none",		SADTF_PIERCING_NONE,		0},
17063 	{	"default",	SADTF_PIERCING_DEFAULT,		0},
17064 	{	"retail",	SADTF_PIERCING_RETAIL,		0},
17065 };
17066 
17067 const int Num_piercing_effect_types = sizeof(PiercingTypes)/sizeof(flag_def_list);
17068 
piercing_type_get(char * str)17069 int piercing_type_get(char *str)
17070 {
17071 	int i;
17072 	for(i = 0; i < Num_piercing_effect_types; i++)
17073 	{
17074 		if(!stricmp(PiercingTypes[i].name, str))
17075 			return PiercingTypes[i].def;
17076 	}
17077 
17078 	// default to retail
17079 	return SADTF_PIERCING_RETAIL;
17080 }
17081 
17082 // Nuke: handle difficulty scaling type
17083 flag_def_list	DifficultyScaleTypes[] = {
17084 	{	"first",	ADT_DIFF_SCALE_FIRST,	0},
17085 	{	"last",		ADT_DIFF_SCALE_LAST,	0},
17086 	{	"manual",	ADT_DIFF_SCALE_MANUAL,	0},
17087 };
17088 
17089 const int Num_difficulty_scale_types = sizeof(DifficultyScaleTypes)/sizeof(flag_def_list);
17090 
difficulty_scale_type_get(char * str)17091 int difficulty_scale_type_get(char *str) {
17092 	int i;
17093 	for(i = 0; i < Num_difficulty_scale_types; i++){
17094 		if (!stricmp(DifficultyScaleTypes[i].name, str))
17095 			return DifficultyScaleTypes[i].def;
17096 	}
17097 
17098 	// indicate error
17099 	return ADT_DIFF_SCALE_BAD_VAL;
17100 }
17101 
17102 // Nuke: flag list for +constant: values
17103 flag_def_list	ArmorTypeConstants[] = {
17104 	{	"base damage",			AT_CONSTANT_BASE_DMG,		0},
17105 	{	"current damage",		AT_CONSTANT_CURRENT_DMG,	0},
17106 	{	"difficulty factor",	AT_CONSTANT_DIFF_FACTOR,	0},
17107 	{	"random",				AT_CONSTANT_RANDOM,			0},
17108 	{	"pi",					AT_CONSTANT_PI,				0},
17109 };
17110 
17111 const int Num_armor_type_constants = sizeof(ArmorTypeConstants)/sizeof(flag_def_list);
17112 
armor_type_constants_get(char * str)17113 int armor_type_constants_get(char *str){
17114 	int i;
17115 	for (i = 0; i < Num_armor_type_constants; i++){
17116 		if (!stricmp(ArmorTypeConstants[i].name, str))
17117 			return ArmorTypeConstants[i].def;
17118 	}
17119 	// this shouldnt happen, but if it does theirs a define for that
17120 	return AT_CONSTANT_BAD_VAL;
17121 }
17122 
17123 
17124 //**************************************************************
17125 //WMC - All the extra armor crap
17126 
17127 //****************************Calculation type addition
17128 //4 steps to add a new one
17129 
17130 //Armor types
17131 //STEP 1: Add a define
17132 #define AT_TYPE_ADDITIVE				0
17133 #define AT_TYPE_MULTIPLICATIVE			1
17134 #define AT_TYPE_EXPONENTIAL				2
17135 #define AT_TYPE_EXPONENTIAL_BASE		3
17136 #define AT_TYPE_CUTOFF					4
17137 #define AT_TYPE_REVERSE_CUTOFF			5
17138 #define AT_TYPE_INSTANT_CUTOFF			6
17139 #define AT_TYPE_INSTANT_REVERSE_CUTOFF	7
17140 // Added by Nuke
17141 #define AT_TYPE_CAP						8
17142 #define AT_TYPE_INSTANT_CAP				9
17143 #define AT_TYPE_SET						10
17144 #define AT_TYPE_STORE					11
17145 #define AT_TYPE_LOAD					12
17146 #define AT_TYPE_RANDOM					13
17147 
17148 // Nuke: this is the number of storage locations load/store calculations are allowed to use
17149 #define AT_NUM_STORAGE_LOCATIONS		8
17150 
17151 //STEP 2: Add the name string to the array
17152 char *TypeNames[] = {
17153 	"additive",
17154 	"multiplicative",
17155 	"exponential",
17156 	"exponential base",
17157 	"cutoff",
17158 	"reverse cutoff",
17159 	"instant cutoff",
17160 	"instant reverse cutoff",
17161 	// Added by Nuke
17162 	"cap",
17163 	"instant cap",
17164 	"set",
17165 	"load",
17166 	"store",
17167 	"random"
17168 };
17169 
17170 //STEP 3: Add the default value
17171 float TypeDefaultValues[] = {
17172 	0.0f,	//additive
17173 	1.0f,	//multiplicatve
17174 	1.0f,	//exp
17175 	1.0f, 	//exp base - Damage will always be one (No mathematical way to do better)
17176 	0.0f,	//cutoff
17177 	0.0f,	//reverse cutoff
17178 	0.0f,	//instant cutoff
17179 	0.0f,	//rev instant cutoff
17180 	// Added by Nuke
17181 	0.0f,	// cap - caps are the same as reverse cutoffs, but sets damage to value instead of 0
17182 	0.0f,	// instant cap
17183 	0.0f,	// set - set the damage to value
17184 	0.0f,	// data storage index - load and store calculations allow you to dump and retrieve the current damage in one of a few memory locations (these only persist for the duration of the computation)
17185 	0.0f,	// data storage index
17186 	0.0f	// random min/max
17187 };
17188 
17189 const int Num_armor_calculation_types = sizeof(TypeNames)/sizeof(char*);
17190 
calculation_type_get(char * str)17191 int calculation_type_get(char *str)
17192 {
17193 	for(int i = 0; i < Num_armor_calculation_types; i++)
17194 	{
17195 		if(!stricmp(TypeNames[i], str))
17196 			return i;
17197 	}
17198 
17199 	return -1;
17200 }
17201 
17202 //STEP 4: Add the calculation to the switch statement.
GetDamage(float damage_applied,int in_damage_type_idx,float diff_dmg_scale)17203 float ArmorType::GetDamage(float damage_applied, int in_damage_type_idx, float diff_dmg_scale)
17204 {
17205 	// Nuke: If the weapon has no damage type, just return damage
17206 	if (in_damage_type_idx < 0) {
17207 		// multiply by difficulty scaler now, since it is no longer done where this is called
17208 		return (damage_applied * diff_dmg_scale);
17209 	}
17210 
17211 	//Initialize vars
17212 	uint i,num;
17213 	ArmorDamageType *adtp = NULL;
17214 
17215 	//Find the entry in the weapon that corresponds to the given weapon damage type
17216 	num = DamageTypes.size();
17217 	for(i = 0; i < num; i++)
17218 	{
17219 		if(DamageTypes[i].DamageTypeIndex == in_damage_type_idx)
17220 		{
17221 			adtp = &DamageTypes[i];
17222 			break;
17223 		}
17224 	}
17225 
17226 	//curr_arg is the current calculation type value
17227 	float curr_arg;
17228 
17229 	//Make sure that we _have_ an armor entry for this damage type
17230 	if(adtp != NULL)
17231 	{
17232 		//How many calculations do we have to do?
17233 		num = adtp->Calculations.size();
17234 
17235 		// Used for instant cutoffs/cap, to instantly end the loop
17236 		bool end_now = false;
17237 		// used for load/store operations
17238 		float storage[AT_NUM_STORAGE_LOCATIONS];
17239 		int storage_idx;
17240 		bool using_storage = false;
17241 		// constant related stuff
17242 		float constant_val;
17243 		float base_damage;
17244 		bool using_constant = false;
17245 
17246 		// set storage locations to zero
17247 		for (i = 0; i < AT_NUM_STORAGE_LOCATIONS; i++) {
17248 			storage[i]=0.0f;
17249 		}
17250 
17251 		// check to see if we need to difficulty scale damage first
17252 		if (adtp->difficulty_scale_type == ADT_DIFF_SCALE_FIRST) {
17253 			damage_applied *= diff_dmg_scale;
17254 		}
17255 
17256 		// user may want to use base damage as a constant
17257 		base_damage = damage_applied;
17258 		// LOOP!
17259 		for (i = 0; i < num; i++) {
17260 			storage_idx = adtp->altArguments[i];
17261 			//Set curr_arg
17262 			// use storage index at +Stored Value:
17263 			if ( (storage_idx >= 0) && (storage_idx < AT_NUM_STORAGE_LOCATIONS) ) {
17264 				curr_arg = storage[storage_idx];
17265 				using_storage = true;
17266 			// using +value: (or error cases caught at parse, where this holda a 0.0f)
17267 			} else if (storage_idx == AT_CONSTANT_NOT_USED) { // save time checking all possible constants when most of the time you will be using +value:
17268 				curr_arg = adtp->Arguments[i];
17269 			// maybe handle constants
17270 			} else if (storage_idx == AT_CONSTANT_BASE_DMG) {
17271 				curr_arg = base_damage;
17272 				using_constant = true;
17273 			} else if (storage_idx == AT_CONSTANT_CURRENT_DMG) {
17274 				curr_arg = damage_applied;
17275 				using_constant = true;
17276 			} else if (storage_idx == AT_CONSTANT_DIFF_FACTOR) {
17277 				curr_arg = diff_dmg_scale;
17278 				using_constant = true;
17279 			} else if (storage_idx == AT_CONSTANT_RANDOM) {
17280 				constant_val = frand();
17281 				curr_arg = constant_val;
17282 				using_constant = true;
17283 			} else if (storage_idx == AT_CONSTANT_PI) {
17284 				constant_val = PI;
17285 				curr_arg = constant_val;
17286 				using_constant = true;
17287 			} else { // fail
17288 				constant_val = 0.0f;
17289 				curr_arg = constant_val;
17290 			}
17291 			// new calcs go here
17292 			switch(adtp->Calculations[i])
17293 			{
17294 				case AT_TYPE_ADDITIVE:
17295 					damage_applied += curr_arg;
17296 					break;
17297 				case AT_TYPE_MULTIPLICATIVE:
17298 					damage_applied *= curr_arg;
17299 					break;
17300 				case AT_TYPE_EXPONENTIAL:
17301 					damage_applied = powf(damage_applied, curr_arg);
17302 					break;
17303 				case AT_TYPE_EXPONENTIAL_BASE:
17304 					damage_applied = powf(curr_arg, damage_applied);
17305 					break;
17306 				case AT_TYPE_CUTOFF:
17307 					if(damage_applied < curr_arg)
17308 						damage_applied = 0;
17309 					break;
17310 				case AT_TYPE_REVERSE_CUTOFF:
17311 					if(damage_applied > curr_arg)
17312 						damage_applied = 0;
17313 					break;
17314 				case AT_TYPE_INSTANT_CUTOFF:
17315 					if(damage_applied < curr_arg)
17316 					{
17317 						damage_applied = 0;
17318 						end_now = true;
17319 					}
17320 					break;
17321 				case AT_TYPE_INSTANT_REVERSE_CUTOFF:
17322 					if(damage_applied > curr_arg)
17323 					{
17324 						damage_applied = 0;
17325 						end_now = true;
17326 					}
17327 					break;
17328 				case AT_TYPE_CAP:
17329 					if (damage_applied > curr_arg)
17330 						damage_applied = curr_arg;
17331 					break;
17332 				case AT_TYPE_INSTANT_CAP:
17333 					if (damage_applied > curr_arg) {
17334 						damage_applied = curr_arg;
17335 						end_now = true;
17336 					}
17337 					break;
17338 				case AT_TYPE_SET:
17339 					damage_applied = curr_arg;
17340 					break;
17341 				case AT_TYPE_STORE:
17342 					if (using_storage || using_constant) {
17343 						Warning(LOCATION, "Cannot use +Stored Value: or +Constant: with +Store:, that would be bad. Skipping calculation.");
17344 					} else {
17345 						storage_idx =  int(floorf(curr_arg));
17346 						// Nuke: idiotproof this, no segfault 4 u
17347 						if ( (storage_idx < 0) || (storage_idx >= AT_NUM_STORAGE_LOCATIONS) ) {
17348 							Warning(LOCATION, "+Value: for +Store: calculation out of range. Should be between 0 and %i. Read: %i, Skipping calculation.", AT_NUM_STORAGE_LOCATIONS, storage_idx);
17349 							storage_idx = 0;
17350 						} else {
17351 							storage[storage_idx] = damage_applied;
17352 						}
17353 					}
17354 					break;
17355 				case AT_TYPE_LOAD:
17356 					if (using_storage || using_constant) {
17357 						Warning(LOCATION, "Cannot use +Stored Value: or +Constant: with +Load:, that would be bad. Skipping calculation.");
17358 					} else {
17359 						storage_idx =  int(floorf(curr_arg));
17360 						// Nuke: idiotproof this, no segfault 4 u
17361 						if ( (storage_idx < 0) || (storage_idx >= AT_NUM_STORAGE_LOCATIONS) ) {
17362 							Warning(LOCATION, "+Value: for +Load: calculation out of range. Should be between 0 and %i. Read: %i, Skipping calculation.", AT_NUM_STORAGE_LOCATIONS, storage_idx);
17363 							storage_idx = 0;
17364 						} else {
17365 							damage_applied = storage[storage_idx];
17366 						}
17367 					}
17368 					break;
17369 				case AT_TYPE_RANDOM:  // Nuke: get a random number between damage_applied and +value:
17370 					if (damage_applied > curr_arg) {
17371 						damage_applied = frand_range( curr_arg, damage_applied );
17372 					} else {
17373 						damage_applied = frand_range( damage_applied, curr_arg );
17374 					}
17375 				break;
17376 			}
17377 
17378 			if(end_now)
17379 				break;
17380 		}
17381 		// Nuke: check to see if we need to difficulty scale damage last
17382 		if (adtp->difficulty_scale_type == ADT_DIFF_SCALE_LAST)
17383 			damage_applied *= diff_dmg_scale;
17384 
17385 		return damage_applied;
17386 	}
17387 	// fail return is fail
17388 	return (damage_applied * diff_dmg_scale);
17389 }
17390 
GetShieldPiercePCT(int damage_type_idx)17391 float ArmorType::GetShieldPiercePCT(int damage_type_idx)
17392 {
17393 	if(damage_type_idx < 0)
17394 		return 0.0f;
17395 
17396 	//Initialize vars
17397 	uint i,num;
17398 	ArmorDamageType *adtp = NULL;
17399 
17400 	//Find the entry in the weapon that corresponds to the given weapon damage type
17401 	num = DamageTypes.size();
17402 	for(i = 0; i < num; i++)
17403 	{
17404 		if(DamageTypes[i].DamageTypeIndex == damage_type_idx)
17405 		{
17406 			adtp = &DamageTypes[i];
17407 			break;
17408 		}
17409 	}
17410 	if(adtp != NULL){
17411 		return adtp->shieldpierce_pct;
17412 	}
17413 
17414 	return 0.0f;
17415 }
17416 
GetPiercingType(int damage_type_idx)17417 int ArmorType::GetPiercingType(int damage_type_idx)
17418 {
17419 	if(damage_type_idx < 0)
17420 		return 0;
17421 
17422 	//Initialize vars
17423 	uint i,num;
17424 	ArmorDamageType *adtp = NULL;
17425 
17426 	//Find the entry in the weapon that corresponds to the given weapon damage type
17427 	num = DamageTypes.size();
17428 	for(i = 0; i < num; i++)
17429 	{
17430 		if(DamageTypes[i].DamageTypeIndex == damage_type_idx)
17431 		{
17432 			adtp = &DamageTypes[i];
17433 			break;
17434 		}
17435 	}
17436 	if(adtp != NULL){
17437 		return adtp->piercing_type;
17438 	}
17439 
17440 	return 0;
17441 }
17442 
GetPiercingLimit(int damage_type_idx)17443 float ArmorType::GetPiercingLimit(int damage_type_idx)
17444 {
17445 	if(damage_type_idx < 0)
17446 		return 0.0f;
17447 
17448 	//Initialize vars
17449 	uint i,num;
17450 	ArmorDamageType *adtp = NULL;
17451 
17452 	//Find the entry in the weapon that corresponds to the given weapon damage type
17453 	num = DamageTypes.size();
17454 	for(i = 0; i < num; i++)
17455 	{
17456 		if(DamageTypes[i].DamageTypeIndex == damage_type_idx)
17457 		{
17458 			adtp = &DamageTypes[i];
17459 			break;
17460 		}
17461 	}
17462 	if(adtp != NULL){
17463 		return adtp->piercing_start_pct;
17464 	}
17465 
17466 	return 0.0f;
17467 }
17468 
17469 //***********************************Member functions
17470 
ArmorType(char * in_name)17471 ArmorType::ArmorType(char* in_name)
17472 {
17473 	uint len = strlen(in_name);
17474 	if(len >= NAME_LENGTH) {
17475 		Warning(LOCATION, "Armor name %s is %d characters too long, and will be truncated", in_name, len - NAME_LENGTH);
17476 	}
17477 
17478 	strncpy(Name, in_name, NAME_LENGTH-1);
17479 }
17480 
ParseData()17481 void ArmorType::ParseData()
17482 {
17483 	ArmorDamageType adt;
17484 	char buf[NAME_LENGTH];
17485 	float temp_float;
17486 	int temp_int;
17487 	int calc_type = -1;
17488 
17489 	//Get the damage types
17490 	required_string("$Damage Type:");
17491 	do
17492 	{
17493 		//Get damage type name
17494 		stuff_string(buf, F_NAME, NAME_LENGTH);
17495 
17496 		//Clear the struct and set the index
17497 		adt.clear();
17498 		adt.DamageTypeIndex = damage_type_add(buf);
17499 		bool no_content = true;
17500 
17501 		//Get calculation and argument
17502 		while (optional_string("+Calculation:"))
17503 		{
17504 			//+Calculation
17505 			stuff_string(buf, F_NAME, NAME_LENGTH);
17506 
17507 			calc_type = calculation_type_get(buf);
17508 
17509 			//Make sure we have a valid calculation type
17510 			if(calc_type == -1)
17511 			{
17512 				Warning(LOCATION, "Armor '%s': Armor calculation type '%s' is invalid, and has been skipped", Name, buf);
17513 				// Nuke: guess we need to add this here too
17514 				if (optional_string("+Stored Value:")) {
17515 					stuff_int(&temp_int);
17516 				} else if (optional_string("+Constant:")) {
17517 					stuff_string(buf, F_NAME, NAME_LENGTH);
17518 				} else {
17519 					required_string("+Value:");
17520 					stuff_float(&temp_float);
17521 				}
17522 			}
17523 			else
17524 			{
17525 				adt.Calculations.push_back(calc_type);
17526 				// Nuke: maybe were using a stored location
17527 				if (optional_string("+Stored Value:")) {
17528 					stuff_int(&temp_int);
17529 					// Nuke: idiot-proof
17530 					if ( (temp_int < 0) || (temp_int >= AT_NUM_STORAGE_LOCATIONS) ) {
17531 						Error(LOCATION, "+Stored Value: is out of range. Should be between 0 and %i. Read: %i, Using value 0.", AT_NUM_STORAGE_LOCATIONS-1, temp_int);
17532 						temp_int = AT_CONSTANT_NOT_USED;
17533 					}
17534 					adt.altArguments.push_back(temp_int);
17535 					adt.Arguments.push_back(0.0f); // this isnt used in this case, just take up space so the indices lign up, also a fallback value in case of bad altArguments
17536 				} else if (optional_string("+Constant:")) { // use one of the pre-defined constants
17537 					stuff_string(buf, F_NAME, NAME_LENGTH);
17538 					temp_int = armor_type_constants_get(buf);
17539 					// Nuke: idiot proof some more
17540 					if (temp_int == AT_CONSTANT_BAD_VAL) {
17541 						Error(LOCATION, "Invalid +Constant: name, '%s'. Using value 0.", buf);
17542 						temp_int = AT_CONSTANT_NOT_USED;
17543 					}
17544 					adt.altArguments.push_back(temp_int);
17545 					adt.Arguments.push_back(0.0f); // this isnt used in this case, just take up space so the indices lign up, also a fallback value in case of bad altArguments
17546 				} else { // Nuke: +Value, only required if storage location or constant is not used -nuke
17547 					required_string("+Value:");
17548 					stuff_float(&temp_float);
17549 					adt.altArguments.push_back(AT_CONSTANT_NOT_USED); // set this to AT_CONSTANT_NOT_USED so we know to just use the value from adt.Arguments instead of constants/storage locations
17550 					adt.Arguments.push_back(temp_float);
17551 				}
17552 				no_content = false;
17553 			}
17554 		}
17555 
17556 		adt.shieldpierce_pct = 0.0f;
17557 
17558 		if(optional_string("+Shield Piercing Percentage:")) {
17559 			stuff_float(&temp_float);
17560 			CLAMP(temp_float, 0.0f, 1.0f);
17561 			adt.shieldpierce_pct = temp_float;
17562 			no_content = false;
17563 		}
17564 
17565 		adt.piercing_start_pct = 0.1f;
17566 		adt.piercing_type = -1;
17567 
17568 		if(optional_string("+Weapon Piercing Effect Start Limit:")) {
17569 			stuff_float(&temp_float);
17570 			CLAMP(temp_float, 0.0f, 100.0f);
17571 			temp_float /= 100.0f;
17572 			adt.piercing_start_pct = temp_float;
17573 			no_content = false;
17574 		}
17575 
17576 		if(optional_string("+Weapon Piercing Type:")) {
17577 			stuff_string(buf, F_NAME, NAME_LENGTH);
17578 			adt.piercing_type = piercing_type_get(buf);
17579 			no_content = false;
17580 		}
17581 
17582 		// Nuke: don't forget to init things
17583 		adt.difficulty_scale_type = ADT_DIFF_SCALE_FIRST;
17584 
17585 		if (optional_string("+Difficulty Scale Type:")) {
17586 			stuff_string(buf, F_NAME, NAME_LENGTH);
17587 			temp_int = difficulty_scale_type_get(buf);
17588 			if (temp_int == ADT_DIFF_SCALE_BAD_VAL) {
17589 				Error(LOCATION, "Invalid +Difficulty Scale Type: name: '%s'. Reverting to default behavior.", buf);
17590 				adt.difficulty_scale_type = ADT_DIFF_SCALE_FIRST;
17591 			} else {
17592 				adt.difficulty_scale_type = temp_int;
17593 			}
17594 			no_content = false;
17595 		}
17596 
17597 		//If we have calculations in this damage type, add it
17598 		if(!no_content)
17599 		{
17600 			if(adt.Calculations.size() != adt.Arguments.size())
17601 			{
17602 				Warning(LOCATION, "Armor '%s', damage type %d: Armor has a different number of calculation types than arguments (%d, %d)", Name, DamageTypes.size(), adt.Calculations.size(), adt.Arguments.size());
17603 			}
17604 			DamageTypes.push_back(adt);
17605 		}
17606 	} while(optional_string("$Damage Type:"));
17607 }
17608 
17609 //********************************Global functions
17610 
armor_type_get_idx(char * name)17611 int armor_type_get_idx(char* name)
17612 {
17613 	int i, num;
17614 	num = Armor_types.size();
17615 	for(i = 0; i < num; i++)
17616 	{
17617 		if(Armor_types[i].IsName(name))
17618 			return i;
17619 	}
17620 
17621 	//Didn't find anything.
17622 	return -1;
17623 }
17624 
parse_armor_type()17625 void parse_armor_type()
17626 {
17627 	char name_buf[NAME_LENGTH];
17628 	ArmorType tat("");
17629 
17630 	required_string("$Name:");
17631 	stuff_string(name_buf, F_NAME, NAME_LENGTH);
17632 
17633 	tat = ArmorType(name_buf);
17634 
17635 	//now parse the actual table (damage type/armor type pairs)
17636 	tat.ParseData();
17637 
17638 	//rest of the parse data
17639 	if (optional_string("$Flags:"))
17640 		parse_string_flag_list((int*)&tat.flags, Armor_flags, Num_armor_flags);
17641 
17642 	//Add it to global armor types
17643 	Armor_types.push_back(tat);
17644 }
17645 
armor_parse_table(const char * filename)17646 void armor_parse_table(const char *filename)
17647 {
17648 	int rval;
17649 
17650 	// open localization
17651 	lcl_ext_open();
17652 
17653 	if ((rval = setjmp(parse_abort)) != 0) {
17654 		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", filename, rval));
17655 		lcl_ext_close();
17656 		return;
17657 	}
17658 
17659 	read_file_text(filename, CF_TYPE_TABLES);
17660 	reset_parse();
17661 
17662 	//Enumerate through all the armor types and add them.
17663 	while ( optional_string("#Armor Type") ) {
17664 		while ( required_string_either("#End", "$Name:") ) {
17665 			parse_armor_type();
17666 			continue;
17667 		}
17668 
17669 		required_string("#End");
17670 	}
17671 
17672 	// add tbl/tbm to multiplayer validation list
17673 	fs2netd_add_table_validation(filename);
17674 
17675 	// close localization
17676 	lcl_ext_close();
17677 }
17678 
armor_init()17679 void armor_init()
17680 {
17681 	if (!armor_inited) {
17682 		armor_parse_table("armor.tbl");
17683 
17684 		parse_modular_table(NOX("*-amr.tbm"), armor_parse_table);
17685 
17686 		armor_inited = 1;
17687 	}
17688 }
17689 
17690 //**************************************************************
17691 // AI targeting priority functions
17692 //**************************************************************
parse_ai_target_priorities()17693 void parse_ai_target_priorities()
17694 {
17695 	int i, j, num_strings;
17696 	int n_entries = Ai_tp_list.size();
17697 	SCP_vector <SCP_string> temp_strings;
17698 
17699 	bool first_time = false;
17700 	int already_exists = -1;
17701 
17702 	if (n_entries == 0)
17703 		first_time = true;
17704 
17705 	required_string("$Name:");
17706 	ai_target_priority temp_priority = init_ai_target_priorities();
17707 
17708 	stuff_string(temp_priority.name, F_NAME, NAME_LENGTH);
17709 	if (first_time == false) {
17710 		for (i = 0; i < n_entries; i++) {
17711 			if (!strnicmp(temp_priority.name, Ai_tp_list[i].name, NAME_LENGTH)) {
17712 				already_exists = i;
17713 			}
17714 		}
17715 	}
17716 
17717 	if (optional_string("+Object Type:") ) {
17718 		char tempname[NAME_LENGTH];
17719 		stuff_string(tempname, F_NAME, NAME_LENGTH);
17720 
17721 		for (j = 0; j < num_ai_tgt_objects; j++) {
17722 			if ( !stricmp(ai_tgt_objects[j].name, tempname) ) {
17723 				temp_priority.obj_type = ai_tgt_objects[j].def;
17724 			}
17725 		}
17726 	}
17727 
17728 	if (optional_string("+Weapon Class:") ) {
17729 		temp_strings.clear();
17730 		num_strings = stuff_string_list(temp_strings);
17731 
17732 		for(i = 0; i < num_strings; i++) {
17733 			for(j = 0; j < MAX_WEAPON_TYPES ; j++) {
17734 				if ( !stricmp(Weapon_info[j].name, temp_strings[i].c_str()) ) {
17735 					temp_priority.weapon_class.push_back(j);
17736 					break;
17737 				}
17738 			}
17739 			if (j == MAX_WEAPON_TYPES) {
17740 				Warning(LOCATION, "Unidentified weapon class '%s' set for target priority group '%s'\n", temp_strings[i].c_str(), temp_priority.name);
17741 			}
17742 		}
17743 	}
17744 
17745 	if (optional_string("+Object Flags:") ) {
17746 		temp_strings.clear();
17747 		num_strings = stuff_string_list(temp_strings);
17748 
17749 		for (i = 0; i < num_strings; i++) {
17750 			for (j = 0; j < num_ai_tgt_obj_flags; j++) {
17751 				if ( !stricmp(ai_tgt_obj_flags[j].name, temp_strings[i].c_str()) ) {
17752 					temp_priority.obj_flags |= ai_tgt_obj_flags[j].def;
17753 					break;
17754 				}
17755 			}
17756 			if (j == num_ai_tgt_obj_flags) {
17757 				Warning(LOCATION, "Unidentified object flag '%s' set for target priority group '%s'\n", temp_strings[i].c_str(), temp_priority.name);
17758 			}
17759 		}
17760 	}
17761 
17762 	if (optional_string("+Ship Class Flags:") ) {
17763 		temp_strings.clear();
17764 		num_strings = stuff_string_list(temp_strings);
17765 
17766 		for (i = 0; i < num_strings; i++) {
17767 			for (j = 0; j < num_ai_tgt_ship_flags; j++) {
17768 				if ( !stricmp(ai_tgt_ship_flags[j].name, temp_strings[i].c_str()) ) {
17769 					if (ai_tgt_ship_flags[j].var == 0) {
17770 						temp_priority.sif_flags |= ai_tgt_ship_flags[j].def;
17771 					} else {
17772 						temp_priority.sif2_flags |= ai_tgt_ship_flags[j].def;
17773 					}
17774 					break;
17775 				}
17776 			}
17777 			if (j == num_ai_tgt_ship_flags) {
17778 				Warning(LOCATION, "Unidentified ship class flag '%s' set for target priority group '%s'\n", temp_strings[i].c_str(), temp_priority.name);
17779 			}
17780 		}
17781 	}
17782 
17783 	if (optional_string("+Weapon Class Flags:") ) {
17784 		temp_strings.clear();
17785 		num_strings = stuff_string_list(temp_strings);
17786 
17787 		for (i = 0; i < num_strings; i++) {
17788 			for (j = 0; j < num_ai_tgt_weapon_flags; j++) {
17789 				if ( !stricmp(ai_tgt_weapon_flags[j].name, temp_strings[i].c_str()) ) {
17790 					if (ai_tgt_weapon_flags[j].var == 0) {
17791 						temp_priority.wif_flags |= ai_tgt_weapon_flags[j].def;
17792 					} else {
17793 						temp_priority.wif2_flags |= ai_tgt_weapon_flags[j].def;
17794 					}
17795 					break;
17796 				}
17797 			}
17798 			if (j == num_ai_tgt_weapon_flags) {
17799 				Warning(LOCATION, "Unidentified weapon class flag '%s' set for target priority group '%s'\n", temp_strings[i].c_str(), temp_priority.name);
17800 			}
17801 		}
17802 	}
17803 
17804 	temp_strings.clear();
17805 
17806 	if (already_exists == -1) {
17807 		Ai_tp_list.push_back(temp_priority);
17808 	} else {
17809 		Ai_tp_list[already_exists] = temp_priority;
17810 	}
17811 }
17812 
init_ai_target_priorities()17813 ai_target_priority init_ai_target_priorities()
17814 {
17815 	ai_target_priority temp_priority;
17816 
17817 	//initialize the entries
17818 	temp_priority.obj_flags = 0;
17819 	temp_priority.obj_type = -1;
17820 	temp_priority.ship_class.clear();
17821 	temp_priority.ship_type.clear();
17822 	temp_priority.sif_flags = 0;
17823 	temp_priority.sif2_flags = 0;
17824 	temp_priority.weapon_class.clear();
17825 	temp_priority.wif2_flags = 0;
17826 	temp_priority.wif_flags = 0;
17827 	temp_priority.name[0] = '\0';
17828 
17829 	//return the initialized
17830 	return temp_priority;
17831 }
17832 
parse_weapon_targeting_priorities()17833 void parse_weapon_targeting_priorities()
17834 {
17835 	char tempname[NAME_LENGTH];
17836 	int i = 0;
17837 	int j = 0;
17838 	int k = 0;
17839 
17840 	if (optional_string("$Name:")) {
17841 		stuff_string(tempname, F_NAME, NAME_LENGTH);
17842 
17843 		for(k = 0; k < MAX_WEAPON_TYPES ; k++) {
17844 			if ( !stricmp(Weapon_info[k].name, tempname) ) {
17845 				// found weapon, yay!
17846 				// reset the list
17847 
17848 				weapon_info *wip = &Weapon_info[k];
17849 
17850 				wip->num_targeting_priorities = 0;
17851 
17852 				if (optional_string("+Target Priority:")) {
17853 					SCP_vector <SCP_string> tgt_priorities;
17854 					int num_strings = stuff_string_list(tgt_priorities);
17855 
17856 					if (num_strings > 32)
17857 						num_strings = 32;
17858 
17859 					int num_groups = Ai_tp_list.size();
17860 
17861 					for(i = 0; i < num_strings; i++) {
17862 						for(j = 0; j < num_groups; j++) {
17863 							if ( !stricmp(Ai_tp_list[j].name, tgt_priorities[i].c_str()))  {
17864 								wip->targeting_priorities[i] = j;
17865 								wip->num_targeting_priorities++;
17866 								break;
17867 							}
17868 						}
17869 						if(j == num_groups)
17870 							Warning(LOCATION, "Unrecognized string '%s' found when setting weapon targeting priorities.\n", tgt_priorities[i].c_str());
17871 					}
17872 				}
17873 				// no need to keep searching for more
17874 				break;
17875 			}
17876 		}
17877 		if(k == MAX_WEAPON_TYPES)
17878 			Warning(LOCATION, "Unrecognized weapon '%s' found when setting weapon targeting priorities.\n", tempname);
17879 	}
17880 }
17881 
ship_get_subobj_model_num(ship_info * sip,char * subobj_name)17882 int ship_get_subobj_model_num(ship_info* sip, char* subobj_name)
17883 {
17884 	for (int i = 0; i < sip->n_subsystems; i++) {
17885 		if (!subsystem_stricmp(sip->subsystems[i].subobj_name, subobj_name))
17886 			return sip->subsystems[i].subobj_num;
17887 	}
17888 
17889 	return -1;
17890 }
17891 
init_path_metadata(path_metadata & metadata)17892 void init_path_metadata(path_metadata& metadata)
17893 {
17894 	vm_vec_zero(&metadata.departure_rvec);
17895 }
17896 
ship_get_sound(object * objp,GameSoundsIndex id)17897 int ship_get_sound(object *objp, GameSoundsIndex id)
17898 {
17899 	Assert( objp != NULL );
17900 	Assert( id >= 0 && id < (int) Snds.size() );
17901 
17902 	// ugh, it's possible that we're an observer at this point
17903 	if (objp->type == OBJ_OBSERVER)
17904 		return id;
17905 
17906 	Assert( objp->type == OBJ_SHIP );
17907 
17908 	ship *shipp = &Ships[objp->instance];
17909 	ship_info *sip = &Ship_info[shipp->ship_info_index];
17910 
17911 	SCP_map<GameSoundsIndex, int>::iterator element = sip->ship_sounds.find(id);
17912 
17913 	if (element == sip->ship_sounds.end())
17914 		return id;
17915 	else
17916 		return (*element).second;
17917 }
17918 
ship_has_sound(object * objp,GameSoundsIndex id)17919 bool ship_has_sound(object *objp, GameSoundsIndex id)
17920 {
17921 	Assert( objp != NULL );
17922 	Assert( id >= 0 && id < (int) Snds.size() );
17923 
17924 	Assert( objp->type == OBJ_SHIP );
17925 
17926 	ship *shipp = &Ships[objp->instance];
17927 	ship_info *sip = &Ship_info[shipp->ship_info_index];
17928 
17929 	SCP_map<GameSoundsIndex, int>::iterator element = sip->ship_sounds.find(id);
17930 
17931 	if (element == sip->ship_sounds.end())
17932 		return false;
17933 	else
17934 		return true;
17935 }
17936 
17937 /**
17938  * Given a ship with bounding box and a point, find the closest point on the bbox
17939  *
17940  * @param ship_obj Object that has the bounding box (should be a ship)
17941  * @param start World position of the point being compared
17942  * @param box_pt OUTPUT PARAMETER: closest point on the bbox to start
17943  *
17944  * @return point is inside bbox, TRUE/1
17945  * @return point is outside bbox, FALSE/0
17946  */
get_nearest_bbox_point(object * ship_obj,vec3d * start,vec3d * box_pt)17947 int get_nearest_bbox_point(object *ship_obj, vec3d *start, vec3d *box_pt)
17948 {
17949 	vec3d temp, rf_start;
17950 	polymodel *pm;
17951 	pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].model_num);
17952 
17953 	// get start in ship rf
17954 	vm_vec_sub(&temp, start, &ship_obj->pos);
17955 	vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
17956 
17957 	// find box_pt
17958 	int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
17959 
17960 	// get box_pt in world rf
17961 	vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
17962 	vm_vec_add2(box_pt, &ship_obj->pos);
17963 
17964 	return inside;
17965 }
17966