forked from ReactiveDrop/reactivedrop_public_src
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathasw_marine_shared.cpp
More file actions
2885 lines (2486 loc) · 84.6 KB
/
Copy pathasw_marine_shared.cpp
File metadata and controls
2885 lines (2486 loc) · 84.6 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
#include "cbase.h"
#ifdef CLIENT_DLL
#include "c_asw_marine.h"
#include "c_asw_weapon.h"
#include "c_asw_player.h"
#include "c_asw_marine_resource.h"
#include "iasw_client_vehicle.h"
#include "c_te_effect_dispatch.h"
#include "c_asw_simple_alien.h"
#include "c_asw_fx.h"
#include "c_asw_pickup_weapon.h"
#include "c_asw_shieldbug.h"
#include "c_asw_hack.h"
#include "c_physics_prop_statue.h"
#include "prediction.h"
#define CASW_Simple_Alien C_ASW_Simple_Alien
#define CASW_Pickup_Weapon C_ASW_Pickup_Weapon
#define CAI_BaseNPC C_AI_BaseNPC
#define CStatueProp C_StatueProp
#else
#include "te_effect_dispatch.h"
#include "asw_marine.h"
#include "asw_weapon.h"
#include "asw_player.h"
#include "asw_marine_resource.h"
#include "iasw_vehicle.h"
#include "iservervehicle.h"
#include "asw_simple_alien.h"
#include "asw_pickup_weapon.h"
#include "asw_alien.h"
#include "asw_hack.h"
#include "physics_prop_statue.h"
#include "asw_util_shared.h"
#endif
#include "game_timescale_shared.h"
#include "asw_marine_gamemovement.h"
#include "asw_trace_filter_melee.h"
#include "asw_gamerules.h"
#include "ai_debug_shared.h"
#include "takedamageinfo.h"
#include "decals.h"
#include "shot_manipulator.h"
#include "effect_dispatch_data.h"
#include "asw_marine_profile.h"
#include "asw_remote_turret_shared.h"
#include "obstacle_pushaway.h"
#include "bone_setup.h"
#include "ammodef.h"
#include "asw_equipment_list.h"
#include "asw_weapon_parse.h"
#include "fmtstr.h"
#include "collisionutils.h"
#include "coordsize.h"
#include "asw_melee_system.h"
#include "eventlist.h"
#include "particle_parse.h"
#include "asw_trace_filter_shot.h"
#include "asw_deathmatch_mode.h"
#include "asw_weapon_sniper_rifle.h"
#include "asw_fx_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// the amount of damage required to make an alien always gib
#define ASW_GIB_DAMAGE_LIMIT 200
// knockout and reviving
#define MAX_USERMESSAGE_RATE 0.5f;
#define ASW_MARINE_REVIVE_RADIUS 100.0f
extern ConVar ai_debug_shoot_positions;
extern ConVar asw_melee_debug;
extern ConVar asw_debug_marine_damage;
extern ConVar asw_stim_time_scale;
extern ConVar asw_marine_ff;
extern ConVar asw_fist_finisher_damage_scale;
ConVar asw_leadership_radius("asw_leadership_radius", "600", FCVAR_REPLICATED | FCVAR_CHEAT, "Radius of the leadership field around NCOs with the leadership skill");
ConVar asw_marine_speed_scale_easy("asw_marine_speed_scale_easy", "0.96", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_speed_scale_normal("asw_marine_speed_scale_normal", "1.0", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_speed_scale_hard("asw_marine_speed_scale_hard", "1.024", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_speed_scale_insane("asw_marine_speed_scale_insane", "1.048", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_speed_scale_adrenaline( "asw_marine_speed_scale_adrenaline", "1.0", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_box_collision("asw_marine_box_collision", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_gun_offset_x( "asw_marine_gun_offset_x", "25", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_gun_offset_y( "asw_marine_gun_offset_y", "4", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar asw_marine_gun_offset_z( "asw_marine_gun_offset_z", "34", FCVAR_REPLICATED | FCVAR_CHEAT );
// reactivedrop: setting to 0, this prevents killing shieldbug from front using shotguns
ConVar asw_allow_hull_shots("asw_allow_hull_shots", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar rd_difficulty_tier( "rd_difficulty_tier", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "Used to make difficulties higher than Brutal. 0 - default difficulties, 1 - Easy is as hard as Brutal + 1, 2 - Easy is as hard as Brutal + 6" );
ConVar sv_showimpacts( "sv_showimpacts", "0", FCVAR_REPLICATED, "Shows client (red) and server (blue) bullet impact point (1=both, 2=client-only, 3=server-only)" );
ConVar rd_shoot_from_face( "rd_shoot_from_face", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "In first person, the gun fires out of our eyes instead of from where the gun is being held." );
#ifdef GAME_DLL
extern ConVar ai_show_hull_attacks;
ConVar asw_melee_knockback_up_force( "asw_melee_knockback_up_force", "1.0", FCVAR_CHEAT );
ConVar rd_explosive_shotgun_pellets( "rd_explosive_shotgun_pellets", "0", FCVAR_CHEAT );
ConVar rd_explosive_vindicator_pellets( "rd_explosive_vindicator_pellets", "0", FCVAR_CHEAT );
ConVar rd_explosive_minigun_bullets( "rd_explosive_minigun_bullets", "0", FCVAR_CHEAT );
ConVar rd_explosive_autogun_bullets( "rd_explosive_autogun_bullets", "0", FCVAR_CHEAT );
ConVar rd_explosive_railgun_bullets( "rd_explosive_railgun_bullets", "0", FCVAR_CHEAT);
ConVar rd_explosive_bullets( "rd_explosive_bullets", "0", FCVAR_CHEAT);
ConVar rd_explosive_bullets_dmg( "rd_explosive_bullets_dmg", "50", FCVAR_CHEAT);
ConVar rd_explosive_bullets_radius( "rd_explosive_bullets_radius", "200", FCVAR_CHEAT);
#endif
#ifdef CLIENT_DLL
extern ConVar rd_sniper_scope_weapon_switch;
#endif
static const float ASW_MARINE_MELEE_HULL_TRACE_Z = 32.0f;
bool CASW_Marine::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex /*=0*/ )
{
if ( GetHealth() <= 0 )
return false;
// check we're actually carrying this weapon in one of our 3 marine slots
CASW_Weapon *pWeaponPri = GetASWWeapon( 0 );
CASW_Weapon *pWeaponSec = GetASWWeapon( 1 );
CASW_Weapon *pWeaponExt = GetASWWeapon( 2 );
CASW_Weapon *pWeaponTmp = GetASWWeapon( ASW_TEMPORARY_WEAPON_SLOT );
if ( pWeaponPri != pWeapon && pWeaponSec != pWeapon && pWeaponExt != pWeapon && pWeaponTmp != pWeapon )
return false;
if ( pWeapon == pWeaponTmp )
{
m_bLastWeaponBeforeTempWasSecondary = GetActiveASWWeapon() == pWeaponSec;
}
#ifdef CLIENT_DLL
CreateBackpack( GetActiveASWWeapon() );
#endif
//Orange. BaseClass::Weapon_Switch() overrides m_flNextPrimaryAttack of the pWeapon later in CBaseCombatWeapon::DefaultDeploy()
//So we have to calc delay before it and restore with ApplyWeaponSwitchTime().
//This way we respect switch time and used gun firerate (unlike it taken before from gun in another slot)
#define ASW_WEAPON_SWITCH_TIME 0.5f
float fSwitchDelay = ASW_WEAPON_SWITCH_TIME;
if ( pWeapon->m_flNextPrimaryAttack > gpGlobals->curtime )
{
fSwitchDelay = MIN( pWeapon->GetFireRate(), pWeapon->m_flNextPrimaryAttack.Get() - gpGlobals->curtime );
fSwitchDelay = MAX( ASW_WEAPON_SWITCH_TIME, fSwitchDelay );
}
if ( BaseClass::Weapon_Switch( pWeapon, viewmodelindex ) )
{
CBaseCombatWeapon *pLast = GetLastWeaponSwitchedTo();
if ( pWeapon != pLast && ASWGameRules() && ASWGameRules()->GetGameState() >= ASW_GS_INGAME )
{
m_hLastWeaponSwitchedTo = pWeapon;
DoAnimationEvent( PLAYERANIMEVENT_WEAPON_SWITCH );
}
CASW_Weapon *pASWWeapon = assert_cast<CASW_Weapon *>( pWeapon );
if ( pASWWeapon )
{
pASWWeapon->ApplyWeaponSwitchTime( fSwitchDelay );
if ( pASWWeapon->Classify() == CLASS_ASW_SNIPER_RIFLE )
{
CASW_Weapon_Sniper_Rifle* pSniper = assert_cast<CASW_Weapon_Sniper_Rifle*>( pWeapon );
if ( pSniper->IsZoomed() && this->IsInhabited() )
{
#ifdef CLIENT_DLL
if ( !rd_sniper_scope_weapon_switch.GetBool() )
{
pSniper->m_bZoomed.Set( false );
}
#else
CASW_Player* pASWPlayer = GetCommander();
if ( pASWPlayer )
{
const char* pszZoomSwitch = engine->GetClientConVarValue( pASWPlayer->entindex(), "rd_sniper_scope_weapon_switch" );
if ( pszZoomSwitch )
{
int value = atoi( pszZoomSwitch );
if ( value == 0 )
{
pSniper->m_bZoomed.Set( false );
}
}
}
#endif
}
}
}
#ifndef CLIENT_DLL
CheckAndRequestAmmo();
if ( pWeaponTmp && pWeapon != pWeaponTmp )
{
DropWeapon( ASW_TEMPORARY_WEAPON_SLOT, true );
}
#endif
return true;
}
return false;
}
// allow switching to weapons with no ammo
bool CASW_Marine::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
{
//if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) )
//return false;
CASW_Weapon* pASWWeapon = dynamic_cast<CASW_Weapon*>(pWeapon);
if ( !pASWWeapon || !pASWWeapon->ASWCanBeSelected() )
return false;
// disallow selection of offhand item
const CASW_EquipItem *pItem = pASWWeapon->GetEquipItem();
if ( pItem && pItem->m_bIsExtra )
return false;
if ( !pWeapon->CanDeploy() )
return false;
if ( GetActiveWeapon() )
{
if ( !GetActiveWeapon()->CanHolster() )
return false;
}
return true;
}
const QAngle& CASW_Marine::ASWEyeAngles( void )
{
// if we're driving, return the angle
if ( IsInVehicle() && GetASWVehicle() )
{
Vector origin;
GetASWVehicle()->ASWGetSeatPosition( m_iVehicleSeat, origin, m_AIEyeAngles );
return m_AIEyeAngles;
}
if ( GetCommander() && IsInhabited() )
return GetCommander()->EyeAngles();
#ifdef CLIENT_DLL
m_AIEyeAngles = EyeAngles();
m_AIEyeAngles[PITCH] = m_fAIPitch;
return m_AIEyeAngles;
#else
return EyeAngles();
#endif
}
bool CASW_Marine::IsInfested()
{
if (!GetMarineResource())
return false;
return GetMarineResource()->IsInfested();
}
// tick emotes, if server sets any emote bool to true, it causes the emote timer
// to go to 2.0 on all machines and tick down to zero (whereupon it gets set to false again)
void CASW_Marine::TickEmotes( float d )
{
TickEmote( d, 1 << 0, m_fEmoteMedicTime );
TickEmote( d, 1 << 1, m_fEmoteAmmoTime );
TickEmote( d, 1 << 2, m_fEmoteSmileTime );
TickEmote( d, 1 << 3, m_fEmoteStopTime );
TickEmote( d, 1 << 4, m_fEmoteGoTime );
TickEmote( d, 1 << 5, m_fEmoteExclaimTime );
TickEmote( d, 1 << 6, m_fEmoteAnimeSmileTime );
TickEmote( d, 1 << 7, m_fEmoteQuestionTime );
TickEmote( d, 1 << 14, m_fEmoteThanksTime );
TickEmote( d, 1 << 15, m_fEmoteWeldTime );
}
bool CASW_Marine::TickEmote( float d, int bit, float &fEmoteTime )
{
if ( ( ( m_iEmote ^ m_iClientEmote ) & bit ) != 0 )
{
m_iClientEmote ^= bit;
if ( m_iEmote & bit ) // started an emote
fEmoteTime = 2.0;
}
// if we're doing an emote, tick down the emote timer
if ( ( m_iEmote & bit ) != 0 )
{
fEmoteTime -= d * 2;
if ( fEmoteTime <= 0 )
m_iEmote &= ~bit;
}
return ( m_iEmote & bit ) != 0;
}
// asw fixme to be + eye height (crouch/no)
Vector CASW_Marine::EyePosition( void )
{
#ifdef CLIENT_DLL
//if (m_bUseLastRenderedEyePosition)
//return m_vecLastRenderedPos + GetViewOffset();
return GetRenderOrigin() + GetViewOffset();
#else
return GetAbsOrigin() + GetViewOffset();
#endif
}
void CASW_Marine::AvoidPhysicsProps( CUserCmd *pCmd )
{
#ifndef _XBOX
// Don't avoid if noclipping or in movetype none
switch ( GetMoveType() )
{
case MOVETYPE_NOCLIP:
case MOVETYPE_NONE:
case MOVETYPE_OBSERVER:
return;
default:
break;
}
AvoidPushawayProps( this, pCmd );
#endif
}
Vector CASW_Marine::Weapon_ShootPosition( )
{
if ( rd_shoot_from_face.GetBool() && IsInhabited() && GetASWControls() == ASWC_FIRSTPERSON && ( !GetActiveASWWeapon() || GetActiveASWWeapon()->ShouldFireFromCameraInFirstPerson() ) )
{
return EyePosition();
}
Vector forward, right, up, v;
v = GetAbsOrigin();
QAngle ang = ASWEyeAngles();
ang.x = 0; // clear out pitch, so we're matching the fixes point of our autoaim calcs
AngleVectors( ang, &forward, &right, &up );
v = v + up * asw_marine_gun_offset_z.GetFloat();
Vector vecSrc = v
+ forward * asw_marine_gun_offset_x.GetFloat()
+ right * asw_marine_gun_offset_y.GetFloat();
trace_t tr;
UTIL_TraceLine(v, vecSrc, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction < 1.0f)
return tr.endpos;
return vecSrc;
}
float CASW_Marine::MaxSpeed()
{
float speed = MarineSkills()->GetSkillBasedValueByMarine( this, ASW_MARINE_SKILL_AGILITY, ASW_MARINE_SUBSKILL_AGILITY_MOVE_SPEED ) * m_fSpeedScale;
float speedscale = 1.0f;
// half speed if we're firing or reloading
if (GetActiveASWWeapon())
speedscale = GetActiveASWWeapon()->GetMovementScale();
// adjust the speed by difficulty level
extern ConVar rd_difficulty_tier;
if ( rd_difficulty_tier.GetInt() == 0 )
{
switch ( ASWGameRules()->GetSkillLevel() )
{
case 5: speedscale *= asw_marine_speed_scale_insane.GetFloat(); break;
case 4: speedscale *= asw_marine_speed_scale_insane.GetFloat(); break;
case 3: speedscale *= asw_marine_speed_scale_hard.GetFloat(); break;
case 2: speedscale *= asw_marine_speed_scale_normal.GetFloat(); break;
default: speedscale *= asw_marine_speed_scale_easy.GetFloat(); break;
}
}
else
{
speedscale *= asw_marine_speed_scale_insane.GetFloat();
}
// if we're an AI following someone, boost our speed as we get far away.
// it's a quadratic interpolation from 110% speed to 175% speed mapped
// to a distance of 20 units to 1800 units
CBaseEntity *pSquadLeader = GetSquadLeader();
// reactivedrop: we don't want AI marines to run faster in PvP
if (!IsInhabited() && pSquadLeader && !ASWDeathmatchMode() )
{
float distSq = GetAbsOrigin().DistToSqr(pSquadLeader->GetAbsOrigin());
float t = (distSq - (20*20)) / (1800*1800);
float q = ( 1.750f - 1.100f ) * t + 1.100f;
speedscale *= clamp<float>( q, 1.000f, 1.750f );
}
// bots don't use the input code, so we scale their movement speed here.
if ( !IsInhabited() && ( m_bWalking || m_bForceWalking ) )
{
return 180.0f;
}
// here's where we account for increased speed via powerup
if ( m_iPowerupType == POWERUP_TYPE_INCREASED_SPEED )
speedscale *= 1.75;
// speed up as time slows down
float flTimeDifference = 0.0f;
if ( gpGlobals->curtime < ASWGameRules()->GetStimEndTime() + 1.5f && asw_stim_time_scale.GetFloat() != 1.0f )
{
flTimeDifference = MAX( 0.0f, ( 1.0f - GameTimescale()->GetCurrentTimescale() ) / ( 1.0f - asw_stim_time_scale.GetFloat() ) );
}
if ( flTimeDifference > 0.0f )
{
speedscale *= 1.0f + flTimeDifference * ( asw_marine_speed_scale_adrenaline.GetFloat() - 1.0f );
}
return speed * speedscale;
}
// ammo in weapons this marine is carrying
int CASW_Marine::GetWeaponAmmoCount( int iAmmoIndex )
{
// find how much is in guns we're carrying
int iWeapons = ASW_MAX_MARINE_WEAPONS;
int iWeaponAmmo = 0;
for (int i=0;i<iWeapons;i++)
{
CASW_Weapon* pWeapon = GetASWWeapon(i);
if (pWeapon && pWeapon->GetPrimaryAmmoType() == iAmmoIndex)
iWeaponAmmo += pWeapon->Clip1();
if (pWeapon && pWeapon->GetSecondaryAmmoType() == iAmmoIndex)
iWeaponAmmo += pWeapon->Clip2();
}
return iWeaponAmmo;
}
// include ammo in weapons this marine is carrying
int CASW_Marine::GetWeaponAmmoCount( char *szName )
{
return GetWeaponAmmoCount( GetAmmoDef()->Index(szName) );
}
// include ammo in weapons this marine is carrying
int CASW_Marine::GetTotalAmmoCount( int iAmmoIndex )
{
// find how much is in held ammo
int iSpareAmmo = GetAmmoCount(iAmmoIndex);
// find how much is in guns we're carrying
int iWeaponAmmo = GetWeaponAmmoCount(iAmmoIndex);;
return iSpareAmmo + iWeaponAmmo;
}
int CASW_Marine::GetAllAmmoCount( void )
{
int nCount = 0;
for ( int i = 0; i < MAX_AMMO_TYPES; ++i )
{
nCount += GetTotalAmmoCount( i );
}
return nCount;
}
// include ammo in weapons this marine is carrying
int CASW_Marine::GetTotalAmmoCount( char *szName )
{
return GetTotalAmmoCount( GetAmmoDef()->Index(szName) );
}
// controlling a turret
bool CASW_Marine::IsControllingTurret()
{
return (m_hRemoteTurret.Get()!=NULL);
}
CASW_Remote_Turret* CASW_Marine::GetRemoteTurret()
{
return m_hRemoteTurret.Get();
}
CBaseCombatWeapon *CASW_Marine::ASWAnim_GetActiveWeapon()
{
return GetActiveWeapon();
}
bool CASW_Marine::ASWAnim_CanMove()
{
return true;
}
bool CASW_Marine::IsInhabited()
{
if ( !GetMarineResource() )
{
// NPC-marine spawned by a mapper rather than a squad-marine.
return m_bInhabited;
}
return GetMarineResource()->IsInhabited();
}
CASW_Marine_Profile *CASW_Marine::GetMarineProfile()
{
CASW_Marine_Resource *pMR = GetMarineResource();
if ( !pMR )
{
return m_nMarineProfile != -1 ? MarineProfileList()->GetProfile( m_nMarineProfile ) : NULL;
}
return pMR->GetProfile();
}
void CASW_Marine::DoDamagePowerupEffects( CBaseEntity *pTarget, CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
#ifdef GAME_DLL
m_iDamageAttributeEffects = 0;
if ( m_iPowerupType < 0 )
return;
switch ( m_iPowerupType )
{
case POWERUP_TYPE_FREEZE_BULLETS:
m_iDamageAttributeEffects |= BULLET_ATT_FREEZE;
//EmitSound( "ASW_Weapon_Crit.Freeze" );
break;
case POWERUP_TYPE_FIRE_BULLETS:
m_iDamageAttributeEffects |= BULLET_ATT_FIRE;
//EmitSound( "ASW_Weapon_Crit.Flame" );
break;
//case POWERUP_TYPE_CHEMICAL_BULLETS:
// m_iDamageAttributeEffects |= BULLET_ATT_CHEMICAL;
// EmitSound( "ASW_Weapon_Crit.Normal" );
// break;
//case POWERUP_TYPE_EXPLOSIVE_BULLETS:
// m_iDamageAttributeEffects |= BULLET_ATT_EXPLODE;
// EmitSound( "ASW_Weapon_Crit.Normal" );
// break;
case POWERUP_TYPE_ELECTRIC_BULLETS:
m_iDamageAttributeEffects |= BULLET_ATT_ELECTRIC;
//EmitSound( "ASW_Weapon_Crit.Electric" );
break;
default:
break;
}
if ( m_iDamageAttributeEffects <= 0 || !GetMarineProfile() )
return;
// if we have a bullet-based powerup,
// check if the weapon that has it is currently wielded
CASW_Weapon* pWeapon = GetActiveASWWeapon();
if ( !pWeapon || !pWeapon->m_bPoweredUp )
return;
info.ScaleDamage( 1.5f );
if ( m_iDamageAttributeEffects & BULLET_ATT_EXPLODE )
{
CBaseEntity *pHelpHelpImBeingSupressed = (CBaseEntity*) te->GetSuppressHost();
te->SetSuppressHost( NULL );
int iExplosionDamage = 16;
float flExplosionRadius = 40;
Vector vecExplosionPos = info.GetDamagePosition();
CPASFilter filter( vecExplosionPos );
int iFlags = TE_EXPLFLAG_SCALEPARTICLES;
iFlags |= TE_EXPLFLAG_NOSOUND;
if ( gpGlobals->curtime < m_flLastAttributeExplosionSound + 0.5f )
{
// no sound
}
else
{
m_flLastAttributeExplosionSound = gpGlobals->curtime;
CBaseEntity::EmitSound( filter, 0, "ASWGrenadeAmmo.Explode", &vecExplosionPos );
}
DispatchParticleEffect( "explosion_air_small", vecExplosionPos, vec3_angle );
#ifndef CLIENT_DLL
DevMsg("CASW_Marine::DoDamagePowerupEffects()\n");
#endif
RadiusDamage( CTakeDamageInfo( this, this, iExplosionDamage, DMG_BLAST ), vecExplosionPos, flExplosionRadius, CLASS_NONE, NULL );
te->SetSuppressHost( pHelpHelpImBeingSupressed );
}
if ( m_iDamageAttributeEffects & BULLET_ATT_FIRE )
{
IASW_Spawnable_NPC *pSpawnableNPC = dynamic_cast<IASW_Spawnable_NPC*>( pTarget );
if ( pSpawnableNPC )
{
pSpawnableNPC->ASW_Ignite( 2.0f, 0, info.GetAttacker(), info.GetWeapon(), info.GetInflictor() );
}
}
if ( m_iDamageAttributeEffects & BULLET_ATT_FREEZE )
{
if ( pTarget )
{
CAI_BaseNPC *pNPC = pTarget->MyNPCPointer();
if ( pNPC )
{
pNPC->Freeze( 3.0f, this );
}
}
}
/*
if ( m_iDamageAttributeEffects & BULLET_ATT_CHEMICAL )
{
CASW_Weapon_EffectVolume *pRad = (CASW_Weapon_EffectVolume*) CreateEntityByName("asw_weapon_effect_volume");
if (pRad)
{
pRad->SetAbsOrigin( info.GetDamagePosition() );
pRad->SetOwnerEntity( this );
pRad->SetOwnerWeapon( GetActiveASWWeapon() );
pRad->SetEffectFlag( ASW_WEV_RADIATION );
pRad->SetDuration( 3.0f );
pRad->Spawn();
// also spawn our big cloud marking out the area of radiation
CASW_Emitter *pEmitter = (CASW_Emitter*) CreateEntityByName("asw_emitter");
if ( pEmitter )
{
pEmitter->UseTemplate("radcloud1");
pEmitter->SetAbsOrigin( info.GetDamagePosition() );
pEmitter->Spawn();
pEmitter->SetDieTime( gpGlobals->curtime + 3.0f );
}
// play the geiger sound for 10 seconds
const char *pszSound = "Misc.Geiger";
float fRadPitch = random->RandomInt( 95, 105 );
CPASAttenuationFilter filter( this );
CSoundPatch *pPatch = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), CHAN_STATIC, pszSound, ATTN_NORM );
CSoundEnvelopeController::GetController().Play( pPatch, 1.0, fRadPitch );
CSoundEnvelopeController::GetController().CommandAdd( pPatch, 1.0f, SOUNDCTRL_CHANGE_VOLUME, 1.0f, 0.0f );
CSoundEnvelopeController::GetController().CommandAdd( pPatch, 2.0f, SOUNDCTRL_DESTROY, 0.0f, 0.0f );
}
}
*/
if ( m_iDamageAttributeEffects & BULLET_ATT_ELECTRIC )
{
int damageType = info.GetDamageType();
damageType |= DMG_SHOCK;
info.SetDamageType( damageType );
// search other enemies near this one
int iMaxArcs = 1; // TODO: max arc should be attribute of the weapon? if we can combine two attribs into one somehow
CUtlVector<CBaseEntity*> shocked;
int iNumShocked = 0; // TODO: Use size of shocked vector for this?
shocked.AddToTail( pTarget );
float flArcDistSqr = 300.0f * 300.0f;
float flShockDamage = info.GetDamage(); // TODO: base on attribute?
float flDamageReduction = flShockDamage / (float)iMaxArcs; // how much damage each arc loses
Vector vecShockSrc = info.GetDamagePosition();
CBaseEntity *pLastShocked = pTarget;
pLastShocked->EmitSound( "Electricity.Zap" );
if ( pTarget->IsAlienClassType() )
{
CASW_Alien* pAlien = assert_cast<CASW_Alien*>(pTarget);
pAlien->ForceFlinch(vecShockSrc);
}
for ( int k = 0; k < iMaxArcs; k++ )
{
CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs();
int nAIs = g_AI_Manager.NumAIs();
bool bShocked = false;
for ( int i = 0; i < nAIs; i++ )
{
if ( shocked.Find( ppAIs[i] ) != shocked.InvalidIndex() ) // already shocked this guy
continue;
if ( IRelationType( ppAIs[i] ) >= D_LI ) // friendly
continue;
if ( ppAIs[i]->GetAbsOrigin().DistToSqr( vecShockSrc ) > flArcDistSqr ) // too far away
continue;
// trace from the last shock position to this guy
trace_t shockTR;
Vector vecAIPos = ppAIs[i]->WorldSpaceCenter();
CASWTraceFilterShot traceFilter( this, pLastShocked, COLLISION_GROUP_NONE, vecAIPos - vecShockSrc );
AI_TraceLine( vecShockSrc, vecAIPos, MASK_SHOT, &traceFilter, &shockTR );
if ( shockTR.fraction != 1.0 && shockTR.m_pEnt )
{
if ( IRelationType( shockTR.m_pEnt ) >= D_LI ) // friendly
continue;
// shock this thing
vecAIPos = shockTR.endpos;
CTakeDamageInfo shockDmgInfo( this, this, flShockDamage, DMG_SHOCK );
Vector vecShockDir = vecAIPos - vecShockSrc;
VectorNormalize( vecShockDir );
shockDmgInfo.SetDamagePosition( shockTR.endpos );
shockDmgInfo.SetDamageForce( vecShockDir );
shockDmgInfo.ScaleDamageForce( 1.0 );
shockDmgInfo.SetAmmoType( info.GetAmmoType() );
shockTR.m_pEnt->DispatchTraceAttack( shockDmgInfo, vecShockDir, &shockTR );
if ( shockTR.m_pEnt->IsAlienClassType() )
{
CASW_Alien *pAlien = assert_cast<CASW_Alien*>(shockTR.m_pEnt);
pAlien->ForceFlinch(shockTR.endpos);
}
// spawn a shock effect
// spawn a shock effect
CRecipientFilter filter;
filter.AddAllPlayers();
UserMessageBegin( filter, "ASWEnemyTeslaGunArcShock" );
WRITE_SHORT( shockTR.m_pEnt->entindex() );
WRITE_SHORT( pLastShocked->entindex() );
MessageEnd();
vecShockSrc = vecAIPos;
pLastShocked = shockTR.m_pEnt;
bShocked = true;
iNumShocked++;
// reduce shock damage as energy arcs
flShockDamage = MAX( flShockDamage - flDamageReduction, 0.0f );
break;
}
}
if ( !bShocked )
break;
}
if ( iNumShocked > 0 )
{
// Return reduced shock damage if we arced
info.SetDamage( flShockDamage );
}
}
#endif //GAME_DLL
}
void CASW_Marine::FireBullets( const FireBulletsInfo_t &info )
{
float fPiercingChance = MarineSkills()->GetSkillBasedValueByMarine( this, ASW_MARINE_SKILL_STOPPING_POWER, ASW_MARINE_SUBSKILL_PIERCING_CHANCE );
if ( fPiercingChance > 0 )
{
FirePenetratingBullets( info, 1, fPiercingChance, 0 );
}
else
{
FireRegularBullets( info );
}
}
void CASW_Marine::FireRegularBullets( const FireBulletsInfo_t &info )
{
static int tracerCount;
trace_t tr;
CAmmoDef* pAmmoDef = GetAmmoDef();
int nDamageType = pAmmoDef->DamageType(info.m_iAmmoType);
int nAmmoFlags = pAmmoDef->Flags(info.m_iAmmoType);
#ifdef GAME_DLL
if (ASWGameRules())
ASWGameRules()->m_fLastFireTime = gpGlobals->curtime;
#endif
#ifdef CLIENT_DLL
const bool bShowHitboxes = ( sv_showimpacts.GetInt() == 1 || sv_showimpacts.GetInt() == 2 ) && prediction->IsFirstTimePredicted();
#define DrawHitboxes DrawClientHitboxes
#define SHOWIMPACTS_COLOR 255, 0, 0
#else
const bool bShowHitboxes = sv_showimpacts.GetInt() == 1 || sv_showimpacts.GetInt() == 3;
#define DrawHitboxes DrawServerHitboxes
#define SHOWIMPACTS_COLOR 0, 0, 255
#endif
bool bDoServerEffects = true;
#if defined ( _XBOX ) && defined( GAME_DLL )
if( IsInhabited() )
{
CBasePlayer *pPlayer = GetCommander();
if (pPlayer)
{
int rumbleEffect = pPlayer->GetActiveWeapon()->GetRumbleEffect();
if( rumbleEffect != RUMBLE_INVALID )
{
if( rumbleEffect == RUMBLE_SHOTGUN_SINGLE )
{
if( info.m_iShots == 12 )
{
// Upgrade to double barrel rumble effect
rumbleEffect = RUMBLE_SHOTGUN_DOUBLE;
}
}
pPlayer->RumbleEffect( rumbleEffect, 0, RUMBLE_FLAG_RESTART );
}
}
}
#endif//_XBOX
float flPlayerDamage = info.m_flPlayerDamage;
if ( flPlayerDamage == 0 )
{
if ( nAmmoFlags & AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER )
{
flPlayerDamage = pAmmoDef->PlrDamage( info.m_iAmmoType );
}
}
// the default attacker is ourselves
CBaseEntity *pAttacker = info.m_pAttacker ? info.m_pAttacker : this;
// Make sure we don't have a dangling damage target from a recursive call
if ( g_MultiDamage.GetTarget() != NULL )
{
ApplyMultiDamage();
}
ClearMultiDamage();
g_MultiDamage.SetDamageType( nDamageType | DMG_NEVERGIB );
Vector vecDir;
Vector vecEnd;
Vector vecFinalDir; // bullet's final direction can be changed by passing through a portal
CASWTraceFilterShot traceFilter( this, info.m_pAdditionalIgnoreEnt, COLLISION_GROUP_NONE, info.m_vecDirShooting );
traceFilter.SetSkipMarines( false );
traceFilter.SetSkipRollingMarines( true );
int iSeed = CBaseEntity::GetPredictionRandomSeed();// & 255;
if ( iSeed == -1 )
{
Assert( !IsInhabited() );
iSeed = gpGlobals->tickcount;
}
CShotManipulator Manipulator( info.m_vecDirShooting );
bool bDoImpacts = false;
bool bDoTracers = false;
for (int iShot = 0; iShot < info.m_iShots; iShot++)
{
bool bHitWater = false;
bool bHitGlass = false;
// Prediction is only usable on players
RandomSeed( iSeed + iShot ); // init random system with this seed
if (info.m_vecSpread[0] < 0) // weapon wants simple random spread, not gaussian
{
QAngle angChange(info.m_vecSpread[0] * random->RandomFloat(-0.5f, 0.5f),
info.m_vecSpread[1] * random->RandomFloat(-0.5f, 0.5f),
info.m_vecSpread[2] * random->RandomFloat(-0.5f, 0.5f));
matrix3x4_t matrix;
AngleMatrix( angChange, matrix );
VectorTransform(info.m_vecDirShooting, matrix, vecDir);
}
else
{
// If we're firing multiple shots, and the first shot has to be bang on target, ignore spread
if ( iShot == 0 && info.m_iShots > 1 && (info.m_nFlags & FIRE_BULLETS_FIRST_SHOT_ACCURATE) )
{
vecDir = Manipulator.GetShotDirection();
}
else
{
if (info.m_nFlags & FIRE_BULLETS_ANGULAR_SPREAD)
vecDir = Manipulator.ApplyAngularSpread( info.m_vecSpread );
else
vecDir = Manipulator.ApplySpread( info.m_vecSpread );
}
}
vecEnd = info.m_vecSrc + vecDir * info.m_flDistance;
if( (info.m_nFlags & FIRE_BULLETS_HULL) != 0 && asw_allow_hull_shots.GetBool())
{
// hulls that make it easier to hit targets with the shotgun.
AI_TraceHull( info.m_vecSrc, vecEnd, Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), MASK_SHOT, &traceFilter, &tr );
}
else
{
AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr);
}
vecFinalDir = tr.endpos - tr.startpos;
if (vecFinalDir == vec3_origin)
{
vecFinalDir = info.m_vecDirShooting;
}
VectorNormalize( vecFinalDir );
#ifdef GAME_DLL
if ( ai_debug_shoot_positions.GetBool() )
NDebugOverlay::Line(info.m_vecSrc, vecEnd, 255, 255, 255, false, .1 );
#endif
// Now hit all triggers along the ray that respond to shots...
// Clip the ray to the first collided solid returned from traceline
CTakeDamageInfo triggerInfo( pAttacker, pAttacker, info.m_flDamage, nDamageType );
CalculateBulletDamageForce( &triggerInfo, info.m_iAmmoType, vecFinalDir, tr.endpos );
triggerInfo.ScaleDamageForce( info.m_flDamageForceScale );
triggerInfo.SetAmmoType( info.m_iAmmoType );
#ifdef GAME_DLL
TraceAttackToTriggers( triggerInfo, tr.startpos, tr.endpos, vecFinalDir );
#endif
// Make sure given a valid bullet type
if (info.m_iAmmoType == -1)
{
DevMsg("ERROR: Undefined ammo type!\n");
return;
}
Vector vecTracerDest = tr.endpos;
// do damage, paint decals
if (tr.fraction != 1.0)
{
#ifdef GAME_DLL
// For shots that don't need persistance
int soundEntChannel = ( info.m_nFlags&FIRE_BULLETS_TEMPORARY_DANGER_SOUND ) ? SOUNDENT_CHANNEL_BULLET_IMPACT : SOUNDENT_CHANNEL_UNSPECIFIED;
CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 200, 0.5, this, soundEntChannel );
#endif
// See if the bullet ended up underwater + started out of the water
if ( !bHitWater && ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) )
{
bHitWater = HandleShotImpactingWater( info, vecEnd, &traceFilter, &vecTracerDest );
}
float flActualDamage = info.m_flDamage;
// If we hit a player, and we have player damage specified, use that instead
// Adrian: Make sure to use the currect value if we hit a vehicle the player is currently driving.
if ( flPlayerDamage != 0.0f )
{
if ( tr.m_pEnt->IsPlayer() )
{
flActualDamage = flPlayerDamage;
}
#ifdef GAME_DLL
else if ( tr.m_pEnt->GetServerVehicle() )
{
if ( tr.m_pEnt->GetServerVehicle()->GetPassenger() && tr.m_pEnt->GetServerVehicle()->GetPassenger()->IsPlayer() )
{
flActualDamage = flPlayerDamage;
}
}
#endif
}
int nActualDamageType = nDamageType;
if ( flActualDamage == 0.0 )
{
flActualDamage = g_pGameRules->GetAmmoDamage( pAttacker, tr.m_pEnt, info.m_iAmmoType );
}
else
{
nActualDamageType = nDamageType | ((flActualDamage > ASW_GIB_DAMAGE_LIMIT) ? DMG_ALWAYSGIB : DMG_NEVERGIB );
}
if ( !bHitWater || ((info.m_nFlags & FIRE_BULLETS_DONT_HIT_UNDERWATER) == 0) )
{
// Damage specified by function parameter
CTakeDamageInfo dmgInfo( this, pAttacker, flActualDamage, nActualDamageType );
CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, vecFinalDir, tr.endpos );
dmgInfo.ScaleDamageForce( info.m_flDamageForceScale );
dmgInfo.SetAmmoType( info.m_iAmmoType );
dmgInfo.SetWeapon( GetActiveASWWeapon() );
DoDamagePowerupEffects( tr.m_pEnt, dmgInfo, vecFinalDir, &tr );
tr.m_pEnt->DispatchTraceAttack( dmgInfo, vecFinalDir, &tr );
if ( !bHitWater || (info.m_nFlags & FIRE_BULLETS_ALLOW_WATER_SURFACE_IMPACTS) )
{
if ( bDoServerEffects == true )
{
DoImpactEffect( tr, nDamageType );
}
else
{
bDoImpacts = true;
}
}