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(°rees);
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(°rees);
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(°rees);
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(°rees);
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(°rees);
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(°rees);
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(°rees);
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(¤t_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(¤t_trigger->start);
3715 else
3716 current_trigger->start = 0;
3717
3718 if ( optional_string("+reverse_delay:") )
3719 stuff_int(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_trigger->end );
3758 else
3759 current_trigger->end = 0;
3760 }else{
3761
3762 if(optional_string("+delay:"))
3763 stuff_int(¤t_trigger->start);
3764 else
3765 current_trigger->start = 0;
3766
3767 if ( optional_string("+reverse_delay:") )
3768 stuff_int(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_trigger->end );
3808 else
3809 current_trigger->end = 0;
3810
3811 if(optional_string("$Sound:")){
3812 required_string("+Start:");
3813 stuff_int(¤t_trigger->start_sound );
3814 required_string("+Loop:");
3815 stuff_int(¤t_trigger->loop_sound );
3816 required_string("+End:");
3817 stuff_int(¤t_trigger->end_sound );
3818 required_string("+Radius:");
3819 stuff_float(¤t_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