-
Notifications
You must be signed in to change notification settings - Fork 183
Expand file tree
/
Copy pathshiphit.cpp
More file actions
executable file
·2583 lines (2175 loc) · 88.2 KB
/
Copy pathshiphit.cpp
File metadata and controls
executable file
·2583 lines (2175 loc) · 88.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include <algorithm>
#include "asteroid/asteroid.h"
#include "debris/debris.h"
#include "fireball/fireballs.h"
#include "freespace.h"
#include "gamesequence/gamesequence.h"
#include "gamesnd/eventmusic.h"
#include "gamesnd/gamesnd.h"
#include "globalincs/linklist.h"
#include "hud/hud.h"
#include "hud/hudmessage.h"
#include "hud/hudtarget.h"
#include "iff_defs/iff_defs.h"
#include "io/joy_ff.h"
#include "io/keycontrol.h"
#include "io/timer.h"
#include "mission/missionlog.h"
#include "mod_table/mod_table.h"
#include "network/multi.h"
#include "network/multi_pmsg.h"
#include "network/multi_respawn.h"
#include "network/multimsgs.h"
#include "network/multiutil.h"
#include "object/object.h"
#include "object/objectdock.h"
#include "object/objectshield.h"
#include "object/objectsnd.h"
#include "parse/parselo.h"
#include "scripting/scripting.h"
#include "playerman/player.h"
#include "popup/popup.h"
#include "render/3d.h"
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "ship/shiphit.h"
#include "weapon/beam.h"
#include "weapon/emp.h"
#include "weapon/shockwave.h"
#include "weapon/weapon.h"
//#pragma optimize("", off)
//#pragma auto_inline(off)
struct ssm_firing_info;
extern void ssm_create(object *target, vec3d *start, size_t ssm_index, ssm_firing_info *override, int team);
typedef struct spark_pair {
int index1, index2;
float dist;
} spark_pair;
#define MAX_SPARK_PAIRS ((MAX_SHIP_HITS * MAX_SHIP_HITS - MAX_SHIP_HITS) / 2)
#define BIG_SHIP_MIN_RADIUS 80.0f // ship radius above which death rolls can't be shortened by excessive damage
vec3d Dead_camera_pos;
vec3d Original_vec_to_deader;
static bool global_damage = false;
//WMC - Camera rough draft stuff
/*
camid dead_get_camera()
{
static camid dead_camera;
if(!dead_camera.isValid())
dead_camera = cam_create("Dead camera");
return dead_camera;
}
*/
bool is_subsys_destroyed(ship *shipp, int submodel)
{
ship_subsys *subsys;
if (submodel == -1) {
return false;
}
for ( subsys=GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys) ) {
if (subsys->system_info->subobj_num == submodel) {
if (subsys->current_hits > 0.0f) {
return false;
} else {
return true;
}
}
}
return false;
}
// do_subobj_destroyed_stuff is called when a subobject for a ship is killed. Separated out
// to separate function on 10/15/97 by MWA for easy multiplayer access. It does all of the
// cool things like blowing off the model (if applicable, writing the logs, etc)
void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vec3d* hitpos, bool no_explosion )
{
ship_info *sip;
object *ship_objp;
model_subsystem *psub;
vec3d g_subobj_pos;
int type, i, log_index;
// get some local variables
sip = &Ship_info[ship_p->ship_info_index];
ship_objp = &Objects[ship_p->objnum];
psub = subsys->system_info;
type = psub->type;
get_subsystem_world_pos(ship_objp, subsys, &g_subobj_pos);
// create fireballs when subsys destroy for large ships.
object* objp = &Objects[ship_p->objnum];
if (!(subsys->flags[Ship::Subsystem_Flags::Vanished]) && !no_explosion) {
if (objp->radius > 100.0f) {
// number of fireballs determined by radius of subsys
int num_fireballs;
if ( psub->radius < 3 ) {
num_fireballs = 1;
} else {
num_fireballs = 5;
}
vec3d temp_vec, center_to_subsys, rand_vec;
vm_vec_sub(¢er_to_subsys, &g_subobj_pos, &objp->pos);
for (i=0; i<num_fireballs; i++) {
if (i==0) {
// make first fireball at hitpos
if (hitpos) {
temp_vec = *hitpos;
} else {
temp_vec = g_subobj_pos;
}
} else {
// make other fireballs at random positions, but try to keep on the surface
vm_vec_rand_vec_quick(&rand_vec);
float dot = vm_vec_dot(¢er_to_subsys, &rand_vec);
vm_vec_scale_add2(&rand_vec, ¢er_to_subsys, -dot/vm_vec_mag_squared(¢er_to_subsys));
vm_vec_scale_add(&temp_vec, &g_subobj_pos, &rand_vec, 0.5f*psub->radius);
}
// scale fireball size according to size of subsystem, but not less than 10
float fireball_rad = psub->radius * 0.2f;
if (fireball_rad < 10) {
fireball_rad = 10.0f;
}
vec3d fb_vel;
vm_vec_cross(&fb_vel, &objp->phys_info.rotvel, ¢er_to_subsys);
vm_vec_add2(&fb_vel, &objp->phys_info.vel);
int fireball_type = fireball_ship_explosion_type(sip);
if(fireball_type < 0) {
fireball_type = FIREBALL_EXPLOSION_MEDIUM;
}
fireball_create( &temp_vec, fireball_type, FIREBALL_MEDIUM_EXPLOSION, OBJ_INDEX(objp), fireball_rad, 0, &fb_vel );
}
}
}
if ( MULTIPLAYER_MASTER ) {
int index;
index = ship_get_index_from_subsys(subsys, ship_p->objnum);
vec3d hit;
if (hitpos) {
hit = *hitpos;
} else {
hit = g_subobj_pos;
}
send_subsystem_destroyed_packet( ship_p, index, hit );
}
// next do a quick sanity check on the current hits that we are keeping for the generic subsystems
// I think that there might be rounding problems with the floats. This code keeps us safe.
if ( ship_p->subsys_info[type].type_count == 1 ) {
ship_p->subsys_info[type].aggregate_current_hits = 0.0f;
} else {
float hits;
ship_subsys *ssp;
hits = 0.0f;
for ( ssp=GET_FIRST(&ship_p->subsys_list); ssp != END_OF_LIST(&ship_p->subsys_list); ssp = GET_NEXT(ssp) ) {
// type matches?
if ( (ssp->system_info->type == type) && !(ssp->flags[Ship::Subsystem_Flags::No_aggregate]) ) {
hits += ssp->current_hits;
}
}
ship_p->subsys_info[type].aggregate_current_hits = hits;
}
// store an event in the event log. Also, determine if all turrets or all
// engines have been destroyed (if the subsystem is a turret or engine).
// put a disabled or disarmed entry in the log if this is the case
//
// MWA -- 1/8/98 A problem was found when trying to determine (via sexpression) when some subsystems
// were destroyed. The bottom line is that is the psub->name and psub->subobj_name are different,
// then direct detection doesn't work. (This scenario happens mainly with turrets and probably with
// engines). So, my solution is to encode the ship_info index, and the subsystem index into one
// integer, and pass that as the "index" parameter to add_entry. We'll use that information to
// print out the info in the mission log.
Assert( ship_p->ship_info_index < 65535 );
// get the "index" of this subsystem in the ship info structure.
for ( i = 0; i < sip->n_subsystems; i++ ) {
if ( &(sip->subsystems[i]) == psub )
break;
}
Assert( i < sip->n_subsystems );
Assert( i < 65535 );
log_index = ((ship_p->ship_info_index << 16) & 0xffff0000) | (i & 0xffff);
// Don't log, display info, or play sounds about the activation subsytem
// FUBAR/Goober5000 - or about vanishing subsystems, per precedent with ship-vanish
int notify = (psub->type != SUBSYSTEM_ACTIVATION) && !(subsys->flags[Ship::Subsystem_Flags::Vanished]);
if (notify)
{
mission_log_add_entry(LOG_SHIP_SUBSYS_DESTROYED, ship_p->ship_name, psub->subobj_name, log_index );
if ( ship_objp == Player_obj )
{
if (!no_explosion) {
snd_play( &Snds[SND_SUBSYS_DIE_1], 0.0f );
}
if (strlen(psub->alt_dmg_sub_name))
HUD_printf(XSTR( "Your %s subsystem has been destroyed", 499), psub->alt_dmg_sub_name);
else {
char r_name[NAME_LENGTH];
strcpy_s(r_name, ship_subsys_get_name(subsys));
for (i = 0; r_name[i] > 0; i++) {
if (r_name[i] == '|')
r_name[i] = ' ';
}
HUD_printf(XSTR( "Your %s subsystem has been destroyed", 499), r_name );
}
}
}
if ( psub->type == SUBSYSTEM_TURRET ) {
if ( ship_p->subsys_info[type].aggregate_current_hits <= 0.0f ) {
// Don't create "disarmed" event for small ships.
if (!(Ship_info[ship_p->ship_info_index].is_small_ship())) {
mission_log_add_entry(LOG_SHIP_DISARMED, ship_p->ship_name, NULL );
// ship_p->flags |= SF_DISARMED;
}
}
} else if (psub->type == SUBSYSTEM_ENGINE ) {
// when an engine is destroyed, we must change the max velocity of the ship
// to be some fraction of its normal maximum value
if ( ship_p->subsys_info[type].aggregate_current_hits <= 0.0f ) {
mission_log_add_entry(LOG_SHIP_DISABLED, ship_p->ship_name, NULL );
ship_p->flags.set(Ship::Ship_Flags::Disabled); // add the disabled flag
}
}
if ( psub->subobj_num > -1 ) {
shipfx_blow_off_subsystem(ship_objp,ship_p,subsys,&g_subobj_pos,no_explosion);
subsys->submodel_info_1.blown_off = 1;
}
if ( (psub->subobj_num != psub->turret_gun_sobj) && (psub->turret_gun_sobj >= 0) ) {
subsys->submodel_info_2.blown_off = 1;
}
if (notify && !no_explosion) {
// play sound effect when subsys gets blown up
int sound_index=-1;
if ( Ship_info[ship_p->ship_info_index].is_huge_ship() ) {
sound_index=SND_CAPSHIP_SUBSYS_EXPLODE;
} else if ( Ship_info[ship_p->ship_info_index].is_big_ship() ) {
sound_index=SND_SUBSYS_EXPLODE;
}
if ( sound_index >= 0 ) {
snd_play_3d( &Snds[sound_index], &g_subobj_pos, &View_position );
}
}
// make the shipsounds work as they should...
if(subsys->subsys_snd_flags[Ship::Subsys_Sound_Flags::Alive])
{
obj_snd_delete_type(ship_p->objnum, subsys->system_info->alive_snd, subsys);
subsys->subsys_snd_flags.remove(Ship::Subsys_Sound_Flags::Alive);
}
if(subsys->subsys_snd_flags[Ship::Subsys_Sound_Flags::Turret_rotation])
{
obj_snd_delete_type(ship_p->objnum, subsys->system_info->turret_base_rotation_snd, subsys);
obj_snd_delete_type(ship_p->objnum, subsys->system_info->turret_gun_rotation_snd, subsys);
subsys->subsys_snd_flags.remove(Ship::Subsys_Sound_Flags::Turret_rotation);
}
if(subsys->subsys_snd_flags[Ship::Subsys_Sound_Flags::Rotate])
{
obj_snd_delete_type(ship_p->objnum, subsys->system_info->rotation_snd, subsys);
subsys->subsys_snd_flags.remove(Ship::Subsys_Sound_Flags::Rotate);
}
if((subsys->system_info->dead_snd != -1) && !(subsys->subsys_snd_flags[Ship::Subsys_Sound_Flags::Dead]))
{
obj_snd_assign(ship_p->objnum, subsys->system_info->dead_snd, &subsys->system_info->pnt, 0, OS_SUBSYS_DEAD, subsys);
subsys->subsys_snd_flags.remove(Ship::Subsys_Sound_Flags::Dead);
}
}
// Return weapon type that is associated with damaging_objp
// input: damaging_objp => object pointer responsible for damage
// exit: -1 => no weapon type is associated with damage object
// >=0 => weapon type associated with damage object
int shiphit_get_damage_weapon(object *damaging_objp)
{
int weapon_info_index = -1;
if ( damaging_objp ) {
switch(damaging_objp->type) {
case OBJ_WEAPON:
weapon_info_index = Weapons[damaging_objp->instance].weapon_info_index;
break;
case OBJ_SHOCKWAVE:
weapon_info_index = shockwave_get_weapon_index(damaging_objp->instance);
break;
case OBJ_BEAM:
if (Beams_use_damage_factors) {
weapon_info_index = beam_get_weapon_info_index(damaging_objp);
}
break;
default:
weapon_info_index = -1;
break;
}
}
return weapon_info_index;
}
// Return range at which this object can apply damage.
// Based on object type and subsystem type.
float subsys_get_range(object *other_obj, ship_subsys *subsys)
{
float range;
Assert(subsys); // Goober5000
if ((other_obj) && (other_obj->type == OBJ_SHOCKWAVE)) { // Goober5000 - check for NULL when via sexp
range = shockwave_get_max_radius(other_obj->instance) * 0.75f; // Shockwaves were too lethal to subsystems.
} else if ( subsys->system_info->type == SUBSYSTEM_TURRET ) {
range = subsys->system_info->radius*3;
} else {
range = subsys->system_info->radius*2;
}
return range;
}
#define MAX_DEBRIS_SHARDS 16 // cap the amount of debris shards that fly off per hit
// Make some random debris particles. Previous way was not very random. Create debris 75% of the time.
// Don't worry about multiplayer since this debris is the small stuff that cannot collide
void create_subsys_debris(object *ship_objp, vec3d *hitpos)
{
float show_debris = frand();
if ( show_debris <= 0.75f ) {
int ndebris;
ndebris = (int)(show_debris * Detail.num_small_debris) + 1; // number of pieces of debris to create
if ( ndebris > MAX_DEBRIS_SHARDS )
ndebris = MAX_DEBRIS_SHARDS;
//mprintf(( "Damage = %.1f, ndebris=%d\n", show_debris, ndebris ));
for (int i=0; i<ndebris; i++ ) {
debris_create( ship_objp, -1, -1, hitpos, hitpos, 0, 1.0f );
}
}
}
void create_vaporize_debris(object *ship_objp, vec3d *hitpos)
{
int ndebris;
float show_debris = frand();
ndebris = (int)(4.0f * ((0.5f + show_debris) * Detail.num_small_debris)) + 5; // number of pieces of debris to create
if ( ndebris > MAX_DEBRIS_SHARDS ) {
ndebris = MAX_DEBRIS_SHARDS;
}
//mprintf(( "Damage = %.1f, ndebris=%d\n", show_debris, ndebris ));
for (int i=0; i<ndebris; i++ ) {
debris_create( ship_objp, -1, -1, hitpos, hitpos, 0, 1.4f );
}
}
#define MAX_SUBSYS_LIST 200 //DTP MAX SUBSYS LIST BUMPED FROM 32 to 200, ahmm 32???
typedef struct {
float dist;
float range;
ship_subsys *ptr;
} sublist;
// do_subobj_hit_stuff() is called when a collision is detected between a ship and something
// else. This is where we see if any sub-objects on the ship should take damage.
//
// Depending on where the collision occurs, the sub-system and surrounding hull will take
// different amounts of damage. The amount of damage a sub-object takes depending on how
// close the colliding object is to the center of the sub-object. The remaining hull damage
// will be returned to the caller via the damage parameter.
//
//
// 0 -> 0.5 radius : 100% subobject 0% hull
// 0.5 -> 1.0 radius : 50% subobject 50% hull
// 1.0 -> 2.0 radius : 25% subobject 75% hull
// > 2.0 radius : 0% subobject 100% hull
//
//
// The weapon damage is not neccesarily distributed evently between sub-systems when more than
// one sub-system is to take damage. Whenever damage is to be assigned to a sub-system, the above
// percentages are used. So, if more than one sub-object is taking damage, the second sub-system
// to be assigned damage will take less damage. Eg. weapon hits in the 25% damage range of two
// subsytems, and the weapon damage is 12. First subsystem takes 3 points damage. Second subsystem
// will take 0.25*9 = 2.25 damage. Should be close enough for most cases, and hull would receive
// 0.75 * 9 = 6.75 damage.
//
// Used to use the following constants, but now damage is linearly scaled up to 2x the subsystem
// radius. Same damage applied as defined by constants below.
//
// Returns unapplied damage, which will probably be applied to the hull.
//
// Shockwave damage is handled here. If other_obj->type == OBJ_SHOCKWAVE, it's a shockwave.
// apply the same damage to all subsystems.
// Note: A negative damage number means to destroy the corresponding subsystem. For example, call with -SUBSYSTEM_ENGINE to destroy engine.
//
//WMC - hull_should_apply armor means that the initial subsystem had no armor, so the hull should apply armor instead.
float do_subobj_hit_stuff(object *ship_objp, object *other_obj, vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor)
{
vec3d g_subobj_pos;
float damage_left, damage_if_hull;
int weapon_info_index;
ship_subsys *subsys;
ship *ship_p;
sublist subsys_list[MAX_SUBSYS_LIST];
int subsys_hit_first = -1; // the subsys which should be hit first and take most of the damage; index into subsys_list
vec3d hitpos2;
float ss_dif_scale = 1.0f; // Nuke: Set a base dificulty scale for compatibility
//WMC - first, set this to damage if it isn't NULL, in case we want to return with no damage to subsystems
if(hull_should_apply_armor != NULL) {
*hull_should_apply_armor = true;
}
Assert(ship_objp); // Goober5000 (but other_obj might be NULL via sexp)
Assert(hitpos); // Goober5000
ship_p = &Ships[ship_objp->instance];
// Don't damage player subsystems in a training mission.
if ( The_mission.game_type & MISSION_TYPE_TRAINING ) {
if (ship_objp == Player_obj){
return damage;
}
}
// Shockwave damage is applied like weapon damage. It gets consumed.
if ((other_obj != NULL) && (other_obj->type == OBJ_SHOCKWAVE)) // Goober5000 check for NULL
{
// MK, 9/2/99. Shockwaves do zero subsystem damage on small ships.
// Goober5000 - added back in via flag
if ((Ship_info[ship_p->ship_info_index].is_small_ship()) && !(The_mission.ai_profile->flags[AI::Profile_Flags::Shockwaves_damage_small_ship_subsystems]))
return damage;
else {
damage_left = shockwave_get_damage(other_obj->instance) / 4.0f;
damage_if_hull = damage_left;
}
hitpos2 = other_obj->pos;
} else {
damage_left = damage;
damage_if_hull = damage;
hitpos2 = *hitpos;
}
// scale subsystem damage if appropriate
weapon_info_index = shiphit_get_damage_weapon(other_obj); // Goober5000 - a NULL other_obj returns -1
if ((weapon_info_index >= 0) && ((other_obj->type == OBJ_WEAPON) ||
(Beams_use_damage_factors && (other_obj->type == OBJ_BEAM)))) {
if ( Weapon_info[weapon_info_index].wi_flags[Weapon::Info_Flags::Training] ) {
return damage_left;
}
damage_left *= Weapon_info[weapon_info_index].subsystem_factor;
damage_if_hull *= Weapon_info[weapon_info_index].armor_factor;
}
#ifndef NDEBUG
float hitpos_dist = vm_vec_dist( hitpos, &ship_objp->pos );
if ( hitpos_dist > ship_objp->radius * 2.0f ) {
mprintf(( "BOGUS HITPOS PASSED TO DO_SUBOBJ_HIT_STUFF (%.1f > %.1f)!\nInvestigate ship %s (%s), a hit was registered on this ship outside this ship's radius.\n", hitpos_dist, ship_objp->radius * 2.0f, ship_p->ship_name, Ship_info[ship_p->ship_info_index].name ));
// Get John ASAP!!!! Someone passed a local coordinate instead of world for hitpos probably.
}
#endif
if (!global_damage) {
create_subsys_debris(ship_objp, hitpos);
}
// First, create a list of the N subsystems within range.
// Then, one at a time, process them in order.
int count = 0;
for ( subsys=GET_FIRST(&ship_p->subsys_list); subsys != END_OF_LIST(&ship_p->subsys_list); subsys = GET_NEXT(subsys) )
{
model_subsystem *mss = subsys->system_info;
//Deal with cheat correctly. If damage is the negative of the subsystem type, then we'll just kill the subsystem
//See process_debug_keys() in keycontrol.cpp for details.
if (damage < 0.0f) {
// single player or multiplayer
Assert(Player_ai->targeted_subsys != NULL);
if ( (subsys == Player_ai->targeted_subsys) && (subsys->current_hits > 0.0f) ) {
Assert(mss->type == (int) -damage);
if (!(subsys->flags[Ship::Subsystem_Flags::No_aggregate])) {
ship_p->subsys_info[mss->type].aggregate_current_hits -= subsys->current_hits;
if (ship_p->subsys_info[mss->type].aggregate_current_hits < 0.0f) {
ship_p->subsys_info[mss->type].aggregate_current_hits = 0.0f;
}
}
subsys->current_hits = 0.0f;
do_subobj_destroyed_stuff( ship_p, subsys, hitpos );
continue;
} else {
continue;
}
}
if (subsys->current_hits > 0.0f) {
float dist, range;
if (Fixed_turret_collisions && submodel_num != -1 && submodel_num == mss->turret_gun_sobj) {
// Special case:
// if the subsystem is a turret and the hit submodel is its barrel,
// get the distance between the hit and the turret barrel center
find_submodel_instance_world_point(&g_subobj_pos, ship_p->model_instance_num, submodel_num, &ship_objp->orient, &ship_objp->pos);
dist = vm_vec_dist_quick(&hitpos2, &g_subobj_pos);
// Damage attenuation range of barrel radius * 2 makes full damage
// be taken regardless of where the barrel is hit
range = submodel_get_radius(Ship_info[ship_p->ship_info_index].model_num, submodel_num) * 2;
} else {
// Default behavior:
// get the distance between the hit and the subsystem center
get_subsystem_world_pos(ship_objp, subsys, &g_subobj_pos);
dist = vm_vec_dist_quick(&hitpos2, &g_subobj_pos);
range = subsys_get_range(other_obj, subsys);
}
if ( dist < range) {
if (Damage_impacted_subsystem_first && submodel_num != -1 && (submodel_num == mss->subobj_num || submodel_num == mss->turret_gun_sobj)) {
// If the hit impacted this subsystem's submodel, then make sure this subsys
// gets dealt damage first, even if another subsystem is closer to the hit location
subsys_hit_first = count;
}
if (mss->flags[Model::Subsystem_Flags::Collide_submodel]) {
if (submodel_num != -1 && submodel_num != mss->subobj_num && submodel_num != mss->turret_gun_sobj) {
// If this subsystem only wants to take damage when its submodel receives
// a direct hit and the current hit did not do so, skip it.
continue;
}
}
subsys_list[count].dist = dist;
subsys_list[count].range = range;
subsys_list[count].ptr = subsys;
count++;
if (count >= MAX_SUBSYS_LIST){
break;
}
}
}
}
int dmg_type_idx = -1;
int parent_armor_flags = 0;
if(ship_p->armor_type_idx > -1)
parent_armor_flags = Armor_types[ship_p->armor_type_idx].flags;
if (other_obj)
{
if(other_obj->type == OBJ_SHOCKWAVE) {
dmg_type_idx = shockwave_get_damage_type_idx(other_obj->instance);
} else if(other_obj->type == OBJ_WEAPON) {
dmg_type_idx = Weapon_info[Weapons[other_obj->instance].weapon_info_index].damage_type_idx;
} else if(other_obj->type == OBJ_BEAM) {
dmg_type_idx = Weapon_info[beam_get_weapon_info_index(other_obj)].damage_type_idx;
} else if(other_obj->type == OBJ_ASTEROID) {
dmg_type_idx = Asteroid_info[Asteroids[other_obj->instance].asteroid_type].damage_type_idx;
} else if(other_obj->type == OBJ_DEBRIS) {
dmg_type_idx = Debris[other_obj->instance].damage_type_idx;
} else if(other_obj->type == OBJ_SHIP) {
dmg_type_idx = Ships[other_obj->instance].collision_damage_type_idx;
}
}
// Now scan the sorted list of subsystems in range.
// Apply damage to the nearest one first (exception: subsys_hit_first),
// subtracting off damage as we go.
int i, j;
for (j=0; j<count; j++)
{
float dist, range;
ship_subsys *subsystem;
int min_index = -1;
if (Damage_impacted_subsystem_first && subsys_hit_first > -1) {
min_index = subsys_hit_first;
subsys_hit_first = -1;
} else {
float min_dist = 9999999.9f;
for (i=0; i<count; i++) {
if (subsys_list[i].dist < min_dist) {
min_dist = subsys_list[i].dist;
min_index = i;
}
}
}
Assert(min_index != -1);
float damage_to_apply = 0.0f;
subsystem = subsys_list[min_index].ptr;
range = subsys_list[min_index].range;
dist = subsys_list[min_index].dist;
subsys_list[min_index].dist = 9999999.9f; // Make sure we don't use this one again.
Assert(range > 0.0f); // Goober5000 - avoid div-0 below
// only do this for the closest affected subsystem
if ( (j == 0) && (!(parent_armor_flags & SAF_IGNORE_SS_ARMOR))) {
if(subsystem->armor_type_idx > -1)
{
damage = Armor_types[subsystem->armor_type_idx].GetDamage(damage, dmg_type_idx, 1.0f); // Nuke: I don't think we need to apply damage sacaling to this one, using 1.0f
if(hull_should_apply_armor) {
*hull_should_apply_armor = false;
}
}
}
// HORRIBLE HACK!
// MK, 9/4/99
// When Helios bombs are dual fired against the Juggernaut in sm3-01 (FS2), they often
// miss their target. There is code dating to FS1 in the collision code to detect that a bomb or
// missile has somehow missed its target. It gets its lifeleft set to 0.1 and then it detonates.
// Unfortunately, the shockwave damage was cut by 4 above. So boost it back up here.
if ((weapon_info_index >= 0) && (dist < 10.0f) && ((other_obj) && (other_obj->type == OBJ_SHOCKWAVE))) { // Goober5000 check for NULL
damage_left *= 4.0f * Weapon_info[weapon_info_index].subsystem_factor;
damage_if_hull *= 4.0f * Weapon_info[weapon_info_index].armor_factor;
}
if ( dist < range/2.0f ) {
if (subsystem->flags[Ship::Subsystem_Flags::Damage_as_hull])
damage_to_apply = damage_if_hull;
else
damage_to_apply = damage_left;
} else if ( dist < range ) {
if (subsystem->flags[Ship::Subsystem_Flags::Damage_as_hull])
damage_to_apply = damage_if_hull * (1.0f - dist/range);
else
damage_to_apply = damage_left * (1.0f - dist/range);
}
// if we're not in CLIENT_NODAMAGE multiplayer mode (which is a the NEW way of doing things)
if ( (damage_to_apply > 0.1f) && !(MULTIPLAYER_CLIENT) )
{
// Decrease damage to subsystems to player ships.
if (ship_objp->flags[Object::Object_Flags::Player_ship]){
ss_dif_scale = The_mission.ai_profile->subsys_damage_scale[Game_skill_level];
}
// Goober5000 - subsys guardian
if (subsystem->subsys_guardian_threshold > 0)
{
float min_subsys_strength = 0.01f * subsystem->subsys_guardian_threshold * subsystem->max_hits;
if ( (subsystem->current_hits - (damage_to_apply * ss_dif_scale)) < min_subsys_strength ) {
// find damage needed to take object to min subsys strength
damage_to_apply = subsystem->current_hits - min_subsys_strength;
// make sure damage is positive
damage_to_apply = MAX(0, damage_to_apply);
}
}
// decrease the damage left to apply to the ship subsystems
// WMC - since armor aborbs damage, subtract the amount of damage before we apply armor
damage_left -= (damage_to_apply * ss_dif_scale);
// if this subsystem doesn't carry damage then subtract it off of our total return
if (subsystem->system_info->flags[Model::Subsystem_Flags::Carry_no_damage]) {
if ((other_obj->type != OBJ_SHOCKWAVE) || (!(subsystem->system_info->flags[Model::Subsystem_Flags::Carry_shockwave]))) {
float subsystem_factor = 0.0f;
if ((weapon_info_index >= 0) && ((other_obj->type == OBJ_WEAPON) || (other_obj->type == OBJ_SHOCKWAVE))) {
if (subsystem->flags[Ship::Subsystem_Flags::Damage_as_hull]) {
subsystem_factor = Weapon_info[weapon_info_index].armor_factor;
} else {
subsystem_factor = Weapon_info[weapon_info_index].subsystem_factor;
}
}
if (subsystem_factor > 0.0f) {
damage -= ((MIN(subsystem->current_hits, (damage_to_apply * ss_dif_scale))) / subsystem_factor);
} else {
damage -= MIN(subsystem->current_hits, (damage_to_apply * ss_dif_scale));
}
}
}
//Apply armor to damage
if (subsystem->armor_type_idx >= 0) {
// Nuke: this will finally factor it in to damage_to_apply and i wont need to factor it in anywhere after this
damage_to_apply = Armor_types[subsystem->armor_type_idx].GetDamage(damage_to_apply, dmg_type_idx, ss_dif_scale);
} else { // Nuke: no get damage call to apply difficulty scaling, so factor it in now
damage_to_apply *= ss_dif_scale;
}
subsystem->current_hits -= damage_to_apply;
if (!(subsystem->flags[Ship::Subsystem_Flags::No_aggregate])) {
ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits -= damage_to_apply;
}
if (subsystem->current_hits < 0.0f) {
damage_left -= subsystem->current_hits;
if (!(subsystem->flags[Ship::Subsystem_Flags::No_aggregate])) {
ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits -= subsystem->current_hits;
}
subsystem->current_hits = 0.0f; // set to 0 so repair on subsystem takes immediate effect
}
if ( ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits < 0.0f ){
ship_p->subsys_info[subsystem->system_info->type].aggregate_current_hits = 0.0f;
}
// multiplayer clients never blow up subobj stuff on their own
if ( (subsystem->current_hits <= 0.0f) && !MULTIPLAYER_CLIENT) {
do_subobj_destroyed_stuff( ship_p, subsystem, hitpos );
}
if (damage_left <= 0) { // no more damage to distribute, so stop checking
damage_left = 0.0f;
break;
}
}
}
if (damage < 0.0f) {
damage = 0.0f;
}
// Note: I changed this to return damage_left and it completely screwed up balance.
// It had taken a few MX-50s to destory an Anubis (with 40% hull), then it took maybe ten.
// So, I left it alone. -- MK, 4/15/98
return damage;
}
// Store who/what killed the player, so we can tell the player how he died
void shiphit_record_player_killer(object *killer_objp, player *p)
{
switch (killer_objp->type) {
case OBJ_WEAPON:
p->killer_objtype=OBJ_WEAPON;
p->killer_weapon_index=Weapons[killer_objp->instance].weapon_info_index;
p->killer_species = Ship_info[Ships[Objects[killer_objp->parent].instance].ship_info_index].species;
if ( &Objects[killer_objp->parent] == Player_obj ) {
// killed by a missile?
if(Weapon_info[p->killer_weapon_index].subtype == WP_MISSILE){
p->flags |= PLAYER_FLAGS_KILLED_SELF_MISSILES;
} else {
p->flags |= PLAYER_FLAGS_KILLED_SELF_UNKNOWN;
}
}
// in multiplayer, record callsign of killer if killed by another player
if ( (Game_mode & GM_MULTIPLAYER) && ( Objects[killer_objp->parent].flags[Object::Object_Flags::Player_ship]) ) {
int pnum;
pnum = multi_find_player_by_object( &Objects[killer_objp->parent] );
if ( pnum != -1 ) {
strcpy_s(p->killer_parent_name, Net_players[pnum].m_player->callsign);
} else {
nprintf(("Network", "Couldn't find player object of weapon for killer of %s\n", p->callsign));
}
} else {
strcpy_s(p->killer_parent_name, Ships[Objects[killer_objp->parent].instance].ship_name);
end_string_at_first_hash_symbol(p->killer_parent_name);
}
break;
case OBJ_SHOCKWAVE:
p->killer_objtype=OBJ_SHOCKWAVE;
p->killer_weapon_index = shockwave_get_weapon_index(killer_objp->instance);
p->killer_species = Ship_info[Ships[Objects[killer_objp->parent].instance].ship_info_index].species;
if ( &Objects[killer_objp->parent] == Player_obj ) {
p->flags |= PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE;
}
if ( (Game_mode & GM_MULTIPLAYER) && ( Objects[killer_objp->parent].flags[Object::Object_Flags::Player_ship]) ) {
int pnum;
pnum = multi_find_player_by_object( &Objects[killer_objp->parent] );
if ( pnum != -1 ) {
strcpy_s(p->killer_parent_name, Net_players[pnum].m_player->callsign);
} else {
nprintf(("Network", "Couldn't find player object of shockwave for killer of %s\n", p->callsign));
}
} else {
strcpy_s(p->killer_parent_name, Ships[Objects[killer_objp->parent].instance].ship_name);
end_string_at_first_hash_symbol(p->killer_parent_name);
}
break;
case OBJ_SHIP:
p->killer_objtype=OBJ_SHIP;
p->killer_weapon_index=-1;
p->killer_species = Ship_info[Ships[killer_objp->instance].ship_info_index].species;
if ( Ships[killer_objp->instance].flags[Ship::Ship_Flags::Exploded] ) {
p->flags |= PLAYER_FLAGS_KILLED_BY_EXPLOSION;
}
if ( Ships[Objects[p->objnum].instance].wash_killed ) {
p->flags |= PLAYER_FLAGS_KILLED_BY_ENGINE_WASH;
}
// in multiplayer, record callsign of killer if killed by another player
if ( (Game_mode & GM_MULTIPLAYER) && (killer_objp->flags[Object::Object_Flags::Player_ship]) ) {
int pnum;
pnum = multi_find_player_by_object( killer_objp );
if ( pnum != -1 ) {
strcpy_s(p->killer_parent_name, Net_players[pnum].m_player->callsign);
} else {
nprintf(("Network", "Couldn't find player object for killer of %s\n", p->callsign));
}
} else {
strcpy_s(p->killer_parent_name, Ships[killer_objp->instance].ship_name);
end_string_at_first_hash_symbol(p->killer_parent_name);
}
break;
case OBJ_DEBRIS:
case OBJ_ASTEROID:
if ( killer_objp->type == OBJ_DEBRIS ) {
p->killer_objtype = OBJ_DEBRIS;
} else {
p->killer_objtype = OBJ_ASTEROID;
}
p->killer_weapon_index=-1;
p->killer_species = -1;
p->killer_parent_name[0] = '\0';
break;
case OBJ_BEAM:
int beam_obj;
beam_obj = beam_get_parent(killer_objp);
p->killer_species = -1;
p->killer_objtype = OBJ_BEAM;
if(beam_obj != -1){
if((Objects[beam_obj].type == OBJ_SHIP) && (Objects[beam_obj].instance >= 0)){
strcpy_s(p->killer_parent_name, Ships[Objects[beam_obj].instance].ship_name);
end_string_at_first_hash_symbol(p->killer_parent_name);
}
p->killer_species = Ship_info[Ships[Objects[beam_obj].instance].ship_info_index].species;
} else {
strcpy_s(p->killer_parent_name, "");
}
break;
case OBJ_NONE:
if ( Game_mode & GM_MULTIPLAYER ) {
Int3();
}
p->killer_objtype=-1;
p->killer_weapon_index=-1;
p->killer_parent_name[0]=0;
p->killer_species = -1;
break;
default:
Int3();
break;
}
}
// Say dead stuff.
void show_dead_message(object *ship_objp, object *other_obj)
{
player *player_p;
// not doing anything when a non player dies.
if ( !(ship_objp->flags[Object::Object_Flags::Player_ship]) ){
return;
}
if(other_obj == NULL){
return;
}
// Get a pointer to the player (we are assured a player ship was killed)
if ( Game_mode & GM_NORMAL ) {
player_p = Player;
} else {
// in multiplayer, get a pointer to the player that died.
int pnum = multi_find_player_by_object( ship_objp );
if ( pnum == -1 ) {
return;
}
player_p = Net_players[pnum].m_player;
}
// multiplayer clients should already have this information.
if ( !MULTIPLAYER_CLIENT ){
shiphit_record_player_killer( other_obj, player_p );
}
// display a hud message is the guy killed isn't me (multiplayer only)
/*
if ( (Game_mode & GM_MULTIPLAYER) && (ship_obj != Player_obj) ) {
char death_text[256];
player_generate_death_text( player_p, death_text );
HUD_sourced_printf(HUD_SOURCE_HIDDEN, death_text);
}
*/
}
/* JAS: THIS DOESN'T SEEM TO BE USED, SO I COMMENTED IT OUT
// Apply damage to a ship, destroying if necessary, etc.
// Returns portion of damage that exceeds ship shields, ie the "unused" portion of the damage.
// Note: This system does not use the mesh shield. It applies damage to the overall ship shield.
float apply_damage_to_ship(object *objp, float damage)
{
float _ss;
add_shield_strength(objp, -damage);
// check if shields are below 0%, if so take leftover damage and apply to ship integrity
if ((_ss = get_shield_strength(objp)) < 0.0f ) {
damage = -_ss;
set_shield_strength(objp, 0.0f);
} else
damage = 0.0f;
return damage;
}
*/
// Do music processing for a ship hit.
void ship_hit_music(object *ship_objp, object *other_obj)
{
Assert(ship_objp); // Goober5000
Assert(other_obj); // Goober5000
ship* ship_p = &Ships[ship_objp->instance];
object *parent;
// Switch to battle track when a ship is hit by fire
//
// If the ship hit has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive
// ship, so don't start the battle music
if (!stricmp(Ai_class_names[Ai_info[ship_p->ai_index].ai_class], NOX("none")))
return;
// Only start if ship hit and firing ship are from different teams
int attackee_team, attacker_team;
attackee_team = Ships[ship_objp->instance].team;
// avoid uninitialized value by matching them
attacker_team = attackee_team;
switch ( other_obj->type )