Source code

Revision control

1
/***
2
*
3
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
4
*
5
* This product contains software technology licensed from Id
6
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
7
* All Rights Reserved.
8
*
9
* Use, distribution, and modification of this source code and/or resulting
10
* object code is restricted to non-commercial enhancements to products from
11
* Valve LLC. All other use, distribution, or modification is prohibited
12
* without written permission from Valve LLC.
13
*
14
****/
15
/*
16
17
===== player.cpp ========================================================
18
19
functions dealing with the player
20
21
*/
22
23
#include "extdll.h"
24
#include "util.h"
25
26
#include "cbase.h"
27
#include "player.h"
28
#include "trains.h"
29
#include "nodes.h"
30
#include "weapons.h"
31
#include "soundent.h"
32
#include "monsters.h"
33
#include "shake.h"
34
#include "decals.h"
35
#include "gamerules.h"
36
#include "game.h"
37
#include "pm_shared.h"
38
#include "hltv.h"
39
40
// #define DUCKFIX
41
42
extern DLL_GLOBAL ULONG g_ulModelIndexPlayer;
43
extern DLL_GLOBAL BOOL g_fGameOver;
44
extern DLL_GLOBAL BOOL g_fDrawLines;
45
int gEvilImpulse101;
46
extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle;
47
48
49
BOOL gInitHUD = TRUE;
50
51
extern void CopyToBodyQue(entvars_t* pev);
52
extern void respawn(entvars_t *pev, BOOL fCopyCorpse);
53
extern Vector VecBModelOrigin(entvars_t *pevBModel );
54
extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer );
55
56
// the world node graph
57
extern CGraph WorldGraph;
58
59
#define TRAIN_ACTIVE 0x80
60
#define TRAIN_NEW 0xc0
61
#define TRAIN_OFF 0x00
62
#define TRAIN_NEUTRAL 0x01
63
#define TRAIN_SLOW 0x02
64
#define TRAIN_MEDIUM 0x03
65
#define TRAIN_FAST 0x04
66
#define TRAIN_BACK 0x05
67
68
#define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes
69
#define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit)
70
71
// Global Savedata for player
72
TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] =
73
{
74
DEFINE_FIELD( CBasePlayer, m_flFlashLightTime, FIELD_TIME ),
75
DEFINE_FIELD( CBasePlayer, m_iFlashBattery, FIELD_INTEGER ),
76
77
DEFINE_FIELD( CBasePlayer, m_afButtonLast, FIELD_INTEGER ),
78
DEFINE_FIELD( CBasePlayer, m_afButtonPressed, FIELD_INTEGER ),
79
DEFINE_FIELD( CBasePlayer, m_afButtonReleased, FIELD_INTEGER ),
80
81
DEFINE_ARRAY( CBasePlayer, m_rgItems, FIELD_INTEGER, MAX_ITEMS ),
82
DEFINE_FIELD( CBasePlayer, m_afPhysicsFlags, FIELD_INTEGER ),
83
84
DEFINE_FIELD( CBasePlayer, m_flTimeStepSound, FIELD_TIME ),
85
DEFINE_FIELD( CBasePlayer, m_flTimeWeaponIdle, FIELD_TIME ),
86
DEFINE_FIELD( CBasePlayer, m_flSwimTime, FIELD_TIME ),
87
DEFINE_FIELD( CBasePlayer, m_flDuckTime, FIELD_TIME ),
88
DEFINE_FIELD( CBasePlayer, m_flWallJumpTime, FIELD_TIME ),
89
90
DEFINE_FIELD( CBasePlayer, m_flSuitUpdate, FIELD_TIME ),
91
DEFINE_ARRAY( CBasePlayer, m_rgSuitPlayList, FIELD_INTEGER, CSUITPLAYLIST ),
92
DEFINE_FIELD( CBasePlayer, m_iSuitPlayNext, FIELD_INTEGER ),
93
DEFINE_ARRAY( CBasePlayer, m_rgiSuitNoRepeat, FIELD_INTEGER, CSUITNOREPEAT ),
94
DEFINE_ARRAY( CBasePlayer, m_rgflSuitNoRepeatTime, FIELD_TIME, CSUITNOREPEAT ),
95
DEFINE_FIELD( CBasePlayer, m_lastDamageAmount, FIELD_INTEGER ),
96
97
DEFINE_ARRAY( CBasePlayer, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ),
98
DEFINE_FIELD( CBasePlayer, m_pActiveItem, FIELD_CLASSPTR ),
99
DEFINE_FIELD( CBasePlayer, m_pLastItem, FIELD_CLASSPTR ),
100
101
DEFINE_ARRAY( CBasePlayer, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ),
102
DEFINE_FIELD( CBasePlayer, m_idrowndmg, FIELD_INTEGER ),
103
DEFINE_FIELD( CBasePlayer, m_idrownrestored, FIELD_INTEGER ),
104
DEFINE_FIELD( CBasePlayer, m_tSneaking, FIELD_TIME ),
105
106
DEFINE_FIELD( CBasePlayer, m_iTrain, FIELD_INTEGER ),
107
DEFINE_FIELD( CBasePlayer, m_bitsHUDDamage, FIELD_INTEGER ),
108
DEFINE_FIELD( CBasePlayer, m_flFallVelocity, FIELD_FLOAT ),
109
DEFINE_FIELD( CBasePlayer, m_iTargetVolume, FIELD_INTEGER ),
110
DEFINE_FIELD( CBasePlayer, m_iWeaponVolume, FIELD_INTEGER ),
111
DEFINE_FIELD( CBasePlayer, m_iExtraSoundTypes, FIELD_INTEGER ),
112
DEFINE_FIELD( CBasePlayer, m_iWeaponFlash, FIELD_INTEGER ),
113
DEFINE_FIELD( CBasePlayer, m_fLongJump, FIELD_BOOLEAN ),
114
DEFINE_FIELD( CBasePlayer, m_fInitHUD, FIELD_BOOLEAN ),
115
DEFINE_FIELD( CBasePlayer, m_tbdPrev, FIELD_TIME ),
116
117
DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ),
118
DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ),
119
DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ),
120
121
//DEFINE_FIELD( CBasePlayer, m_fDeadTime, FIELD_FLOAT ), // only used in multiplayer games
122
//DEFINE_FIELD( CBasePlayer, m_fGameHUDInitialized, FIELD_INTEGER ), // only used in multiplayer games
123
//DEFINE_FIELD( CBasePlayer, m_flStopExtraSoundTime, FIELD_TIME ),
124
//DEFINE_FIELD( CBasePlayer, m_fKnownItem, FIELD_INTEGER ), // reset to zero on load
125
//DEFINE_FIELD( CBasePlayer, m_iPlayerSound, FIELD_INTEGER ), // Don't restore, set in Precache()
126
//DEFINE_FIELD( CBasePlayer, m_pentSndLast, FIELD_EDICT ), // Don't restore, client needs reset
127
//DEFINE_FIELD( CBasePlayer, m_flSndRoomtype, FIELD_FLOAT ), // Don't restore, client needs reset
128
//DEFINE_FIELD( CBasePlayer, m_flSndRange, FIELD_FLOAT ), // Don't restore, client needs reset
129
//DEFINE_FIELD( CBasePlayer, m_fNewAmmo, FIELD_INTEGER ), // Don't restore, client needs reset
130
//DEFINE_FIELD( CBasePlayer, m_flgeigerRange, FIELD_FLOAT ), // Don't restore, reset in Precache()
131
//DEFINE_FIELD( CBasePlayer, m_flgeigerDelay, FIELD_FLOAT ), // Don't restore, reset in Precache()
132
//DEFINE_FIELD( CBasePlayer, m_igeigerRangePrev, FIELD_FLOAT ), // Don't restore, reset in Precache()
133
//DEFINE_FIELD( CBasePlayer, m_iStepLeft, FIELD_INTEGER ), // Don't need to restore
134
//DEFINE_ARRAY( CBasePlayer, m_szTextureName, FIELD_CHARACTER, CBTEXTURENAMEMAX ), // Don't need to restore
135
//DEFINE_FIELD( CBasePlayer, m_chTextureType, FIELD_CHARACTER ), // Don't need to restore
136
//DEFINE_FIELD( CBasePlayer, m_fNoPlayerSound, FIELD_BOOLEAN ), // Don't need to restore, debug
137
//DEFINE_FIELD( CBasePlayer, m_iUpdateTime, FIELD_INTEGER ), // Don't need to restore
138
//DEFINE_FIELD( CBasePlayer, m_iClientHealth, FIELD_INTEGER ), // Don't restore, client needs reset
139
//DEFINE_FIELD( CBasePlayer, m_iClientBattery, FIELD_INTEGER ), // Don't restore, client needs reset
140
//DEFINE_FIELD( CBasePlayer, m_iClientHideHUD, FIELD_INTEGER ), // Don't restore, client needs reset
141
//DEFINE_FIELD( CBasePlayer, m_fWeapon, FIELD_BOOLEAN ), // Don't restore, client needs reset
142
//DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), // Don't restore, depends on server message after spawning and only matters in multiplayer
143
//DEFINE_FIELD( CBasePlayer, m_vecAutoAim, FIELD_VECTOR ), // Don't save/restore - this is recomputed
144
//DEFINE_ARRAY( CBasePlayer, m_rgAmmoLast, FIELD_INTEGER, MAX_AMMO_SLOTS ), // Don't need to restore
145
//DEFINE_FIELD( CBasePlayer, m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore
146
//DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), // Don't need to restore
147
148
};
149
150
151
int giPrecacheGrunt = 0;
152
int gmsgShake = 0;
153
int gmsgFade = 0;
154
int gmsgSelAmmo = 0;
155
int gmsgFlashlight = 0;
156
int gmsgFlashBattery = 0;
157
int gmsgResetHUD = 0;
158
int gmsgInitHUD = 0;
159
int gmsgShowGameTitle = 0;
160
int gmsgCurWeapon = 0;
161
int gmsgHealth = 0;
162
int gmsgDamage = 0;
163
int gmsgBattery = 0;
164
int gmsgTrain = 0;
165
int gmsgLogo = 0;
166
int gmsgWeaponList = 0;
167
int gmsgAmmoX = 0;
168
int gmsgHudText = 0;
169
int gmsgDeathMsg = 0;
170
int gmsgScoreInfo = 0;
171
int gmsgTeamInfo = 0;
172
int gmsgTeamScore = 0;
173
int gmsgGameMode = 0;
174
int gmsgMOTD = 0;
175
int gmsgServerName = 0;
176
int gmsgAmmoPickup = 0;
177
int gmsgWeapPickup = 0;
178
int gmsgItemPickup = 0;
179
int gmsgHideWeapon = 0;
180
int gmsgSetCurWeap = 0;
181
int gmsgSayText = 0;
182
int gmsgTextMsg = 0;
183
int gmsgSetFOV = 0;
184
int gmsgShowMenu = 0;
185
int gmsgGeigerRange = 0;
186
int gmsgTeamNames = 0;
187
188
int gmsgStatusText = 0;
189
int gmsgStatusValue = 0;
190
191
192
193
void LinkUserMessages( void )
194
{
195
// Already taken care of?
196
if ( gmsgSelAmmo )
197
{
198
return;
199
}
200
201
gmsgSelAmmo = REG_USER_MSG("SelAmmo", sizeof(SelAmmo));
202
gmsgCurWeapon = REG_USER_MSG("CurWeapon", 3);
203
gmsgGeigerRange = REG_USER_MSG("Geiger", 1);
204
gmsgFlashlight = REG_USER_MSG("Flashlight", 2);
205
gmsgFlashBattery = REG_USER_MSG("FlashBat", 1);
206
gmsgHealth = REG_USER_MSG( "Health", 1 );
207
gmsgDamage = REG_USER_MSG( "Damage", 12 );
208
gmsgBattery = REG_USER_MSG( "Battery", 2);
209
gmsgTrain = REG_USER_MSG( "Train", 1);
210
//gmsgHudText = REG_USER_MSG( "HudTextPro", -1 );
211
gmsgHudText = REG_USER_MSG( "HudText", -1 ); // we don't use the message but 3rd party addons may!
212
gmsgSayText = REG_USER_MSG( "SayText", -1 );
213
gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 );
214
gmsgWeaponList = REG_USER_MSG("WeaponList", -1);
215
gmsgResetHUD = REG_USER_MSG("ResetHUD", 1); // called every respawn
216
gmsgInitHUD = REG_USER_MSG("InitHUD", 0 ); // called every time a new player joins the server
217
gmsgShowGameTitle = REG_USER_MSG("GameTitle", 1);
218
gmsgDeathMsg = REG_USER_MSG( "DeathMsg", -1 );
219
gmsgScoreInfo = REG_USER_MSG( "ScoreInfo", 9 );
220
gmsgTeamInfo = REG_USER_MSG( "TeamInfo", -1 ); // sets the name of a player's team
221
gmsgTeamScore = REG_USER_MSG( "TeamScore", -1 ); // sets the score of a team on the scoreboard
222
gmsgGameMode = REG_USER_MSG( "GameMode", 1 );
223
gmsgMOTD = REG_USER_MSG( "MOTD", -1 );
224
gmsgServerName = REG_USER_MSG( "ServerName", -1 );
225
gmsgAmmoPickup = REG_USER_MSG( "AmmoPickup", 2 );
226
gmsgWeapPickup = REG_USER_MSG( "WeapPickup", 1 );
227
gmsgItemPickup = REG_USER_MSG( "ItemPickup", -1 );
228
gmsgHideWeapon = REG_USER_MSG( "HideWeapon", 1 );
229
gmsgSetFOV = REG_USER_MSG( "SetFOV", 1 );
230
gmsgShowMenu = REG_USER_MSG( "ShowMenu", -1 );
231
gmsgShake = REG_USER_MSG("ScreenShake", sizeof(ScreenShake));
232
gmsgFade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade));
233
gmsgAmmoX = REG_USER_MSG("AmmoX", 2);
234
gmsgTeamNames = REG_USER_MSG( "TeamNames", -1 );
235
236
gmsgStatusText = REG_USER_MSG("StatusText", -1);
237
gmsgStatusValue = REG_USER_MSG("StatusValue", 3);
238
239
}
240
241
LINK_ENTITY_TO_CLASS( player, CBasePlayer );
242
243
244
245
void CBasePlayer :: Pain( void )
246
{
247
float flRndSound;//sound randomizer
248
249
flRndSound = RANDOM_FLOAT ( 0 , 1 );
250
251
if ( flRndSound <= 0.33 )
252
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM);
253
else if ( flRndSound <= 0.66 )
254
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM);
255
else
256
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM);
257
}
258
259
/*
260
*
261
*/
262
Vector VecVelocityForDamage(float flDamage)
263
{
264
Vector vec(RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
265
266
if (flDamage > -50)
267
vec = vec * 0.7;
268
else if (flDamage > -200)
269
vec = vec * 2;
270
else
271
vec = vec * 10;
272
273
return vec;
274
}
275
276
#if 0 /*
277
static void ThrowGib(entvars_t *pev, char *szGibModel, float flDamage)
278
{
279
edict_t *pentNew = CREATE_ENTITY();
280
entvars_t *pevNew = VARS(pentNew);
281
282
pevNew->origin = pev->origin;
283
SET_MODEL(ENT(pevNew), szGibModel);
284
UTIL_SetSize(pevNew, g_vecZero, g_vecZero);
285
286
pevNew->velocity = VecVelocityForDamage(flDamage);
287
pevNew->movetype = MOVETYPE_BOUNCE;
288
pevNew->solid = SOLID_NOT;
289
pevNew->avelocity.x = RANDOM_FLOAT(0,600);
290
pevNew->avelocity.y = RANDOM_FLOAT(0,600);
291
pevNew->avelocity.z = RANDOM_FLOAT(0,600);
292
CHANGE_METHOD(ENT(pevNew), em_think, SUB_Remove);
293
pevNew->ltime = gpGlobals->time;
294
pevNew->nextthink = gpGlobals->time + RANDOM_FLOAT(10,20);
295
pevNew->frame = 0;
296
pevNew->flags = 0;
297
}
298
299
300
static void ThrowHead(entvars_t *pev, char *szGibModel, floatflDamage)
301
{
302
SET_MODEL(ENT(pev), szGibModel);
303
pev->frame = 0;
304
pev->nextthink = -1;
305
pev->movetype = MOVETYPE_BOUNCE;
306
pev->takedamage = DAMAGE_NO;
307
pev->solid = SOLID_NOT;
308
pev->view_ofs = Vector(0,0,8);
309
UTIL_SetSize(pev, Vector(-16,-16,0), Vector(16,16,56));
310
pev->velocity = VecVelocityForDamage(flDamage);
311
pev->avelocity = RANDOM_FLOAT(-1,1) * Vector(0,600,0);
312
pev->origin.z -= 24;
313
ClearBits(pev->flags, FL_ONGROUND);
314
}
315
316
317
*/
318
#endif
319
320
int TrainSpeed(int iSpeed, int iMax)
321
{
322
float fSpeed, fMax;
323
int iRet = 0;
324
325
fMax = (float)iMax;
326
fSpeed = iSpeed;
327
328
fSpeed = fSpeed/fMax;
329
330
if (iSpeed < 0)
331
iRet = TRAIN_BACK;
332
else if (iSpeed == 0)
333
iRet = TRAIN_NEUTRAL;
334
else if (fSpeed < 0.33)
335
iRet = TRAIN_SLOW;
336
else if (fSpeed < 0.66)
337
iRet = TRAIN_MEDIUM;
338
else
339
iRet = TRAIN_FAST;
340
341
return iRet;
342
}
343
344
void CBasePlayer :: DeathSound( void )
345
{
346
// water death sounds
347
/*
348
if (pev->waterlevel == 3)
349
{
350
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_NONE);
351
return;
352
}
353
*/
354
355
// temporarily using pain sounds for death sounds
356
switch (RANDOM_LONG(1,5))
357
{
358
case 1:
359
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM);
360
break;
361
case 2:
362
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM);
363
break;
364
case 3:
365
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM);
366
break;
367
}
368
369
// play one of the suit death alarms
370
EMIT_GROUPNAME_SUIT(ENT(pev), "HEV_DEAD");
371
}
372
373
// override takehealth
374
// bitsDamageType indicates type of damage healed.
375
376
int CBasePlayer :: TakeHealth( float flHealth, int bitsDamageType )
377
{
378
return CBaseMonster :: TakeHealth (flHealth, bitsDamageType);
379
380
}
381
382
Vector CBasePlayer :: GetGunPosition( )
383
{
384
// UTIL_MakeVectors(pev->v_angle);
385
// m_HackedGunPos = pev->view_ofs;
386
Vector origin;
387
388
origin = pev->origin + pev->view_ofs;
389
390
return origin;
391
}
392
393
//=========================================================
394
// TraceAttack
395
//=========================================================
396
void CBasePlayer :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
397
{
398
if ( pev->takedamage )
399
{
400
m_LastHitGroup = ptr->iHitgroup;
401
402
switch ( ptr->iHitgroup )
403
{
404
case HITGROUP_GENERIC:
405
break;
406
case HITGROUP_HEAD:
407
flDamage *= gSkillData.plrHead;
408
break;
409
case HITGROUP_CHEST:
410
flDamage *= gSkillData.plrChest;
411
break;
412
case HITGROUP_STOMACH:
413
flDamage *= gSkillData.plrStomach;
414
break;
415
case HITGROUP_LEFTARM:
416
case HITGROUP_RIGHTARM:
417
flDamage *= gSkillData.plrArm;
418
break;
419
case HITGROUP_LEFTLEG:
420
case HITGROUP_RIGHTLEG:
421
flDamage *= gSkillData.plrLeg;
422
break;
423
default:
424
break;
425
}
426
427
SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood.
428
TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
429
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
430
}
431
}
432
433
/*
434
Take some damage.
435
NOTE: each call to TakeDamage with bitsDamageType set to a time-based damage
436
type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation
437
etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC.
438
*/
439
440
#define ARMOR_RATIO 0.2 // Armor Takes 80% of the damage
441
#define ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health
442
443
int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
444
{
445
// have suit diagnose the problem - ie: report damage type
446
int bitsDamage = bitsDamageType;
447
int ffound = TRUE;
448
int fmajor;
449
int fcritical;
450
int fTookDamage;
451
int ftrivial;
452
float flRatio;
453
float flBonus;
454
float flHealthPrev = pev->health;
455
456
flBonus = ARMOR_BONUS;
457
flRatio = ARMOR_RATIO;
458
459
if ( ( bitsDamageType & DMG_BLAST ) && g_pGameRules->IsMultiplayer() )
460
{
461
// blasts damage armor more.
462
flBonus *= 2;
463
}
464
465
// Already dead
466
if ( !IsAlive() )
467
return 0;
468
// go take the damage first
469
470
471
CBaseEntity *pAttacker = CBaseEntity::Instance(pevAttacker);
472
473
if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker ) )
474
{
475
// Refuse the damage
476
return 0;
477
}
478
479
// keep track of amount of damage last sustained
480
m_lastDamageAmount = static_cast<int>(flDamage);
481
482
// Armor.
483
if (pev->armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage!
484
{
485
float flNew = flDamage * flRatio;
486
487
float flArmor;
488
489
flArmor = (flDamage - flNew) * flBonus;
490
491
// Does this use more armor than we have?
492
if (flArmor > pev->armorvalue)
493
{
494
flArmor = pev->armorvalue;
495
flArmor *= (1/flBonus);
496
flNew = flDamage - flArmor;
497
pev->armorvalue = 0;
498
}
499
else
500
pev->armorvalue -= flArmor;
501
502
flDamage = flNew;
503
}
504
505
// this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that
506
// as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc)
507
fTookDamage = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, (int)flDamage, bitsDamageType);
508
509
// reset damage time countdown for each type of time based damage player just sustained
510
511
{
512
for (int i = 0; i < CDMG_TIMEBASED; i++)
513
if (bitsDamageType & (DMG_PARALYZE << i))
514
m_rgbTimeBasedDamage[i] = 0;
515
}
516
517
// tell director about it
518
MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
519
WRITE_BYTE ( 9 ); // command length in bytes
520
WRITE_BYTE ( DRC_CMD_EVENT ); // take damage event
521
WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity
522
WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity
523
WRITE_LONG( 5 ); // eventflags (priority and flags)
524
MESSAGE_END();
525
526
527
// how bad is it, doc?
528
529
ftrivial = (pev->health > 75 || m_lastDamageAmount < 5);
530
fmajor = (m_lastDamageAmount > 25);
531
fcritical = (pev->health < 30);
532
533
// handle all bits set in this damage message,
534
// let the suit give player the diagnosis
535
536
// UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash )
537
538
// UNDONE: still need to record damage and heal messages for the following types
539
540
// DMG_BURN
541
// DMG_FREEZE
542
// DMG_BLAST
543
// DMG_SHOCK
544
545
m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client
546
m_bitsHUDDamage = -1; // make sure the damage bits get resent
547
548
while (fTookDamage && (!ftrivial || (bitsDamage & DMG_TIMEBASED)) && ffound && bitsDamage)
549
{
550
ffound = FALSE;
551
552
if (bitsDamage & DMG_CLUB)
553
{
554
if (fmajor)
555
SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture
556
bitsDamage &= ~DMG_CLUB;
557
ffound = TRUE;
558
}
559
if (bitsDamage & (DMG_FALL | DMG_CRUSH))
560
{
561
if (fmajor)
562
SetSuitUpdate("!HEV_DMG5", FALSE, SUIT_NEXT_IN_30SEC); // major fracture
563
else
564
SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture
565
566
bitsDamage &= ~(DMG_FALL | DMG_CRUSH);
567
ffound = TRUE;
568
}
569
570
if (bitsDamage & DMG_BULLET)
571
{
572
if (m_lastDamageAmount > 5)
573
SetSuitUpdate("!HEV_DMG6", FALSE, SUIT_NEXT_IN_30SEC); // blood loss detected
574
//else
575
// SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration
576
577
bitsDamage &= ~DMG_BULLET;
578
ffound = TRUE;
579
}
580
581
if (bitsDamage & DMG_SLASH)
582
{
583
if (fmajor)
584
SetSuitUpdate("!HEV_DMG1", FALSE, SUIT_NEXT_IN_30SEC); // major laceration
585
else
586
SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration
587
588
bitsDamage &= ~DMG_SLASH;
589
ffound = TRUE;
590
}
591
592
if (bitsDamage & DMG_SONIC)
593
{
594
if (fmajor)
595
SetSuitUpdate("!HEV_DMG2", FALSE, SUIT_NEXT_IN_1MIN); // internal bleeding
596
bitsDamage &= ~DMG_SONIC;
597
ffound = TRUE;
598
}
599
600
if (bitsDamage & (DMG_POISON | DMG_PARALYZE))
601
{
602
SetSuitUpdate("!HEV_DMG3", FALSE, SUIT_NEXT_IN_1MIN); // blood toxins detected
603
bitsDamage &= ~(DMG_POISON | DMG_PARALYZE);
604
ffound = TRUE;
605
}
606
607
if (bitsDamage & DMG_ACID)
608
{
609
SetSuitUpdate("!HEV_DET1", FALSE, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected
610
bitsDamage &= ~DMG_ACID;
611
ffound = TRUE;
612
}
613
614
if (bitsDamage & DMG_NERVEGAS)
615
{
616
SetSuitUpdate("!HEV_DET0", FALSE, SUIT_NEXT_IN_1MIN); // biohazard detected
617
bitsDamage &= ~DMG_NERVEGAS;
618
ffound = TRUE;
619
}
620
621
if (bitsDamage & DMG_RADIATION)
622
{
623
SetSuitUpdate("!HEV_DET2", FALSE, SUIT_NEXT_IN_1MIN); // radiation detected
624
bitsDamage &= ~DMG_RADIATION;
625
ffound = TRUE;
626
}
627
if (bitsDamage & DMG_SHOCK)
628
{
629
bitsDamage &= ~DMG_SHOCK;
630
ffound = TRUE;
631
}
632
}
633
634
pev->punchangle.x = -2;
635
636
if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75)
637
{
638
// first time we take major damage...
639
// turn automedic on if not on
640
SetSuitUpdate("!HEV_MED1", FALSE, SUIT_NEXT_IN_30MIN); // automedic on
641
642
// give morphine shot if not given recently
643
SetSuitUpdate("!HEV_HEAL7", FALSE, SUIT_NEXT_IN_30MIN); // morphine shot
644
}
645
646
if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75)
647
{
648
649
// already took major damage, now it's critical...
650
if (pev->health < 6)
651
SetSuitUpdate("!HEV_HLTH3", FALSE, SUIT_NEXT_IN_10MIN); // near death
652
else if (pev->health < 20)
653
SetSuitUpdate("!HEV_HLTH2", FALSE, SUIT_NEXT_IN_10MIN); // health critical
654
655
// give critical health warnings
656
if (!RANDOM_LONG(0,3) && flHealthPrev < 50)
657
SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention
658
}
659
660
// if we're taking time based damage, warn about its continuing effects
661
if (fTookDamage && (bitsDamageType & DMG_TIMEBASED) && flHealthPrev < 75)
662
{
663
if (flHealthPrev < 50)
664
{
665
if (!RANDOM_LONG(0,3))
666
SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention
667
}
668
else
669
SetSuitUpdate("!HEV_HLTH1", FALSE, SUIT_NEXT_IN_10MIN); // health dropping
670
}
671
672
return fTookDamage;
673
}
674
675
//=========================================================
676
// PackDeadPlayerItems - call this when a player dies to
677
// pack up the appropriate weapons and ammo items, and to
678
// destroy anything that shouldn't be packed.
679
//
680
// This is pretty brute force :(
681
//=========================================================
682
void CBasePlayer::PackDeadPlayerItems( void )
683
{
684
int iWeaponRules;
685
int iAmmoRules;
686
int i;
687
CBasePlayerWeapon *rgpPackWeapons[ 20 ];// 20 hardcoded for now. How to determine exactly how many weapons we have?
688
int iPackAmmo[ MAX_AMMO_SLOTS + 1];
689
int iPW = 0;// index into packweapons array
690
int iPA = 0;// index into packammo array
691
692
memset(rgpPackWeapons, 0, sizeof(rgpPackWeapons) );
693
memset(iPackAmmo, -1, sizeof(iPackAmmo) );
694
695
// get the game rules
696
iWeaponRules = g_pGameRules->DeadPlayerWeapons( this );
697
iAmmoRules = g_pGameRules->DeadPlayerAmmo( this );
698
699
if ( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO )
700
{
701
// nothing to pack. Remove the weapons and return. Don't call create on the box!
702
RemoveAllItems( TRUE );
703
return;
704
}
705
706
// go through all of the weapons and make a list of the ones to pack
707
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
708
{
709
if ( m_rgpPlayerItems[ i ] )
710
{
711
// there's a weapon here. Should I pack it?
712
CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ];
713
714
while ( pPlayerItem )
715
{
716
switch( iWeaponRules )
717
{
718
case GR_PLR_DROP_GUN_ACTIVE:
719
if ( m_pActiveItem && pPlayerItem == m_pActiveItem )
720
{
721
// this is the active item. Pack it.
722
rgpPackWeapons[ iPW++ ] = (CBasePlayerWeapon *)pPlayerItem;
723
}
724
break;
725
726
case GR_PLR_DROP_GUN_ALL:
727
rgpPackWeapons[ iPW++ ] = (CBasePlayerWeapon *)pPlayerItem;
728
break;
729
730
default:
731
break;
732
}
733
734
pPlayerItem = pPlayerItem->m_pNext;
735
}
736
}
737
}
738
739
// now go through ammo and make a list of which types to pack.
740
if ( iAmmoRules != GR_PLR_DROP_AMMO_NO )
741
{
742
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
743
{
744
if ( m_rgAmmo[ i ] > 0 )
745
{
746
// player has some ammo of this type.
747
switch ( iAmmoRules )
748
{
749
case GR_PLR_DROP_AMMO_ALL:
750
iPackAmmo[ iPA++ ] = i;
751
break;
752
753
case GR_PLR_DROP_AMMO_ACTIVE:
754
if ( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() )
755
{
756
// this is the primary ammo type for the active weapon
757
iPackAmmo[ iPA++ ] = i;
758
}
759
else if ( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() )
760
{
761
// this is the secondary ammo type for the active weapon
762
iPackAmmo[ iPA++ ] = i;
763
}
764
break;
765
766
default:
767
break;
768
}
769
}
770
}
771
}
772
773
// create a box to pack the stuff into.
774
CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin, pev->angles, edict() );
775
776
pWeaponBox->pev->angles.x = 0;// don't let weaponbox tilt.
777
pWeaponBox->pev->angles.z = 0;
778
779
pWeaponBox->SetThink( &CWeaponBox::Kill );
780
pWeaponBox->pev->nextthink = gpGlobals->time + 120;
781
782
// back these two lists up to their first elements
783
iPA = 0;
784
iPW = 0;
785
786
// pack the ammo
787
while ( iPackAmmo[ iPA ] != -1 )
788
{
789
pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[ iPackAmmo[ iPA ] ].pszName ), m_rgAmmo[ iPackAmmo[ iPA ] ] );
790
iPA++;
791
}
792
793
// now pack all of the items in the lists
794
while ( rgpPackWeapons[ iPW ] )
795
{
796
// weapon unhooked from the player. Pack it into der box.
797
pWeaponBox->PackWeapon( rgpPackWeapons[ iPW ] );
798
799
iPW++;
800
}
801
802
pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some.
803
804
RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above.
805
}
806
807
void CBasePlayer::RemoveAllItems( BOOL removeSuit )
808
{
809
if (m_pActiveItem)
810
{
811
ResetAutoaim( );
812
m_pActiveItem->Holster( );
813
m_pActiveItem = NULL;
814
}
815
816
m_pLastItem = NULL;
817
818
if ( m_pTank != 0 )
819
{
820
m_pTank->Use( this, this, USE_OFF, 0 );
821
m_pTank = NULL;
822
}
823
824
int i;
825
CBasePlayerItem *pPendingItem;
826
for (i = 0; i < MAX_ITEM_TYPES; i++)
827
{
828
m_pActiveItem = m_rgpPlayerItems[i];
829
while (m_pActiveItem)
830
{
831
pPendingItem = m_pActiveItem->m_pNext;
832
m_pActiveItem->Drop( );
833
m_pActiveItem = pPendingItem;
834
}
835
m_rgpPlayerItems[i] = NULL;
836
}
837
m_pActiveItem = NULL;
838
839
pev->viewmodel = 0;
840
pev->weaponmodel = 0;
841
842
if ( removeSuit )
843
pev->weapons = 0;
844
else
845
pev->weapons &= ~WEAPON_ALLWEAPONS;
846
847
for ( i = 0; i < MAX_AMMO_SLOTS;i++)
848
m_rgAmmo[i] = 0;
849
850
UpdateClientData();
851
// send Selected Weapon Message to our client
852
MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev );
853
WRITE_BYTE(0);
854
WRITE_BYTE(0);
855
WRITE_BYTE(0);
856
MESSAGE_END();
857
}
858
859
/*
860
* GLOBALS ASSUMED SET: g_ulModelIndexPlayer
861
*
862
* ENTITY_METHOD(PlayerDie)
863
*/
864
entvars_t *g_pevLastInflictor; // Set in combat.cpp. Used to pass the damage inflictor for death messages.
865
// Better solution: Add as parameter to all Killed() functions.
866
867
void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib )
868
{
869
CSound *pSound;
870
871
// Holster weapon immediately, to allow it to cleanup
872
if ( m_pActiveItem )
873
m_pActiveItem->Holster( );
874
875
g_pGameRules->PlayerKilled( this, pevAttacker, g_pevLastInflictor );
876
877
if ( m_pTank != 0 )
878
{
879
m_pTank->Use( this, this, USE_OFF, 0 );
880
m_pTank = NULL;
881
}
882
883
// this client isn't going to be thinking for a while, so reset the sound until they respawn
884
pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) );
885
{
886
if ( pSound )
887
{
888
pSound->Reset();
889
}
890
}
891
892
SetAnimation( PLAYER_DIE );
893
894
m_iRespawnFrames = 0;
895
896
pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
897
898
pev->deadflag = DEAD_DYING;
899
pev->movetype = MOVETYPE_TOSS;
900
ClearBits( pev->flags, FL_ONGROUND );
901
if (pev->velocity.z < 10)
902
pev->velocity.z += RANDOM_FLOAT(0,300);
903
904
// clear out the suit message cache so we don't keep chattering
905
SetSuitUpdate(NULL, FALSE, 0);
906
907
// send "health" update message to zero
908
m_iClientHealth = 0;
909
MESSAGE_BEGIN( MSG_ONE, gmsgHealth, NULL, pev );
910
WRITE_BYTE( m_iClientHealth );
911
MESSAGE_END();
912
913
// Tell Ammo Hud that the player is dead
914
MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev );
915
WRITE_BYTE(0);
916
WRITE_BYTE(0XFF);
917
WRITE_BYTE(0xFF);
918
MESSAGE_END();
919
920
// reset FOV
921
pev->fov = m_iFOV = m_iClientFOV = 0;
922
923
MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev );
924
WRITE_BYTE(0);
925
MESSAGE_END();
926
927
928
// UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12
929
// UTIL_ScreenFade( edict(), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE );
930
931
if ( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS )
932
{
933
pev->solid = SOLID_NOT;
934
GibMonster(); // This clears pev->model
935
pev->effects |= EF_NODRAW;
936
return;
937
}
938
939
DeathSound();
940
941
pev->angles.x = 0;
942
pev->angles.z = 0;
943
944
SetThink(&CBasePlayer::PlayerDeathThink);
945
pev->nextthink = gpGlobals->time + 0.1;
946
}
947
948
949
// Set the activity based on an event or current state
950
void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim )
951
{
952
int animDesired;
953
float speed;
954
char szAnim[64];
955
956
speed = pev->velocity.Length2D();
957
958
if (pev->flags & FL_FROZEN)
959
{
960
speed = 0;
961
playerAnim = PLAYER_IDLE;
962
}
963
964
switch (playerAnim)
965
{
966
case PLAYER_JUMP:
967
m_IdealActivity = ACT_HOP;
968
break;
969
970
case PLAYER_SUPERJUMP:
971
m_IdealActivity = ACT_LEAP;
972
break;
973
974
case PLAYER_DIE:
975
m_IdealActivity = ACT_DIESIMPLE;
976
m_IdealActivity = GetDeathActivity( );
977
break;
978
979
case PLAYER_ATTACK1:
980
switch( m_Activity )
981
{
982
case ACT_HOVER:
983
case ACT_SWIM:
984
case ACT_HOP:
985
case ACT_LEAP:
986
case ACT_DIESIMPLE:
987
m_IdealActivity = m_Activity;
988
break;
989
default:
990
m_IdealActivity = ACT_RANGE_ATTACK1;
991
break;
992
}
993
break;
994
case PLAYER_IDLE:
995
case PLAYER_WALK:
996
if ( !FBitSet( pev->flags, FL_ONGROUND ) && (m_Activity == ACT_HOP || m_Activity == ACT_LEAP) ) // Still jumping
997
{
998
m_IdealActivity = m_Activity;
999
}
1000
else if ( pev->waterlevel > 1 )
1001
{
1002
if ( speed == 0 )
1003
m_IdealActivity = ACT_HOVER;
1004
else
1005
m_IdealActivity = ACT_SWIM;
1006
}
1007
else
1008
{
1009
m_IdealActivity = ACT_WALK;
1010
}
1011
break;
1012
}
1013
1014
switch (m_IdealActivity)
1015
{
1016
case ACT_HOVER:
1017
case ACT_LEAP:
1018
case ACT_SWIM:
1019
case ACT_HOP:
1020
case ACT_DIESIMPLE:
1021
default:
1022
if ( m_Activity == m_IdealActivity)
1023
return;
1024
m_Activity = m_IdealActivity;
1025
1026
animDesired = LookupActivity( m_Activity );
1027
// Already using the desired animation?
1028
if (pev->sequence == animDesired)
1029
return;
1030
1031
pev->gaitsequence = 0;
1032
pev->sequence = animDesired;
1033
pev->frame = 0;
1034
ResetSequenceInfo( );
1035
return;
1036
1037
case ACT_RANGE_ATTACK1:
1038
if ( FBitSet( pev->flags, FL_DUCKING ) ) // crouching
1039
strcpy( szAnim, "crouch_shoot_" );
1040
else
1041
strcpy( szAnim, "ref_shoot_" );
1042
strcat( szAnim, m_szAnimExtention );
1043
animDesired = LookupSequence( szAnim );
1044
if (animDesired == -1)
1045
animDesired = 0;
1046
1047
if ( pev->sequence != animDesired || !m_fSequenceLoops )
1048
{
1049
pev->frame = 0;
1050
}
1051
1052
if (!m_fSequenceLoops)
1053
{
1054
pev->effects |= EF_NOINTERP;
1055
}
1056
1057
m_Activity = m_IdealActivity;
1058
1059
pev->sequence = animDesired;
1060
ResetSequenceInfo( );
1061
break;
1062
1063
case ACT_WALK:
1064
if (m_Activity != ACT_RANGE_ATTACK1 || m_fSequenceFinished)
1065
{
1066
if ( FBitSet( pev->flags, FL_DUCKING ) ) // crouching
1067
strcpy( szAnim, "crouch_aim_" );
1068
else
1069
strcpy( szAnim, "ref_aim_" );
1070
strcat( szAnim, m_szAnimExtention );
1071
animDesired = LookupSequence( szAnim );
1072
if (animDesired == -1)
1073
animDesired = 0;
1074
m_Activity = ACT_WALK;
1075
}
1076
else
1077
{
1078
animDesired = pev->sequence;
1079
}
1080
}
1081
1082
if ( FBitSet( pev->flags, FL_DUCKING ) )
1083
{
1084
if ( speed == 0)
1085
{
1086
pev->gaitsequence = LookupActivity( ACT_CROUCHIDLE );
1087
// pev->gaitsequence = LookupActivity( ACT_CROUCH );
1088
}
1089
else
1090
{
1091
pev->gaitsequence = LookupActivity( ACT_CROUCH );
1092
}
1093
}
1094
else if ( speed > 220 )
1095
{
1096
pev->gaitsequence = LookupActivity( ACT_RUN );
1097
}
1098
else if (speed > 0)
1099
{
1100
pev->gaitsequence = LookupActivity( ACT_WALK );
1101
}
1102
else
1103
{
1104
// pev->gaitsequence = LookupActivity( ACT_WALK );
1105
pev->gaitsequence = LookupSequence( "deep_idle" );
1106
}
1107
1108
1109
// Already using the desired animation?
1110
if (pev->sequence == animDesired)
1111
return;
1112
1113
//ALERT( at_console, "Set animation to %d\n", animDesired );
1114
// Reset to first frame of desired animation
1115
pev->sequence = animDesired;
1116
pev->frame = 0;
1117
ResetSequenceInfo( );
1118
}
1119
1120
/*
1121
===========
1122
TabulateAmmo
1123
This function is used to find and store
1124
all the ammo we have into the ammo vars.
1125
============
1126
*/
1127
void CBasePlayer::TabulateAmmo()
1128
{
1129
ammo_9mm = AmmoInventory( GetAmmoIndex( "9mm" ) );
1130
ammo_357 = AmmoInventory( GetAmmoIndex( "357" ) );
1131
ammo_argrens = AmmoInventory( GetAmmoIndex( "ARgrenades" ) );
1132
ammo_bolts = AmmoInventory( GetAmmoIndex( "bolts" ) );
1133
ammo_buckshot = AmmoInventory( GetAmmoIndex( "buckshot" ) );
1134
ammo_rockets = AmmoInventory( GetAmmoIndex( "rockets" ) );
1135
ammo_uranium = AmmoInventory( GetAmmoIndex( "uranium" ) );
1136
ammo_hornets = AmmoInventory( GetAmmoIndex( "Hornets" ) );
1137
}
1138
1139
1140
/*
1141
===========
1142
WaterMove
1143
============
1144
*/
1145
#define AIRTIME 12 // lung full of air lasts this many seconds
1146
1147
void CBasePlayer::WaterMove()
1148
{
1149
int air;
1150
1151
if (pev->movetype == MOVETYPE_NOCLIP)
1152
return;
1153
1154
if (pev->health < 0)
1155
return;
1156
1157
// waterlevel 0 - not in water
1158
// waterlevel 1 - feet in water
1159
// waterlevel 2 - waist in water
1160
// waterlevel 3 - head in water
1161
1162
if (pev->waterlevel != 3)
1163
{
1164
// not underwater
1165
1166
// play 'up for air' sound
1167
if (pev->air_finished < gpGlobals->time)
1168
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade1.wav", 1, ATTN_NORM);
1169
else if (pev->air_finished < gpGlobals->time + 9)
1170
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade2.wav", 1, ATTN_NORM);
1171
1172
pev->air_finished = gpGlobals->time + AIRTIME;
1173
pev->dmg = 2;
1174
1175
// if we took drowning damage, give it back slowly
1176
if (m_idrowndmg > m_idrownrestored)
1177
{
1178
// set drowning damage bit. hack - dmg_drownrecover actually
1179
// makes the time based damage code 'give back' health over time.
1180
// make sure counter is cleared so we start count correctly.
1181
1182
// NOTE: this actually causes the count to continue restarting
1183
// until all drowning damage is healed.
1184
1185
m_bitsDamageType |= DMG_DROWNRECOVER;
1186
m_bitsDamageType &= ~DMG_DROWN;
1187
m_rgbTimeBasedDamage[itbd_DrownRecover] = 0;
1188
}
1189
1190
}
1191
else
1192
{ // fully under water
1193
// stop restoring damage while underwater
1194
m_bitsDamageType &= ~DMG_DROWNRECOVER;
1195
m_rgbTimeBasedDamage[itbd_DrownRecover] = 0;
1196
1197
if (pev->air_finished < gpGlobals->time) // drown!
1198
{
1199
if (pev->pain_finished < gpGlobals->time)
1200
{
1201
// take drowning damage
1202
pev->dmg += 1;
1203
if (pev->dmg > 5)
1204
pev->dmg = 5;
1205
TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), pev->dmg, DMG_DROWN);
1206
pev->pain_finished = gpGlobals->time + 1;
1207
1208
// track drowning damage, give it back when
1209
// player finally takes a breath
1210
1211
m_idrowndmg += static_cast<int>(pev->dmg);
1212
}
1213
}
1214
else
1215
{
1216
m_bitsDamageType &= ~DMG_DROWN;
1217
}
1218
}
1219
1220
if (!pev->waterlevel)
1221
{
1222
if (FBitSet(pev->flags, FL_INWATER))
1223
{
1224
ClearBits(pev->flags, FL_INWATER);
1225
}
1226
return;
1227
}
1228
1229
// make bubbles
1230
1231
air = (int)(pev->air_finished - gpGlobals->time);
1232
if (!RANDOM_LONG(0,0x1f) && RANDOM_LONG(0,AIRTIME-1) >= air)
1233
{
1234
switch (RANDOM_LONG(0,3))
1235
{
1236
case 0: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim1.wav", 0.8, ATTN_NORM); break;
1237
case 1: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim2.wav", 0.8, ATTN_NORM); break;
1238
case 2: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim3.wav", 0.8, ATTN_NORM); break;
1239
case 3: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim4.wav", 0.8, ATTN_NORM); break;
1240
}
1241
}
1242
1243
if (pev->watertype == CONTENT_LAVA) // do damage
1244
{
1245
if (pev->dmgtime < gpGlobals->time)
1246
TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 10 * pev->waterlevel, DMG_BURN);
1247
}
1248
else if (pev->watertype == CONTENT_SLIME) // do damage
1249
{
1250
pev->dmgtime = gpGlobals->time + 1;
1251
TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 4 * pev->waterlevel, DMG_ACID);
1252
}
1253
1254
if (!FBitSet(pev->flags, FL_INWATER))
1255
{
1256
SetBits(pev->flags, FL_INWATER);
1257
pev->dmgtime = 0;
1258
}
1259
}
1260
1261
1262
// TRUE if the player is attached to a ladder
1263
BOOL CBasePlayer::IsOnLadder( void )
1264
{
1265
return ( pev->movetype == MOVETYPE_FLY );
1266
}
1267
1268
void CBasePlayer::PlayerDeathThink(void)
1269
{
1270
float flForward;
1271
1272
if (FBitSet(pev->flags, FL_ONGROUND))
1273
{
1274
flForward = pev->velocity.Length() - 20;
1275
if (flForward <= 0)
1276
pev->velocity = g_vecZero;
1277
else
1278
pev->velocity = flForward * pev->velocity.Normalize();
1279
}
1280
1281
if ( HasWeapons() )
1282
{
1283
// we drop the guns here because weapons that have an area effect and can kill their user
1284
// will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
1285
// player class sometimes is freed. It's safer to manipulate the weapons once we know
1286
// we aren't calling into any of their code anymore through the player pointer.
1287
PackDeadPlayerItems();
1288
}
1289
1290
1291
if (pev->modelindex && (!m_fSequenceFinished) && (pev->deadflag == DEAD_DYING))
1292
{
1293
StudioFrameAdvance( );
1294
1295
m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands
1296
if ( m_iRespawnFrames < 120 ) // Animations should be no longer than this
1297
return;
1298
}
1299
1300
// once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore
1301
// this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn
1302
if ( pev->movetype != MOVETYPE_NONE && FBitSet(pev->flags, FL_ONGROUND) )
1303
pev->movetype = MOVETYPE_NONE;
1304
1305
if (pev->deadflag == DEAD_DYING)
1306
pev->deadflag = DEAD_DEAD;
1307
1308
StopAnimation();
1309
1310
pev->effects |= EF_NOINTERP;
1311
pev->framerate = 0.0;
1312
1313
BOOL fAnyButtonDown = (pev->button & ~IN_SCORE );
1314
1315
// wait for all buttons released
1316
if (pev->deadflag == DEAD_DEAD)
1317
{
1318
if (fAnyButtonDown)
1319
return;
1320
1321
if ( g_pGameRules->FPlayerCanRespawn( this ) )
1322
{
1323
m_fDeadTime = gpGlobals->time;
1324
pev->deadflag = DEAD_RESPAWNABLE;
1325
}
1326
1327
return;
1328
}
1329
1330
// if the player has been dead for one second longer than allowed by forcerespawn,
1331
// forcerespawn isn't on. Send the player off to an intermission camera until they
1332
// choose to respawn.
1333
if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > (m_fDeadTime + 6) ) && !(m_afPhysicsFlags & PFLAG_OBSERVER) )
1334
{
1335
// go to dead camera.
1336
StartDeathCam();
1337
}
1338
1339
if ( pev->iuser1 ) // player is in spectator mode
1340
return;
1341
1342
// wait for any button down, or mp_forcerespawn is set and the respawn time is up
1343
if (!fAnyButtonDown
1344
&& !( g_pGameRules->IsMultiplayer() && forcerespawn.value > 0 && (gpGlobals->time > (m_fDeadTime + 5))) )
1345
return;
1346
1347
pev->button = 0;
1348
m_iRespawnFrames = 0;
1349
1350
//ALERT(at_console, "Respawn\n");
1351
1352
respawn(pev, !(m_afPhysicsFlags & PFLAG_OBSERVER) );// don't copy a corpse if we're in deathcam.
1353
pev->nextthink = -1;
1354
}
1355
1356
//=========================================================
1357
// StartDeathCam - find an intermission spot and send the
1358
// player off into observer mode
1359
//=========================================================
1360
void CBasePlayer::StartDeathCam( void )
1361
{
1362
edict_t *pSpot, *pNewSpot;
1363
int iRand;
1364
1365
if ( pev->view_ofs == g_vecZero )
1366
{
1367
// don't accept subsequent attempts to StartDeathCam()
1368
return;
1369
}
1370
1371
pSpot = FIND_ENTITY_BY_CLASSNAME( NULL, "info_intermission");
1372
1373
if ( !FNullEnt( pSpot ) )
1374
{
1375
// at least one intermission spot in the world.
1376
iRand = RANDOM_LONG( 0, 3 );
1377
1378
while ( iRand > 0 )
1379
{
1380
pNewSpot = FIND_ENTITY_BY_CLASSNAME( pSpot, "info_intermission");
1381
1382
if ( pNewSpot )
1383
{
1384
pSpot = pNewSpot;
1385
}
1386
1387
iRand--;
1388
}
1389
1390
CopyToBodyQue( pev );
1391
1392
UTIL_SetOrigin( pev, pSpot->v.origin );
1393
pev->angles = pev->v_angle = pSpot->v.v_angle;
1394
}
1395
else
1396
{
1397
// no intermission spot. Push them up in the air, looking down at their corpse
1398
TraceResult tr;
1399
CopyToBodyQue( pev );
1400
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, 128 ), ignore_monsters, edict(), &tr );
1401
1402
UTIL_SetOrigin( pev, tr.vecEndPos );
1403
pev->angles = pev->v_angle = UTIL_VecToAngles( tr.vecEndPos - pev->origin );
1404
}
1405
1406
// start death cam
1407
1408
m_afPhysicsFlags |= PFLAG_OBSERVER;
1409
pev->view_ofs = g_vecZero;
1410
pev->fixangle = TRUE;
1411
pev->solid = SOLID_NOT;
1412
pev->takedamage = DAMAGE_NO;
1413
pev->movetype = MOVETYPE_NONE;
1414
pev->modelindex = 0;
1415
}
1416
1417
void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle )
1418
{
1419
// clear any clientside entities attached to this player
1420
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
1421
WRITE_BYTE( TE_KILLPLAYERATTACHMENTS );
1422
WRITE_BYTE( (BYTE)entindex() );
1423
MESSAGE_END();
1424
1425
// Holster weapon immediately, to allow it to cleanup
1426
if (m_pActiveItem)
1427
m_pActiveItem->Holster( );
1428
1429
if ( m_pTank != 0 )
1430
{
1431
m_pTank->Use( this, this, USE_OFF, 0 );
1432
m_pTank = NULL;
1433
}
1434
1435
// clear out the suit message cache so we don't keep chattering
1436
SetSuitUpdate(NULL, FALSE, 0);
1437
1438
// Tell Ammo Hud that the player is dead
1439
MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev );
1440
WRITE_BYTE(0);
1441
WRITE_BYTE(0XFF);
1442
WRITE_BYTE(0xFF);
1443
MESSAGE_END();
1444
1445
// reset FOV
1446
m_iFOV = m_iClientFOV = 0;
1447
pev->fov = m_iFOV;
1448
MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev );
1449
WRITE_BYTE(0);
1450
MESSAGE_END();
1451
1452
// Setup flags
1453
m_iHideHUD = (HIDEHUD_HEALTH | HIDEHUD_WEAPONS);
1454
m_afPhysicsFlags |= PFLAG_OBSERVER;
1455
pev->effects = EF_NODRAW;
1456
pev->view_ofs = g_vecZero;
1457
pev->angles = pev->v_angle = vecViewAngle;
1458
pev->fixangle = TRUE;
1459
pev->solid = SOLID_NOT;
1460
pev->takedamage = DAMAGE_NO;
1461
pev->movetype = MOVETYPE_NONE;
1462
ClearBits( m_afPhysicsFlags, PFLAG_DUCKING );
1463
ClearBits( pev->flags, FL_DUCKING );
1464
pev->deadflag = DEAD_RESPAWNABLE;
1465
pev->health = 1;
1466
1467
// Clear out the status bar
1468
m_fInitHUD = TRUE;
1469
1470
pev->team = 0;
1471
MESSAGE_BEGIN( MSG_ALL, gmsgTeamInfo );
1472
WRITE_BYTE( ENTINDEX(edict()) );
1473
WRITE_STRING( "" );
1474
MESSAGE_END();
1475
1476
// Remove all the player's stuff
1477
RemoveAllItems( FALSE );
1478
1479
// Move them to the new position
1480
UTIL_SetOrigin( pev, vecPosition );
1481
1482
// Find a player to watch
1483
m_flNextObserverInput = 0;
1484
Observer_SetMode( m_iObserverLastMode );
1485
}
1486
1487
//
1488
// PlayerUse - handles USE keypress
1489
//
1490
#define PLAYER_SEARCH_RADIUS (float)64
1491
1492
void CBasePlayer::PlayerUse ( void )
1493
{
1494
if ( IsObserver() )
1495
return;
1496
1497
// Was use pressed or released?
1498
if ( ! ((pev->button | m_afButtonPressed | m_afButtonReleased) & IN_USE) )
1499
return;
1500
1501
// Hit Use on a train?
1502
if ( m_afButtonPressed & IN_USE )
1503
{
1504
if ( m_pTank != 0 )
1505
{
1506
// Stop controlling the tank
1507
// TODO: Send HUD Update
1508
m_pTank->Use( this, this, USE_OFF, 0 );
1509
m_pTank = NULL;
1510
return;
1511
}
1512
else
1513
{
1514
if ( m_afPhysicsFlags & PFLAG_ONTRAIN )
1515
{
1516
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
1517
m_iTrain = TRAIN_NEW|TRAIN_OFF;
1518
return;
1519
}
1520
else
1521
{ // Start controlling the train!
1522
CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity );
1523
1524
if ( pTrain && !(pev->button & IN_JUMP) && FBitSet(pev->flags, FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(pev) )
1525
{
1526
m_afPhysicsFlags |= PFLAG_ONTRAIN;
1527
m_iTrain = TrainSpeed(static_cast<int>(pTrain->pev->speed), pTrain->pev->impulse);
1528
m_iTrain |= TRAIN_NEW;
1529
EMIT_SOUND( ENT(pev), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM);
1530
return;
1531
}
1532
}
1533
}
1534
}
1535
1536
CBaseEntity *pObject = NULL;
1537
CBaseEntity *pClosest = NULL;
1538
Vector vecLOS;
1539
float flMaxDot = VIEW_FIELD_NARROW;
1540
float flDot;
1541
1542
UTIL_MakeVectors ( pev->v_angle );// so we know which way we are facing
1543
1544
while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL)
1545
{
1546
1547
if (pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE))
1548
{
1549
// !!!PERFORMANCE- should this check be done on a per case basis AFTER we've determined that
1550
// this object is actually usable? This dot is being done for every object within PLAYER_SEARCH_RADIUS
1551
// when player hits the use key. How many objects can be in that area, anyway? (sjb)
1552
vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs));
1553
1554
// This essentially moves the origin of the target to the corner nearest the player to test to see
1555
// if it's "hull" is in the view cone
1556
vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 );
1557
1558
flDot = DotProduct (vecLOS , gpGlobals->v_forward);
1559
if (flDot > flMaxDot )
1560
{// only if the item is in front of the user
1561
pClosest = pObject;
1562
flMaxDot = flDot;
1563
// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot );
1564
}
1565
// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot );
1566
}
1567
}
1568
pObject = pClosest;
1569
1570
// Found an object
1571
if (pObject )
1572
{
1573
//!!!UNDONE: traceline here to prevent USEing buttons through walls
1574
int caps = pObject->ObjectCaps();
1575
1576
if ( m_afButtonPressed & IN_USE )
1577
EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM);
1578
1579
if ( ( (pev->button & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) ||
1580
( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) )
1581
{
1582
if ( caps & FCAP_CONTINUOUS_USE )
1583
m_afPhysicsFlags |= PFLAG_USING;
1584
1585
pObject->Use( this, this, USE_SET, 1 );
1586
}
1587
// UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away
1588
else if ( (m_afButtonReleased & IN_USE) && (pObject->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use
1589
{
1590
pObject->Use( this, this, USE_SET, 0 );
1591
}
1592
}
1593
else
1594
{
1595
if ( m_afButtonPressed & IN_USE )
1596
EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_denyselect.wav", 0.4, ATTN_NORM);
1597
}
1598
}
1599
1600
1601
1602
void CBasePlayer::Jump()
1603
{
1604
Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping
1605
Vector vecAdjustedVelocity;
1606
Vector vecSpot;
1607
TraceResult tr;
1608
1609
if (FBitSet(pev->flags, FL_WATERJUMP))
1610
return;
1611
1612
if (pev->waterlevel >= 2)
1613
{
1614
return;
1615
}
1616
1617
// jump velocity is sqrt( height * gravity * 2)
1618
1619
// If this isn't the first frame pressing the jump button, break out.
1620
if ( !FBitSet( m_afButtonPressed, IN_JUMP ) )
1621
return; // don't pogo stick
1622
1623
if ( !(pev->flags & FL_ONGROUND) || !pev->groundentity )
1624
{
1625
return;
1626
}
1627
1628
// many features in this function use v_forward, so makevectors now.
1629
UTIL_MakeVectors (pev->angles);
1630
1631
// ClearBits(pev->flags, FL_ONGROUND); // don't stairwalk
1632
1633
SetAnimation( PLAYER_JUMP );
1634
1635
if ( m_fLongJump &&
1636
(pev->button & IN_DUCK) &&
1637
( pev->flDuckTime > 0 ) &&
1638
pev->velocity.Length() > 50 )
1639
{
1640
SetAnimation( PLAYER_SUPERJUMP );
1641
}
1642
1643
// If you're standing on a conveyor, add it's velocity to yours (for momentum)
1644
entvars_t *pevGround = VARS(pev->groundentity);
1645
if ( pevGround && (pevGround->flags & FL_CONVEYOR) )
1646
{
1647
pev->velocity = pev->velocity + pev->basevelocity;
1648
}
1649
}
1650
1651
1652
1653
// This is a glorious hack to find free space when you've crouched into some solid space
1654
// Our crouching collisions do not work correctly for some reason and this is easier
1655
// than fixing the problem :(
1656
void FixPlayerCrouchStuck( edict_t *pPlayer )
1657
{
1658
TraceResult trace;
1659
1660
// Move up as many as 18 pixels if the player is stuck.
1661
for ( int i = 0; i < 18; i++ )
1662
{
1663
UTIL_TraceHull( pPlayer->v.origin, pPlayer->v.origin, dont_ignore_monsters, head_hull, pPlayer, &trace );
1664
if ( trace.fStartSolid )
1665
pPlayer->v.origin.z ++;
1666
else
1667
break;
1668
}
1669
}
1670
1671
void CBasePlayer::Duck( )
1672
{
1673
if (pev->button & IN_DUCK)
1674
{
1675
if ( m_IdealActivity != ACT_LEAP )
1676
{
1677
SetAnimation( PLAYER_WALK );
1678
}
1679
}
1680
}
1681
1682
//
1683
// ID's player as such.
1684
//
1685
int CBasePlayer::Classify ( void )
1686
{
1687
return CLASS_PLAYER;
1688
}
1689
1690
1691
void CBasePlayer::AddPoints( int score, BOOL bAllowNegativeScore )
1692
{
1693
// Positive score always adds
1694
if ( score < 0 )
1695
{
1696
if ( !bAllowNegativeScore )
1697
{
1698
if ( pev->frags < 0 ) // Can't go more negative
1699
return;
1700
1701
if ( -score > pev->frags ) // Will this go negative?
1702
{
1703
score = static_cast<int>(-pev->frags); // Sum will be 0
1704
}
1705
}
1706
}
1707
1708
pev->frags += score;
1709
1710
MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
1711
WRITE_BYTE( ENTINDEX(edict()) );
1712
WRITE_SHORT( static_cast<int>(pev->frags) );
1713
WRITE_SHORT( m_iDeaths );
1714
WRITE_SHORT( 0 );
1715
WRITE_SHORT( g_pGameRules->GetTeamIndex( m_szTeamName ) + 1 );
1716
MESSAGE_END();
1717
}
1718
1719
1720
void CBasePlayer::AddPointsToTeam( int score, BOOL bAllowNegativeScore )
1721
{
1722
int index = entindex();
1723
1724
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
1725
{
1726
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
1727
1728
if ( pPlayer && i != index )
1729
{
1730
if ( g_pGameRules->PlayerRelationship( this, pPlayer ) == GR_TEAMMATE )
1731
{
1732
pPlayer->AddPoints( score, bAllowNegativeScore );
1733
}
1734
}
1735
}
1736
}
1737
1738
//Player ID
1739
void CBasePlayer::InitStatusBar()
1740
{
1741
m_flStatusBarDisappearDelay = 0;
1742
m_SbarString1[0] = m_SbarString0[0] = 0;
1743
}
1744
1745
void CBasePlayer::UpdateStatusBar()
1746
{
1747
int newSBarState[ SBAR_END ];
1748
char sbuf0[ SBAR_STRING_SIZE ];
1749
char sbuf1[ SBAR_STRING_SIZE ];
1750
1751
memset( newSBarState, 0, sizeof(newSBarState) );
1752
strcpy( sbuf0, m_SbarString0 );
1753
strcpy( sbuf1, m_SbarString1 );
1754
1755
// Find an ID Target
1756
TraceResult tr;
1757
UTIL_MakeVectors( pev->v_angle + pev->punchangle );
1758
Vector vecSrc = EyePosition();
1759
Vector vecEnd = vecSrc + (gpGlobals->v_forward * MAX_ID_RANGE);
1760
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, edict(), &tr);
1761
1762
if (tr.flFraction != 1.0)
1763
{
1764
if ( !FNullEnt( tr.pHit ) )
1765
{
1766
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
1767
1768
if (pEntity->Classify() == CLASS_PLAYER )
1769
{
1770
newSBarState[ SBAR_ID_TARGETNAME ] = ENTINDEX( pEntity->edict() );
1771
strcpy( sbuf1, "1 %p1\n2 Health: %i2%%\n3 Armor: %i3%%" );
1772
1773
// allies and medics get to see the targets health
1774
if ( g_pGameRules->PlayerRelationship( this, pEntity ) == GR_TEAMMATE )
1775
{
1776
newSBarState[ SBAR_ID_TARGETHEALTH ] = static_cast<int>(100 * (pEntity->pev->health / pEntity->pev->max_health));
1777
newSBarState[ SBAR_ID_TARGETARMOR ] = static_cast<int>(pEntity->pev->armorvalue); //No need to get it % based since 100 it's the max.
1778
}
1779
1780
m_flStatusBarDisappearDelay = gpGlobals->time + 1.0;
1781
}
1782
}
1783
else if ( m_flStatusBarDisappearDelay > gpGlobals->time )
1784
{
1785
// hold the values for a short amount of time after viewing the object
1786
newSBarState[ SBAR_ID_TARGETNAME ] = m_izSBarState[ SBAR_ID_TARGETNAME ];
1787
newSBarState[ SBAR_ID_TARGETHEALTH ] = m_izSBarState[ SBAR_ID_TARGETHEALTH ];
1788
newSBarState[ SBAR_ID_TARGETARMOR ] = m_izSBarState[ SBAR_ID_TARGETARMOR ];
1789
}
1790
}
1791
1792
BOOL bForceResend = FALSE;
1793
1794
if ( strcmp( sbuf0, m_SbarString0 ) )
1795
{
1796
MESSAGE_BEGIN( MSG_ONE, gmsgStatusText, NULL, pev );
1797
WRITE_BYTE( 0 );
1798
WRITE_STRING( sbuf0 );
1799
MESSAGE_END();
1800
1801
strcpy( m_SbarString0, sbuf0 );
1802
1803
// make sure everything's resent
1804
bForceResend = TRUE;
1805
}
1806
1807
if ( strcmp( sbuf1, m_SbarString1 ) )
1808
{
1809
MESSAGE_BEGIN( MSG_ONE, gmsgStatusText, NULL, pev );
1810
WRITE_BYTE( 1 );
1811
WRITE_STRING( sbuf1 );
1812
MESSAGE_END();
1813
1814
strcpy( m_SbarString1, sbuf1 );
1815
1816
// make sure everything's resent
1817
bForceResend = TRUE;
1818
}
1819
1820
// Check values and send if they don't match
1821
for (int i = 1; i < SBAR_END; i++)
1822
{
1823
if ( newSBarState[i] != m_izSBarState[i] || bForceResend )
1824
{
1825
MESSAGE_BEGIN( MSG_ONE, gmsgStatusValue, NULL, pev );
1826
WRITE_BYTE( i );
1827
WRITE_SHORT( newSBarState[i] );
1828
MESSAGE_END();
1829
1830
m_izSBarState[i] = newSBarState[i];
1831
}
1832
}
1833
}
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
#define CLIMB_SHAKE_FREQUENCY 22 // how many frames in between screen shakes when climbing
1844
#define MAX_CLIMB_SPEED 200 // fastest vertical climbing speed possible
1845
#define CLIMB_SPEED_DEC 15 // climbing deceleration rate
1846
#define CLIMB_PUNCH_X -7 // how far to 'punch' client X axis when climbing
1847
#define CLIMB_PUNCH_Z 7 // how far to 'punch' client Z axis when climbing
1848
1849
void CBasePlayer::PreThink(void)
1850
{
1851
int buttonsChanged = (m_afButtonLast ^ pev->button); // These buttons have changed this frame
1852
1853
// Debounced button codes for pressed/released
1854
// UNDONE: Do we need auto-repeat?
1855
m_afButtonPressed = buttonsChanged & pev->button; // The changed ones still down are "pressed"
1856
m_afButtonReleased = buttonsChanged & (~pev->button); // The ones not down are "released"
1857
1858
g_pGameRules->PlayerThink( this );
1859
1860
if ( g_fGameOver )
1861
return; // intermission or finale
1862
1863
UTIL_MakeVectors(pev->v_angle); // is this still used?
1864
1865
ItemPreFrame( );
1866
WaterMove();
1867
1868
if ( g_pGameRules && g_pGameRules->FAllowFlashlight() )
1869
m_iHideHUD &= ~HIDEHUD_FLASHLIGHT;
1870
else
1871
m_iHideHUD |= HIDEHUD_FLASHLIGHT;
1872
1873
1874
// JOHN: checks if new client data (for HUD and view control) needs to be sent to the client
1875
UpdateClientData();
1876
1877
CheckTimeBasedDamage();
1878
1879
CheckSuitUpdate();
1880
1881
// Observer Button Handling
1882
if ( IsObserver() )
1883
{
1884
Observer_HandleButtons();
1885
Observer_CheckTarget();
1886
Observer_CheckProperties();
1887
pev->impulse = 0;
1888
return;
1889
}
1890
1891
if (pev->deadflag >= DEAD_DYING)
1892
{
1893
PlayerDeathThink();
1894
return;
1895
}
1896
1897
// So the correct flags get sent to client asap.
1898
//
1899
if ( m_afPhysicsFlags & PFLAG_ONTRAIN )
1900
pev->flags |= FL_ONTRAIN;
1901
else
1902
pev->flags &= ~FL_ONTRAIN;
1903
1904
// Train speed control
1905
if ( m_afPhysicsFlags & PFLAG_ONTRAIN )
1906
{
1907
CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity );
1908
float vel;
1909
1910
if ( !pTrain )
1911
{
1912
TraceResult trainTrace;
1913
// Maybe this is on the other side of a level transition
1914
UTIL_TraceLine( pev->origin, pev->origin + Vector(0,0,-38), ignore_monsters, ENT(pev), &trainTrace );
1915
1916
// HACKHACK - Just look for the func_tracktrain classname
1917
if ( trainTrace.flFraction != 1.0 && trainTrace.pHit )
1918
pTrain = CBaseEntity::Instance( trainTrace.pHit );
1919
1920
1921
if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(pev) )
1922
{
1923
//ALERT( at_error, "In train mode with no train!\n" );
1924
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
1925
m_iTrain = TRAIN_NEW|TRAIN_OFF;
1926
return;
1927
}
1928
}
1929
else if ( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || (pev->button & (IN_MOVELEFT|IN_MOVERIGHT) ) )
1930
{
1931
// Turn off the train if you jump, strafe, or the train controls go dead
1932
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
1933
m_iTrain = TRAIN_NEW|TRAIN_OFF;
1934
return;
1935
}
1936
1937
pev->velocity = g_vecZero;
1938
vel = 0;
1939
if ( m_afButtonPressed & IN_FORWARD )
1940
{
1941
vel = 1;
1942
pTrain->Use( this, this, USE_SET, (float)vel );
1943
}
1944
else if ( m_afButtonPressed & IN_BACK )
1945
{
1946
vel = -1;
1947
pTrain->Use( this, this, USE_SET, (float)vel );
1948
}
1949
1950
if (vel)
1951
{
1952
m_iTrain = TrainSpeed(static_cast<int>(pTrain->pev->speed), pTrain->pev->impulse);
1953
m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW;
1954
}
1955
1956
} else if (m_iTrain & TRAIN_ACTIVE)
1957
m_iTrain = TRAIN_NEW; // turn off train
1958
1959
if (pev->button & IN_JUMP)
1960
{
1961
// If on a ladder, jump off the ladder
1962
// else Jump
1963
Jump();
1964
}
1965
1966
1967
// If trying to duck, already ducked, or in the process of ducking
1968
if ((pev->button & IN_DUCK) || FBitSet(pev->flags,FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) )
1969
Duck();
1970
1971
if ( !FBitSet ( pev->flags, FL_ONGROUND ) )
1972
{
1973
m_flFallVelocity = -pev->velocity.z;
1974
}
1975
1976
// StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating?
1977
1978
// Clear out ladder pointer
1979
m_hEnemy = NULL;
1980
1981
if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
1982
{
1983
pev->velocity = g_vecZero;
1984
}
1985
}
1986
/* Time based Damage works as follows:
1987
1) There are several types of timebased damage:
1988
1989
#define DMG_PARALYZE (1 << 14) // slows affected creature down
1990
#define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad
1991
#define DMG_POISON (1 << 16) // blood poisioning
1992
#define DMG_RADIATION (1 << 17) // radiation exposure
1993
#define DMG_DROWNRECOVER (1 << 18) // drown recovery
1994
#define DMG_ACID (1 << 19) // toxic chemicals or acid burns
1995
#define DMG_SLOWBURN (1 << 20) // in an oven
1996
#define DMG_SLOWFREEZE (1 << 21) // in a subzero freezer
1997
1998
2) A new hit inflicting tbd restarts the tbd counter - each monster has an 8bit counter,
1999
per damage type. The counter is decremented every second, so the maximum time
2000
an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius
2001
of a damaging effect like fire, nervegas, radiation will continually reset the counter to max.
2002
2003
3) Every second that a tbd counter is running, the player takes damage. The damage
2004
is determined by the type of tdb.
2005
Paralyze - 1/2 movement rate, 30 second duration.
2006
Nervegas - 5 points per second, 16 second duration = 80 points max dose.
2007
Poison - 2 points per second, 25 second duration = 50 points max dose.
2008
Radiation - 1 point per second, 50 second duration = 50 points max dose.
2009
Drown - 5 points per second, 2 second duration.
2010
Acid/Chemical - 5 points per second, 10 second duration = 50 points max.
2011
Burn - 10 points per second, 2 second duration.
2012
Freeze - 3 points per second, 10 second duration = 30 points max.
2013
2014
4) Certain actions or countermeasures counteract the damaging effects of tbds:
2015
2016
Armor/Heater/Cooler - Chemical(acid),burn, freeze all do damage to armor power, then to body
2017
- recharged by suit recharger
2018
Air In Lungs - drowning damage is done to air in lungs first, then to body
2019
- recharged by poking head out of water
2020
- 10 seconds if swiming fast
2021
Air In SCUBA - drowning damage is done to air in tanks first, then to body
2022
- 2 minutes in tanks. Need new tank once empty.
2023
Radiation Syringe - Each syringe full provides protection vs one radiation dosage
2024
Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison).
2025
Health kit - Immediate stop to acid/chemical, fire or freeze damage.
2026
Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage.
2027
2028
2029
*/
2030
2031
// If player is taking time based damage, continue doing damage to player -
2032
// this simulates the effect of being poisoned, gassed, dosed with radiation etc -
2033
// anything that continues to do damage even after the initial contact stops.
2034
// Update all time based damage counters, and shut off any that are done.
2035
2036
// The m_bitsDamageType bit MUST be set if any damage is to be taken.
2037
// This routine will detect the initial on value of the m_bitsDamageType
2038
// and init the appropriate counter. Only processes damage every second.
2039
2040
//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage
2041
//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval
2042
2043
//#define NERVEGAS_DURATION 16
2044
//#define NERVEGAS_DAMAGE 5.0
2045
2046
//#define POISON_DURATION 25
2047
//#define POISON_DAMAGE 2.0
2048
2049
//#define RADIATION_DURATION 50
2050
//#define RADIATION_DAMAGE 1.0
2051
2052
//#define ACID_DURATION 10
2053
//#define ACID_DAMAGE 5.0
2054
2055
//#define SLOWBURN_DURATION 2
2056
//#define SLOWBURN_DAMAGE 1.0
2057
2058
//#define SLOWFREEZE_DURATION 1.0
2059
//#define SLOWFREEZE_DAMAGE 3.0
2060
2061
/* */
2062
2063
2064
void CBasePlayer::CheckTimeBasedDamage()
2065
{
2066
int i;
2067
BYTE bDuration = 0;
2068
2069
if (!(m_bitsDamageType & DMG_TIMEBASED))
2070
return;
2071
2072
// only check for time based damage approx. every 2 seconds
2073
if (abs(static_cast<int>(gpGlobals->time - m_tbdPrev)) < 2.0)
2074
return;
2075
2076
m_tbdPrev = gpGlobals->time;
2077
2078
for (i = 0; i < CDMG_TIMEBASED; i++)
2079
{
2080
// make sure bit is set for damage type
2081
if (m_bitsDamageType & (DMG_PARALYZE << i))
2082
{
2083
switch (i)
2084
{
2085
case itbd_Paralyze:
2086
// UNDONE - flag movement as half-speed
2087
bDuration = PARALYZE_DURATION;
2088
break;
2089
case itbd_NerveGas:
2090
// TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC);
2091
bDuration = NERVEGAS_DURATION;
2092
break;
2093
case itbd_Poison:
2094
TakeDamage(pev, pev, POISON_DAMAGE, DMG_GENERIC);
2095
bDuration = POISON_DURATION;
2096
break;
2097
case itbd_Radiation:
2098
// TakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC);
2099
bDuration = RADIATION_DURATION;
2100
break;
2101
case itbd_DrownRecover:
2102
// NOTE: this hack is actually used to RESTORE health
2103
// after the player has been drowning and finally takes a breath
2104
if (m_idrowndmg > m_idrownrestored)
2105
{
2106
int idif = min(m_idrowndmg - m_idrownrestored, 10);
2107
2108
TakeHealth(idif, DMG_GENERIC);
2109
m_idrownrestored += idif;
2110
}
2111
bDuration = 4; // get up to 5*10 = 50 points back
2112
break;
2113
case itbd_Acid:
2114
// TakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC);
2115
bDuration = ACID_DURATION;
2116
break;
2117
case itbd_SlowBurn:
2118
// TakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC);
2119
bDuration = SLOWBURN_DURATION;
2120
break;
2121
case itbd_SlowFreeze:
2122
// TakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC);
2123
bDuration = SLOWFREEZE_DURATION;
2124
break;
2125
default:
2126
bDuration = 0;
2127
}
2128
2129
if (m_rgbTimeBasedDamage[i])
2130
{
2131
// use up an antitoxin on poison or nervegas after a few seconds of damage
2132
if (((i == itbd_NerveGas) && (m_rgbTimeBasedDamage[i] < NERVEGAS_DURATION)) ||
2133
((i == itbd_Poison) && (m_rgbTimeBasedDamage[i] < POISON_DURATION)))
2134
{
2135
if (m_rgItems[ITEM_ANTIDOTE])
2136
{
2137
m_rgbTimeBasedDamage[i] = 0;
2138
m_rgItems[ITEM_ANTIDOTE]--;
2139
SetSuitUpdate("!HEV_HEAL4", FALSE, SUIT_REPEAT_OK);
2140
}
2141
}
2142
2143
2144
// decrement damage duration, detect when done.
2145
if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0)
2146
{
2147
m_rgbTimeBasedDamage[i] = 0;
2148
// if we're done, clear damage bits
2149
m_bitsDamageType &= ~(DMG_PARALYZE << i);
2150
}
2151
}
2152
else
2153
// first time taking this damage type - init damage duration
2154
m_rgbTimeBasedDamage[i] = bDuration;
2155
}
2156
}
2157
}
2158
2159
/*
2160
THE POWER SUIT
2161
2162
The Suit provides 3 main functions: Protection, Notification and Augmentation.
2163
Some functions are automatic, some require power.
2164
The player gets the suit shortly after getting off the train in C1A0 and it stays
2165
with him for the entire game.
2166
2167
Protection
2168
2169
Heat/Cold
2170
When the player enters a hot/cold area, the heating/cooling indicator on the suit
2171
will come on and the battery will drain while the player stays in the area.
2172
After the battery is dead, the player starts to take damage.
2173
This feature is built into the suit and is automatically engaged.
2174
Radiation Syringe
2175
This will cause the player to be immune from the effects of radiation for N seconds. Single use item.
2176
Anti-Toxin Syringe
2177
This will cure the player from being poisoned. Single use item.
2178
Health
2179
Small (1st aid kits, food, etc.)
2180
Large (boxes on walls)
2181
Armor
2182
The armor works using energy to create a protective field that deflects a
2183
percentage of damage projectile and explosive attacks. After the armor has been deployed,
2184
it will attempt to recharge itself to full capacity with the energy reserves from the battery.
2185
It takes the armor N seconds to fully charge.
2186
2187
Notification (via the HUD)
2188
2189
x Health
2190
x Ammo
2191
x Automatic Health Care
2192
Notifies the player when automatic healing has been engaged.
2193
x Geiger counter
2194
Classic Geiger counter sound and status bar at top of HUD
2195
alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal.
2196
x Poison
2197
Armor
2198
Displays the current level of armor.
2199
2200
Augmentation
2201
2202
Reanimation (w/adrenaline)
2203
Causes the player to come back to life after he has been dead for 3 seconds.
2204
Will not work if player was gibbed. Single use.
2205
Long Jump
2206
Used by hitting the ??? key(s). Caused the player to further than normal.
2207
SCUBA
2208
Used automatically after picked up and after player enters the water.
2209
Works for N seconds. Single use.
2210
2211
Things powered by the battery
2212
2213
Armor
2214
Uses N watts for every M units of damage.
2215
Heat/Cool
2216
Uses N watts for every second in hot/cold area.
2217
Long Jump
2218
Uses N watts for every jump.
2219
Alien Cloak
2220
Uses N watts for each use. Each use lasts M seconds.
2221
Alien Shield
2222
Augments armor. Reduces Armor drain by one half
2223
2224
*/
2225
2226
// if in range of radiation source, ping geiger counter
2227
2228
#define GEIGERDELAY 0.25
2229
2230
void CBasePlayer :: UpdateGeigerCounter( void )
2231
{
2232
BYTE range;
2233
2234
// delay per update ie: don't flood net with these msgs
2235
if (gpGlobals->time < m_flgeigerDelay)
2236
return;
2237
2238
m_flgeigerDelay = gpGlobals->time + GEIGERDELAY;
2239
2240
// send range to radition source to client
2241
2242
range = (BYTE) (m_flgeigerRange / 4);
2243
2244
if (range != m_igeigerRangePrev)
2245
{
2246
m_igeigerRangePrev = range;
2247
2248
MESSAGE_BEGIN( MSG_ONE, gmsgGeigerRange, NULL, pev );
2249
WRITE_BYTE( range );
2250
MESSAGE_END();
2251
}
2252
2253
// reset counter and semaphore
2254
if (!RANDOM_LONG(0,3))
2255
m_flgeigerRange = 1000;
2256
2257
}
2258
2259
/*
2260
================
2261
CheckSuitUpdate
2262
2263
Play suit update if it's time
2264
================
2265
*/
2266
2267
#define SUITUPDATETIME 3.5
2268
#define SUITFIRSTUPDATETIME 0.1
2269
2270
void CBasePlayer::CheckSuitUpdate()
2271
{
2272
int i;
2273
int isentence = 0;
2274
int isearch = m_iSuitPlayNext;
2275
2276
// Ignore suit updates if no suit
2277
if ( !(pev->weapons & (1<<WEAPON_SUIT)) )
2278
return;
2279
2280
// if in range of radiation source, ping geiger counter
2281
UpdateGeigerCounter();
2282
2283
if ( g_pGameRules->IsMultiplayer() )
2284
{
2285
// don't bother updating HEV voice in multiplayer.
2286
return;
2287
}
2288
2289
if ( gpGlobals->time >= m_flSuitUpdate && m_flSuitUpdate > 0)
2290
{
2291
// play a sentence off of the end of the queue
2292
for (i = 0; i < CSUITPLAYLIST; i++)
2293
{
2294
if ((isentence = m_rgSuitPlayList[isearch]))
2295
break;
2296
2297
if (++isearch == CSUITPLAYLIST)
2298
isearch = 0;
2299
}
2300
2301
if (isentence)
2302
{
2303
m_rgSuitPlayList[isearch] = 0;
2304
if (isentence > 0)
2305
{
2306
// play sentence number
2307
2308
char sentence[CBSENTENCENAME_MAX+1];
2309
strcpy(sentence, "!");
2310
strcat(sentence, gszallsentencenames[isentence]);
2311
EMIT_SOUND_SUIT(ENT(pev), sentence);
2312
}
2313
else
2314
{
2315
// play sentence group
2316
EMIT_GROUPID_SUIT(ENT(pev), -isentence);
2317
}
2318
m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME;
2319
}
2320
else
2321
// queue is empty, don't check
2322
m_flSuitUpdate = 0;
2323
}
2324
}
2325
2326
// add sentence to suit playlist queue. if fgroup is true, then
2327
// name is a sentence group (HEV_AA), otherwise name is a specific
2328
// sentence name ie: !HEV_AA0. If iNoRepeat is specified in
2329
// seconds, then we won't repeat playback of this word or sentence
2330
// for at least that number of seconds.
2331
2332
void CBasePlayer::SetSuitUpdate(const char *name, int fgroup, int iNoRepeatTime)
2333
{
2334
int i;
2335
int isentence;
2336
int iempty = -1;
2337
2338
2339
// Ignore suit updates if no suit
2340
if ( !(pev->weapons & (1<<WEAPON_SUIT)) )
2341
return;
2342
2343
if ( g_pGameRules->IsMultiplayer() )
2344
{
2345
// due to static channel design, etc. We don't play HEV sounds in multiplayer right now.
2346
return;
2347
}
2348
2349
// if name == NULL, then clear out the queue
2350
2351
if (!name)
2352
{
2353
for (i = 0; i < CSUITPLAYLIST; i++)
2354
m_rgSuitPlayList[i] = 0;
2355
return;
2356
}
2357
// get sentence or group number
2358
if (!fgroup)
2359
{
2360
isentence = SENTENCEG_Lookup(name, NULL);
2361
if (isentence < 0)
2362
return;
2363
}
2364
else
2365
// mark group number as negative
2366
isentence = -SENTENCEG_GetIndex(name);
2367
2368
// check norepeat list - this list lets us cancel
2369
// the playback of words or sentences that have already
2370
// been played within a certain time.
2371
2372
for (i = 0; i < CSUITNOREPEAT; i++)
2373
{
2374
if (isentence == m_rgiSuitNoRepeat[i])
2375
{
2376
// this sentence or group is already in
2377
// the norepeat list
2378
2379
if (m_rgflSuitNoRepeatTime[i] < gpGlobals->time)
2380
{
2381
// norepeat time has expired, clear it out
2382
m_rgiSuitNoRepeat[i] = 0;
2383
m_rgflSuitNoRepeatTime[i] = 0.0;
2384
iempty = i;
2385
break;
2386
}
2387
else
2388
{
2389
// don't play, still marked as norepeat
2390
return;
2391
}
2392
}
2393
// keep track of empty slot
2394
if (!m_rgiSuitNoRepeat[i])
2395
iempty = i;
2396
}
2397
2398
// sentence is not in norepeat list, save if norepeat time was given
2399
2400
if (iNoRepeatTime)
2401
{
2402
if (iempty < 0)
2403
iempty = RANDOM_LONG(0, CSUITNOREPEAT-1); // pick random slot to take over
2404
m_rgiSuitNoRepeat[iempty] = isentence;
2405
m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->time;
2406
}
2407
2408
// find empty spot in queue, or overwrite last spot
2409
2410
m_rgSuitPlayList[m_iSuitPlayNext++] = isentence;
2411
if (m_iSuitPlayNext == CSUITPLAYLIST)
2412
m_iSuitPlayNext = 0;
2413
2414
if (m_flSuitUpdate <= gpGlobals->time)
2415
{
2416
if (m_flSuitUpdate == 0)
2417
// play queue is empty, don't delay too long before playback
2418
m_flSuitUpdate = gpGlobals->time + SUITFIRSTUPDATETIME;
2419
else
2420
m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME;
2421
}
2422
2423
}
2424
2425
/*
2426
================
2427
CheckPowerups
2428
2429
Check for turning off powerups
2430
2431
GLOBALS ASSUMED SET: g_ulModelIndexPlayer
2432
================
2433
*/
2434
static void
2435
CheckPowerups(entvars_t *pev)
2436
{
2437
if (pev->health <= 0)
2438
return;
2439
2440
pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
2441
}
2442
2443
2444
//=========================================================
2445
// UpdatePlayerSound - updates the position of the player's
2446
// reserved sound slot in the sound list.
2447
//=========================================================
2448
void CBasePlayer :: UpdatePlayerSound ( void )
2449
{
2450
int iBodyVolume;
2451
int iVolume;
2452
CSound *pSound;
2453
2454
pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt :: ClientSoundIndex( edict() ) );
2455
2456
if ( !pSound )
2457
{
2458
ALERT ( at_console, "Client lost reserved sound!\n" );
2459
return;
2460
}
2461
2462
pSound->m_iType = bits_SOUND_NONE;
2463
2464
// now calculate the best target volume for the sound. If the player's weapon
2465
// is louder than his body/movement, use the weapon volume, else, use the body volume.
2466
2467
if ( FBitSet ( pev->flags, FL_ONGROUND ) )
2468
{
2469
iBodyVolume = static_cast<int>(pev->velocity.Length());
2470
2471
// clamp the noise that can be made by the body, in case a push trigger,
2472
// weapon recoil, or anything shoves the player abnormally fast.
2473
if ( iBodyVolume > 512 )
2474
{
2475
iBodyVolume = 512;
2476
}
2477
}
2478
else
2479
{
2480
iBodyVolume = 0;
2481
}
2482
2483
if ( pev->button & IN_JUMP )
2484
{
2485
iBodyVolume += 100;
2486
}
2487
2488
// convert player move speed and actions into sound audible by monsters.
2489
if ( m_iWeaponVolume > iBodyVolume )
2490
{
2491
m_iTargetVolume = m_iWeaponVolume;
2492
2493
// OR in the bits for COMBAT sound if the weapon is being louder than the player.
2494
pSound->m_iType |= bits_SOUND_COMBAT;
2495
}
2496
else
2497
{
2498
m_iTargetVolume = iBodyVolume;
2499
}
2500
2501
// decay weapon volume over time so bits_SOUND_COMBAT stays set for a while
2502
m_iWeaponVolume -= static_cast<int>(250 * gpGlobals->frametime);
2503
if ( m_iWeaponVolume < 0 )
2504
{
2505
iVolume = 0;
2506
}
2507
2508
2509
// if target volume is greater than the player sound's current volume, we paste the new volume in
2510
// immediately. If target is less than the current volume, current volume is not set immediately to the
2511
// lower volume, rather works itself towards target volume over time. This gives monsters a much better chance
2512
// to hear a sound, especially if they don't listen every frame.
2513
iVolume = pSound->m_iVolume;
2514
2515
if ( m_iTargetVolume > iVolume )
2516
{
2517
iVolume = m_iTargetVolume;
2518
}
2519
else if ( iVolume > m_iTargetVolume )
2520
{
2521
iVolume -= static_cast<int>(250 * gpGlobals->frametime);
2522
2523
if ( iVolume < m_iTargetVolume )
2524
{
2525
iVolume = 0;
2526
}
2527
}
2528
2529
if ( m_fNoPlayerSound )
2530
{
2531
// debugging flag, lets players move around and shoot without monsters hearing.
2532
iVolume = 0;
2533
}
2534
2535
if ( gpGlobals->time > m_flStopExtraSoundTime )
2536
{
2537
// since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two
2538
// after actual emission to make sure it gets heard.
2539
m_iExtraSoundTypes = 0;
2540
}
2541
2542
if ( pSound )
2543
{
2544
pSound->m_vecOrigin = pev->origin;
2545
pSound->m_iType |= ( bits_SOUND_PLAYER | m_iExtraSoundTypes );
2546
pSound->m_iVolume = iVolume;
2547
}
2548
2549
// keep track of virtual muzzle flash
2550
m_iWeaponFlash -= static_cast<int>(256 * gpGlobals->frametime);
2551
if (m_iWeaponFlash < 0)
2552
m_iWeaponFlash = 0;
2553
2554
//UTIL_MakeVectors ( pev->angles );
2555
//gpGlobals->v_forward.z = 0;
2556
2557
// Below are a couple of useful little bits that make it easier to determine just how much noise the
2558
// player is making.
2559
// UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 );
2560
//ALERT ( at_console, "%d/%d\n", iVolume, m_iTargetVolume );
2561
}
2562
2563
2564
void CBasePlayer::PostThink()
2565
{
2566
if ( g_fGameOver )
2567
goto pt_end; // intermission or finale
2568
2569
if (!IsAlive())
2570
goto pt_end;
2571
2572
// Handle Tank controlling
2573
if ( m_pTank != 0 )
2574
{ // if they've moved too far from the gun, or selected a weapon, unuse the gun
2575
if ( m_pTank->OnControls( pev ) && !pev->weaponmodel )
2576
{
2577
m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun
2578
}
2579
else
2580
{ // they've moved off the platform
2581
m_pTank->Use( this, this, USE_OFF, 0 );
2582
m_pTank = NULL;
2583
}
2584
}
2585
2586
// do weapon stuff
2587
ItemPostFrame( );
2588
2589
// check to see if player landed hard enough to make a sound
2590
// falling farther than half of the maximum safe distance, but not as far a max safe distance will
2591
// play a bootscrape sound, and no damage will be inflicted. Fallling a distance shorter than half
2592
// of maximum safe distance will make no sound. Falling farther than max safe distance will play a
2593
// fallpain sound, and damage will be inflicted based on how far the player fell
2594
2595
if ( (FBitSet(pev->flags, FL_ONGROUND)) && (pev->health > 0) && m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD )
2596
{
2597
// ALERT ( at_console, "%f\n", m_flFallVelocity );
2598
2599
if (pev->watertype == CONTENT_WATER)
2600
{
2601
// Did he hit the world or a non-moving entity?
2602
// BUG - this happens all the time in water, especially when
2603
// BUG - water has current force
2604
// if ( !pev->groundentity || VARS(pev->groundentity)->velocity.z == 0 )
2605
// EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM);
2606
}
2607
else if ( m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED )
2608
{// after this point, we start doing damage
2609
2610
float flFallDamage = g_pGameRules->FlPlayerFallDamage( this );
2611
2612
if ( flFallDamage > pev->health )
2613
{//splat
2614
// note: play on item channel because we play footstep landing on body channel
2615
EMIT_SOUND(ENT(pev), CHAN_ITEM, "common/bodysplat.wav", 1, ATTN_NORM);
2616
}
2617
2618
if ( flFallDamage > 0 )
2619
{
2620
TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL );
2621
pev->punchangle.x = 0;
2622
}
2623
}
2624
2625
if ( IsAlive() )
2626
{
2627
SetAnimation( PLAYER_WALK );
2628
}
2629
}
2630
2631
if (FBitSet(pev->flags, FL_ONGROUND))
2632
{
2633
if (m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer())
2634
{
2635
CSoundEnt::InsertSound ( bits_SOUND_PLAYER, pev->origin, static_cast<int>(m_flFallVelocity), 0.2 );
2636
// ALERT( at_console, "fall %f\n", m_flFallVelocity );
2637
}
2638
m_flFallVelocity = 0;
2639
}
2640
2641
// select the proper animation for the player character
2642
if ( IsAlive() )
2643
{
2644
if (!pev->velocity.x && !pev->velocity.y)
2645
SetAnimation( PLAYER_IDLE );
2646
else if ((pev->velocity.x || pev->velocity.y) && (FBitSet(pev->flags, FL_ONGROUND)))
2647
SetAnimation( PLAYER_WALK );
2648
else if (pev->waterlevel > 1)
2649
SetAnimation( PLAYER_WALK );
2650
}
2651
2652
StudioFrameAdvance( );
2653
CheckPowerups(pev);
2654
2655
UpdatePlayerSound();
2656
2657
pt_end:
2658
#if defined( CLIENT_WEAPONS )
2659
// Decay timers on weapons
2660
// go through all of the weapons and make a list of the ones to pack
2661
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ )
2662
{
2663
if ( m_rgpPlayerItems[ i ] )
2664
{
2665
CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ];
2666
2667
while ( pPlayerItem )
2668
{
2669
CBasePlayerWeapon *gun;
2670
2671
gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr();
2672
2673
if ( gun && gun->UseDecrement() )
2674
{
2675
gun->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack - gpGlobals->frametime, -1.0 );
2676
gun->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack - gpGlobals->frametime, -0.001 );
2677
2678
if ( gun->m_flTimeWeaponIdle != 1000 )
2679
{
2680
gun->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle - gpGlobals->frametime, -0.001 );
2681
}
2682
2683
if ( gun->pev->fuser1 != 1000 )
2684
{
2685
gun->pev->fuser1 = max( gun->pev->fuser1 - gpGlobals->frametime, -0.001 );
2686
}
2687
2688
// Only decrement if not flagged as NO_DECREMENT
2689
// if ( gun->m_flPumpTime != 1000 )
2690
// {
2691
// gun->m_flPumpTime = max( gun->m_flPumpTime - gpGlobals->frametime, -0.001 );
2692
// }
2693
2694
}
2695
2696
pPlayerItem = pPlayerItem->m_pNext;
2697
}
2698
}
2699
}
2700
2701
m_flNextAttack -= gpGlobals->frametime;
2702
if ( m_flNextAttack < -0.001 )
2703
m_flNextAttack = -0.001;
2704
2705
if ( m_flNextAmmoBurn != 1000 )
2706
{
2707
m_flNextAmmoBurn -= gpGlobals->frametime;
2708
2709
if ( m_flNextAmmoBurn < -0.001 )
2710
m_flNextAmmoBurn = -0.001;
2711
}
2712
2713
if ( m_flAmmoStartCharge != 1000 )
2714
{
2715
m_flAmmoStartCharge -= gpGlobals->frametime;
2716
2717
if ( m_flAmmoStartCharge < -0.001 )
2718
m_flAmmoStartCharge = -0.001;
2719
}
2720
#endif
2721
2722
// Track button info so we can detect 'pressed' and 'released' buttons next frame
2723
m_afButtonLast = pev->button;
2724
}
2725
2726
2727
// checks if the spot is clear of players
2728
BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot )
2729
{
2730
CBaseEntity *ent = NULL;
2731
2732
if ( !pSpot->IsTriggered( pPlayer ) )
2733
{
2734
return FALSE;
2735
}
2736
2737
while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL )
2738
{
2739
// if ent is a client, don't spawn on 'em
2740
if ( ent->IsPlayer() && ent != pPlayer )
2741
return FALSE;
2742
}
2743
2744
return TRUE;
2745
}
2746
2747
2748
DLL_GLOBAL CBaseEntity *g_pLastSpawn;
2749
inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); }
2750
2751
/*
2752
============
2753
EntSelectSpawnPoint
2754
2755
Returns the entity to spawn at
2756
2757
USES AND SETS GLOBAL g_pLastSpawn
2758
============
2759
*/
2760
edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer )
2761
{
2762
CBaseEntity *pSpot;
2763
edict_t *player;
2764
2765
player = pPlayer->edict();
2766
2767
// choose a info_player_deathmatch point
2768
if (g_pGameRules->IsCoOp())
2769
{
2770
pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_coop");
2771
if ( !FNullEnt(pSpot) )
2772
goto ReturnSpot;
2773
pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_start");
2774
if ( !FNullEnt(pSpot) )
2775
goto ReturnSpot;
2776
}
2777
else if ( g_pGameRules->IsDeathmatch() )
2778
{
2779
pSpot = g_pLastSpawn;
2780
// Randomize the start spot
2781
for ( int i = RANDOM_LONG(1,5); i > 0; i-- )
2782
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
2783
if ( FNullEnt( pSpot ) ) // skip over the null point
2784
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
2785
2786
CBaseEntity *pFirstSpot = pSpot;
2787
2788
do
2789
{
2790
if ( pSpot )
2791
{
2792
// check if pSpot is valid
2793
if ( IsSpawnPointValid( pPlayer, pSpot ) )
2794
{
2795
if ( pSpot->pev->origin == Vector( 0, 0, 0 ) )
2796
{
2797
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
2798
continue;
2799
}
2800
2801
// if so, go to pSpot
2802
goto ReturnSpot;
2803
}
2804
}
2805
// increment pSpot
2806
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
2807
} while ( pSpot != pFirstSpot ); // loop if we're not back to the start
2808
2809
// we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
2810
if ( !FNullEnt( pSpot ) )
2811
{
2812
CBaseEntity *ent = NULL;
2813
while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL )
2814
{
2815
// if ent is a client, kill em (unless they are ourselves)
2816
if ( ent->IsPlayer() && !(ent->edict() == player) )
2817
ent->TakeDamage( VARS(INDEXENT(0)), VARS(INDEXENT(0)), 300, DMG_GENERIC );
2818
}
2819
goto ReturnSpot;
2820
}
2821
}
2822
2823
// If startspot is set, (re)spawn there.
2824
if ( FStringNull( gpGlobals->startspot ) || !strlen(STRING(gpGlobals->startspot)))
2825
{
2826
pSpot = UTIL_FindEntityByClassname(NULL, "info_player_start");
2827
if ( !FNullEnt(pSpot) )
2828
goto ReturnSpot;
2829
}
2830
else
2831
{
2832
pSpot = UTIL_FindEntityByTargetname( NULL, STRING(gpGlobals->startspot) );
2833
if ( !FNullEnt(pSpot) )
2834
goto ReturnSpot;
2835
}
2836
2837
ReturnSpot:
2838
if ( FNullEnt( pSpot ) )
2839
{
2840
ALERT(at_error, "PutClientInServer: no info_player_start on level");
2841
return INDEXENT(0);
2842
}
2843
2844
g_pLastSpawn = pSpot;
2845
return pSpot->edict();
2846
}
2847
2848
void CBasePlayer::Spawn( void )
2849
{
2850
pev->classname = MAKE_STRING("player");
2851
pev->health = 100;
2852
pev->armorvalue = 0;
2853
pev->takedamage = DAMAGE_AIM;
2854
pev->solid = SOLID_SLIDEBOX;
2855
pev->movetype = MOVETYPE_WALK;
2856
pev->max_health = pev->health;
2857
pev->flags &= FL_PROXY; // keep proxy flag sey by engine
2858
pev->flags |= FL_CLIENT;
2859
pev->air_finished = gpGlobals->time + 12;
2860
pev->dmg = 2; // initial water damage
2861
pev->effects = 0;
2862
pev->deadflag = DEAD_NO;
2863
pev->dmg_take = 0;
2864
pev->dmg_save = 0;
2865
pev->friction = 1.0;
2866
pev->gravity = 1.0;
2867
m_bitsHUDDamage = -1;
2868
m_bitsDamageType = 0;
2869
m_afPhysicsFlags = 0;
2870
m_fLongJump = FALSE;// no longjump module.
2871
2872
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" );
2873
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" );
2874
2875
pev->fov = m_iFOV = 0;// init field of view.
2876
m_iClientFOV = -1; // make sure fov reset is sent
2877
2878
m_flNextDecalTime = 0;// let this player decal as soon as he spawns.
2879
2880
m_flgeigerDelay = gpGlobals->time + 2.0; // wait a few seconds until user-defined message registrations
2881
// are recieved by all clients
2882
2883
m_flTimeStepSound = 0;
2884
m_iStepLeft = 0;
2885
m_flFieldOfView = 0.5;// some monsters use this to determine whether or not the player is looking at them.
2886
2887
m_bloodColor = BLOOD_COLOR_RED;
2888
m_flNextAttack = UTIL_WeaponTimeBase();
2889
StartSneaking();
2890
2891
m_iFlashBattery = 99;
2892
m_flFlashLightTime = 1; // force first message
2893
2894
// dont let uninitialized value here hurt the player
2895
m_flFallVelocity = 0;
2896
2897
g_pGameRules->SetDefaultPlayerTeam( this );
2898
g_pGameRules->GetPlayerSpawnSpot( this );
2899
2900
SET_MODEL(ENT(pev), "models/player.mdl");
2901
g_ulModelIndexPlayer = pev->modelindex;
2902
pev->sequence = LookupActivity( ACT_IDLE );
2903
2904
if ( FBitSet(pev->flags, FL_DUCKING) )
2905
UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
2906
else
2907
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
2908
2909
pev->view_ofs = VEC_VIEW;
2910
Precache();
2911
m_HackedGunPos = Vector( 0, 32, 0 );
2912
2913
if ( m_iPlayerSound == SOUNDLIST_EMPTY )
2914
{
2915
ALERT ( at_console, "Couldn't alloc player sound slot!\n" );
2916
}
2917
2918
m_fNoPlayerSound = FALSE;// normal sound behavior.
2919
2920
m_pLastItem = NULL;
2921
m_fInitHUD = TRUE;
2922
m_iClientHideHUD = -1; // force this to be recalculated
2923
m_fWeapon = FALSE;
2924
m_pClientActiveItem = NULL;
2925
m_iClientBattery = -1;
2926
2927
// reset all ammo values to 0
2928
for ( int i = 0; i < MAX_AMMO_SLOTS; i++ )
2929
{
2930
m_rgAmmo[i] = 0;
2931
m_rgAmmoLast[i] = 0; // client ammo values also have to be reset (the death hud clear messages does on the client side)
2932
}
2933
2934
m_lastx = m_lasty = 0;
2935
2936
m_flNextChatTime = gpGlobals->time;
2937
2938
g_pGameRules->PlayerSpawn( this );
2939
}
2940
2941
2942
void CBasePlayer :: Precache( void )
2943
{
2944
// in the event that the player JUST spawned, and the level node graph
2945
// was loaded, fix all of the node graph pointers before the game starts.
2946
2947
// !!!BUGBUG - now that we have multiplayer, this needs to be moved!
2948
if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet )
2949
{
2950
if ( !WorldGraph.FSetGraphPointers() )
2951
{
2952
ALERT ( at_console, "**Graph pointers were not set!\n");
2953
}
2954
else
2955
{
2956
ALERT ( at_console, "**Graph Pointers Set!\n" );
2957
}
2958
}
2959
2960
// SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific)
2961
// because they need to precache before any clients have connected
2962
2963
// init geiger counter vars during spawn and each time
2964
// we cross a level transition
2965
2966
m_flgeigerRange = 1000;
2967
m_igeigerRangePrev = 1000;
2968
2969
m_bitsDamageType = 0;
2970
m_bitsHUDDamage = -1;
2971
2972
m_iClientBattery = -1;
2973
2974
m_iTrain = TRAIN_NEW;
2975
2976
// Make sure any necessary user messages have been registered
2977
LinkUserMessages();
2978
2979
m_iUpdateTime = 5; // won't update for 1/2 a second
2980
2981
if ( gInitHUD )
2982
m_fInitHUD = TRUE;
2983
}
2984
2985
2986
int CBasePlayer::Save( CSave &save )
2987
{
2988
if ( !CBaseMonster::Save(save) )
2989
return 0;
2990
2991
return save.WriteFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) );
2992
}
2993
2994
2995
//
2996
// Marks everything as new so the player will resend this to the hud.
2997
//
2998
void CBasePlayer::RenewItems(void)
2999
{
3000
3001
}
3002
3003
3004
int CBasePlayer::Restore( CRestore &restore )
3005
{
3006
if ( !CBaseMonster::Restore(restore) )
3007
return 0;
3008
3009
int status = restore.ReadFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) );
3010
3011
SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData;
3012
// landmark isn't present.
3013
if ( !pSaveData->fUseLandmark )
3014
{
3015
ALERT( at_console, "No Landmark:%s\n", pSaveData->szLandmarkName );
3016
3017
// default to normal spawn
3018
edict_t* pentSpawnSpot = EntSelectSpawnPoint( this );
3019
pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1);
3020
pev->angles = VARS(pentSpawnSpot)->angles;
3021
}
3022
pev->v_angle.z = 0; // Clear out roll
3023
pev->angles = pev->v_angle;
3024
3025
pev->fixangle = TRUE; // turn this way immediately
3026
3027
// Copied from spawn() for now
3028
m_bloodColor = BLOOD_COLOR_RED;
3029
3030
g_ulModelIndexPlayer = pev->modelindex;
3031
3032
if ( FBitSet(pev->flags, FL_DUCKING) )
3033
{
3034
// Use the crouch HACK
3035
//FixPlayerCrouchStuck( edict() );
3036
// Don't need to do this with new player prediction code.
3037
UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
3038
}
3039
else
3040
{
3041
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
3042
}
3043
3044
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" );
3045
3046
if ( m_fLongJump )
3047
{
3048
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "1" );
3049
}
3050
else
3051
{
3052
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" );
3053
}
3054
3055
RenewItems();
3056
3057
#if defined( CLIENT_WEAPONS )
3058
// HACK: This variable is saved/restored in CBaseMonster as a time variable, but we're using it
3059
// as just a counter. Ideally, this needs its own variable that's saved as a plain float.
3060
// Barring that, we clear it out here instead of using the incorrect restored time value.
3061
m_flNextAttack = UTIL_WeaponTimeBase();
3062
#endif
3063
3064
return status;
3065
}
3066
3067
3068
3069
void CBasePlayer::SelectNextItem( int iItem )
3070
{
3071
CBasePlayerItem *pItem;
3072
3073
pItem = m_rgpPlayerItems[ iItem ];
3074
3075
if (!pItem)
3076
return;
3077
3078
if (pItem == m_pActiveItem)
3079
{
3080
// select the next one in the chain
3081
pItem = m_pActiveItem->m_pNext;
3082
if (! pItem)
3083
{
3084
return;
3085
}
3086
3087
CBasePlayerItem *pLast;
3088
pLast = pItem;
3089
while (pLast->m_pNext)
3090
pLast = pLast->m_pNext;
3091
3092
// relink chain
3093
pLast->m_pNext = m_pActiveItem;
3094
m_pActiveItem->m_pNext = NULL;
3095
m_rgpPlayerItems[ iItem ] = pItem;
3096
}
3097
3098
ResetAutoaim( );
3099
3100
// FIX, this needs to queue them up and delay
3101
if (m_pActiveItem)
3102
{
3103
m_pActiveItem->Holster( );
3104
}
3105
3106
m_pActiveItem = pItem;
3107
3108
if (m_pActiveItem)
3109
{
3110
m_pActiveItem->Deploy( );
3111
m_pActiveItem->UpdateItemInfo( );
3112
}
3113
}
3114
3115
void CBasePlayer::SelectItem(const char *pstr)
3116
{
3117
if (!pstr)
3118
return;
3119
3120
CBasePlayerItem *pItem = NULL;
3121
3122
for (int i = 0; i < MAX_ITEM_TYPES; i++)
3123
{
3124
if (m_rgpPlayerItems[i])
3125
{
3126
pItem = m_rgpPlayerItems[i];
3127
3128
while (pItem)
3129
{
3130
if (FClassnameIs(pItem->pev, pstr))
3131
break;
3132
pItem = pItem->m_pNext;
3133
}
3134
}
3135
3136
if (pItem)
3137
break;
3138
}
3139
3140
if (!pItem)
3141
return;
3142
3143
3144
if (pItem == m_pActiveItem)
3145
return;
3146
3147
ResetAutoaim( );
3148
3149
// FIX, this needs to queue them up and delay
3150
if (m_pActiveItem)
3151
m_pActiveItem->Holster( );
3152
3153
m_pLastItem = m_pActiveItem;
3154
m_pActiveItem = pItem;
3155
3156
if (m_pActiveItem)
3157
{
3158
m_pActiveItem->Deploy( );
3159
m_pActiveItem->UpdateItemInfo( );
3160
}
3161
}
3162
3163
3164
void CBasePlayer::SelectLastItem(void)
3165
{
3166
if (!m_pLastItem)
3167
{
3168
return;
3169
}
3170
3171
if ( m_pActiveItem && !m_pActiveItem->CanHolster() )
3172
{
3173
return;
3174
}
3175
3176
ResetAutoaim( );
3177
3178
// FIX, this needs to queue them up and delay
3179
if (m_pActiveItem)
3180
m_pActiveItem->Holster( );
3181
3182
CBasePlayerItem *pTemp = m_pActiveItem;
3183
m_pActiveItem = m_pLastItem;
3184
m_pLastItem = pTemp;
3185
m_pActiveItem->Deploy( );
3186
m_pActiveItem->UpdateItemInfo( );
3187
}
3188
3189
//==============================================
3190
// HasWeapons - do I have any weapons at all?
3191
//==============================================
3192
BOOL CBasePlayer::HasWeapons( void )
3193
{
3194
int i;
3195
3196
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
3197
{
3198
if ( m_rgpPlayerItems[ i ] )
3199
{
3200
return TRUE;
3201
}
3202
}
3203
3204
return FALSE;
3205
}
3206
3207
void CBasePlayer::SelectPrevItem( int iItem )
3208
{
3209
}
3210
3211
3212
const char *CBasePlayer::TeamID( void )
3213
{
3214
if ( pev == NULL ) // Not fully connected yet
3215
return "";
3216
3217
// return their team name
3218
return m_szTeamName;
3219
}
3220
3221
3222
//==============================================
3223
// !!!UNDONE:ultra temporary SprayCan entity to apply
3224
// decal frame at a time. For PreAlpha CD
3225
//==============================================
3226
class CSprayCan : public CBaseEntity
3227
{
3228
public:
3229
void Spawn ( entvars_t *pevOwner );
3230
void Think( void );
3231
3232
virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
3233
};
3234
3235
void CSprayCan::Spawn ( entvars_t *pevOwner )
3236
{
3237
pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 );
3238
pev->angles = pevOwner->v_angle;
3239
pev->owner = ENT(pevOwner);
3240
pev->frame = 0;
3241
3242
pev->nextthink = gpGlobals->time + 0.1;
3243
EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/sprayer.wav", 1, ATTN_NORM);
3244
}
3245
3246
void CSprayCan::Think( void )
3247
{
3248
TraceResult tr;
3249
int playernum;
3250
int nFrames;
3251
CBasePlayer *pPlayer;
3252
3253
pPlayer = (CBasePlayer *)GET_PRIVATE(pev->owner);
3254
3255
if (pPlayer)
3256
nFrames = pPlayer->GetCustomDecalFrames();
3257
else
3258
nFrames = -1;
3259
3260
playernum = ENTINDEX(pev->owner);
3261
3262
// ALERT(at_console, "Spray by player %i, %i of %i\n", playernum, (int)(pev->frame + 1), nFrames);
3263
3264
UTIL_MakeVectors(pev->angles);
3265
UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr);
3266
3267
// No customization present.
3268
if (nFrames == -1)
3269
{
3270
UTIL_DecalTrace( &tr, DECAL_LAMBDA6 );
3271
UTIL_Remove( this );
3272
}
3273
else
3274
{
3275
UTIL_PlayerDecalTrace( &tr, playernum, static_cast<int>(pev->frame), TRUE );
3276
// Just painted last custom frame.
3277
if ( pev->frame++ >= (nFrames - 1))
3278
UTIL_Remove( this );
3279
}
3280
3281
pev->nextthink = gpGlobals->time + 0.1;
3282
}
3283
3284
class CBloodSplat : public CBaseEntity
3285
{
3286
public:
3287
void Spawn ( entvars_t *pevOwner );
3288
void Spray ( void );
3289
};
3290
3291
void CBloodSplat::Spawn ( entvars_t *pevOwner )
3292
{
3293
pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 );
3294
pev->angles = pevOwner->v_angle;
3295
pev->owner = ENT(pevOwner);
3296
3297
SetThink ( &CBloodSplat::Spray );
3298
pev->nextthink = gpGlobals->time + 0.1;
3299
}
3300
3301
void CBloodSplat::Spray ( void )
3302
{
3303
TraceResult tr;
3304
3305
if ( g_Language != LANGUAGE_GERMAN )
3306
{
3307
UTIL_MakeVectors(pev->angles);
3308
UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr);
3309
3310
UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED );
3311
}
3312
SetThink ( &CBloodSplat::SUB_Remove );
3313
pev->nextthink = gpGlobals->time + 0.1;
3314
}
3315
3316
//==============================================
3317
3318
3319
3320
void CBasePlayer::GiveNamedItem( const char *pszName )
3321
{
3322
edict_t *pent;
3323
3324
int istr = MAKE_STRING(pszName);
3325
3326
pent = CREATE_NAMED_ENTITY(istr);
3327
if ( FNullEnt( pent ) )
3328
{
3329
ALERT ( at_console, "NULL Ent in GiveNamedItem!\n" );
3330
return;
3331
}
3332
VARS( pent )->origin = pev->origin;
3333
pent->v.spawnflags |= SF_NORESPAWN;
3334
3335
DispatchSpawn( pent );
3336
DispatchTouch( pent, ENT( pev ) );
3337
}
3338
3339
3340
3341
CBaseEntity *FindEntityForward( CBaseEntity *pMe )
3342
{
3343
TraceResult tr;
3344
3345
UTIL_MakeVectors(pMe->pev->v_angle);
3346
UTIL_TraceLine(pMe->pev->origin + pMe->pev->view_ofs,pMe->pev->origin + pMe->pev->view_ofs + gpGlobals->v_forward * 8192,dont_ignore_monsters, pMe->edict(), &tr );
3347
if ( tr.flFraction != 1.0 && !FNullEnt( tr.pHit) )
3348
{
3349
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
3350
return pHit;
3351
}
3352
return NULL;
3353
}
3354
3355
3356
BOOL CBasePlayer :: FlashlightIsOn( void )
3357
{
3358
return FBitSet(pev->effects, EF_DIMLIGHT);
3359
}
3360
3361
3362
void CBasePlayer :: FlashlightTurnOn( void )
3363
{
3364
if ( !g_pGameRules->FAllowFlashlight() )
3365
{
3366
return;
3367
}
3368
3369
if ( (pev->weapons & (1<<WEAPON_SUIT)) )
3370
{
3371
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_ON, 1.0, ATTN_NORM, 0, PITCH_NORM );
3372
SetBits(pev->effects, EF_DIMLIGHT);
3373
MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev );
3374
WRITE_BYTE(1);
3375
WRITE_BYTE(m_iFlashBattery);
3376
MESSAGE_END();
3377
3378
m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time;
3379
3380
}
3381
}
3382
3383
3384
void CBasePlayer :: FlashlightTurnOff( void )
3385
{
3386
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM );
3387
ClearBits(pev->effects, EF_DIMLIGHT);
3388
MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev );
3389
WRITE_BYTE(0);
3390
WRITE_BYTE(m_iFlashBattery);
3391
MESSAGE_END();
3392
3393
m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time;
3394
3395
}
3396
3397
/*
3398
===============
3399
ForceClientDllUpdate
3400
3401
When recording a demo, we need to have the server tell us the entire client state
3402
so that the client side .dll can behave correctly.
3403
Reset stuff so that the state is transmitted.
3404
===============
3405
*/
3406
void CBasePlayer :: ForceClientDllUpdate( void )
3407
{
3408
m_iClientHealth = -1;
3409
m_iClientBattery = -1;
3410
m_iTrain |= TRAIN_NEW; // Force new train message.
3411
m_fWeapon = FALSE; // Force weapon send
3412
m_fKnownItem = FALSE; // Force weaponinit messages.
3413
m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message
3414
3415
// Now force all the necessary messages
3416
// to be sent.
3417
UpdateClientData();
3418
}
3419
3420
/*
3421
============
3422
ImpulseCommands
3423
============
3424
*/
3425
extern float g_flWeaponCheat;
3426
3427
void CBasePlayer::ImpulseCommands( )
3428
{
3429
TraceResult tr;// UNDONE: kill me! This is temporary for PreAlpha CDs
3430
3431
// Handle use events
3432
PlayerUse();
3433
3434
int iImpulse = (int)pev->impulse;
3435
switch (iImpulse)
3436
{
3437
case 99:
3438
{
3439
3440
int iOn;
3441
3442
if (!gmsgLogo)
3443
{
3444
iOn = 1;
3445
gmsgLogo = REG_USER_MSG("Logo", 1);
3446
}
3447
else
3448
{
3449
iOn = 0;
3450
}
3451
3452
ASSERT( gmsgLogo > 0 );
3453
// send "health" update message
3454
MESSAGE_BEGIN( MSG_ONE, gmsgLogo, NULL, pev );
3455
WRITE_BYTE(iOn);
3456
MESSAGE_END();
3457
3458
if(!iOn)
3459
gmsgLogo = 0;
3460
break;
3461
}
3462
case 100:
3463
// temporary flashlight for level designers
3464
if ( FlashlightIsOn() )
3465
{
3466
FlashlightTurnOff();
3467
}
3468
else
3469
{
3470
FlashlightTurnOn();
3471
}
3472
break;
3473
3474
case 201:// paint decal
3475
3476
if ( gpGlobals->time < m_flNextDecalTime )
3477
{
3478
// too early!
3479
break;
3480
}
3481
3482
UTIL_MakeVectors(pev->v_angle);
3483
UTIL_TraceLine ( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + gpGlobals->v_forward * 128, ignore_monsters, ENT(pev), & tr);
3484
3485
if ( tr.flFraction != 1.0 )
3486
{// line hit something, so paint a decal
3487
m_flNextDecalTime = gpGlobals->time + decalfrequency.value;
3488
CSprayCan *pCan = GetClassPtr((CSprayCan *)NULL);
3489
pCan->Spawn( pev );
3490
}
3491
3492
break;
3493
3494
default:
3495
// check all of the cheat impulse commands now
3496
CheatImpulseCommands( iImpulse );
3497
break;
3498
}
3499
3500
pev->impulse = 0;
3501
}
3502
3503
//=========================================================
3504
//=========================================================
3505
void CBasePlayer::CheatImpulseCommands( int iImpulse )
3506
{
3507
#if !defined( HLDEMO_BUILD )
3508
if ( g_flWeaponCheat == 0.0 )
3509
{
3510
return;
3511
}
3512
3513
CBaseEntity *pEntity;
3514
TraceResult tr;
3515
3516
switch ( iImpulse )
3517
{
3518
case 76:
3519
{
3520
if (!giPrecacheGrunt)
3521
{
3522
giPrecacheGrunt = 1;
3523
ALERT(at_console, "You must now restart to use Grunt-o-matic.\n");
3524
}
3525
else
3526
{
3527
UTIL_MakeVectors( Vector( 0, pev->v_angle.y, 0 ) );
3528
Create("monster_human_grunt", pev->origin + gpGlobals->v_forward * 128, pev->angles);
3529
}
3530
break;
3531
}
3532
3533
3534
case 101:
3535
gEvilImpulse101 = TRUE;
3536
GiveNamedItem( "item_suit" );
3537
GiveNamedItem( "item_battery" );
3538
GiveNamedItem( "weapon_crowbar" );
3539
GiveNamedItem( "weapon_9mmhandgun" );
3540
GiveNamedItem( "ammo_9mmclip" );
3541
GiveNamedItem( "weapon_shotgun" );
3542
GiveNamedItem( "ammo_buckshot" );
3543
GiveNamedItem( "weapon_9mmAR" );
3544
GiveNamedItem( "ammo_9mmAR" );
3545
GiveNamedItem( "ammo_ARgrenades" );
3546
GiveNamedItem( "weapon_handgrenade" );
3547
GiveNamedItem( "weapon_tripmine" );
3548
#ifndef OEM_BUILD
3549
GiveNamedItem( "weapon_357" );
3550
GiveNamedItem( "ammo_357" );
3551
GiveNamedItem( "weapon_crossbow" );
3552
GiveNamedItem( "ammo_crossbow" );
3553
GiveNamedItem( "weapon_egon" );
3554
GiveNamedItem( "weapon_gauss" );
3555
GiveNamedItem( "ammo_gaussclip" );
3556
GiveNamedItem( "weapon_rpg" );
3557
GiveNamedItem( "ammo_rpgclip" );
3558
GiveNamedItem( "weapon_satchel" );
3559
GiveNamedItem( "weapon_snark" );
3560
GiveNamedItem( "weapon_hornetgun" );
3561
#endif
3562
gEvilImpulse101 = FALSE;
3563
break;
3564
3565
case 102:
3566
// Gibbage!!!
3567
CGib::SpawnRandomGibs( pev, 1, 1 );
3568
break;
3569
3570
case 103:
3571
// What the hell are you doing?
3572
pEntity = FindEntityForward( this );
3573
if ( pEntity )
3574
{
3575
CBaseMonster *pMonster = pEntity->MyMonsterPointer();
3576
if ( pMonster )
3577
pMonster->ReportAIState();
3578
}
3579
break;
3580
3581
case 104:
3582
// Dump all of the global state varaibles (and global entity names)
3583
gGlobalState.DumpGlobals();
3584
break;
3585
3586
case 105:// player makes no sound for monsters to hear.
3587
{
3588
if ( m_fNoPlayerSound )
3589
{
3590
ALERT ( at_console, "Player is audible\n" );
3591
m_fNoPlayerSound = FALSE;
3592
}
3593
else
3594
{
3595
ALERT ( at_console, "Player is silent\n" );
3596
m_fNoPlayerSound = TRUE;
3597
}
3598
break;
3599
}
3600
3601
case 106:
3602
// Give me the classname and targetname of this entity.
3603
pEntity = FindEntityForward( this );
3604
if ( pEntity )
3605
{
3606
ALERT ( at_console, "Classname: %s", STRING( pEntity->pev->classname ) );
3607
3608
if ( !FStringNull ( pEntity->pev->targetname ) )
3609
{
3610
ALERT ( at_console, " - Targetname: %s\n", STRING( pEntity->pev->targetname ) );
3611
}
3612
else
3613
{
3614
ALERT ( at_console, " - TargetName: No Targetname\n" );
3615
}
3616
3617
ALERT ( at_console, "Model: %s\n", STRING( pEntity->pev->model ) );
3618
if ( pEntity->pev->globalname )
3619
ALERT ( at_console, "Globalname: %s\n", STRING( pEntity->pev->globalname ) );
3620
}
3621
break;
3622
3623
case 107:
3624
{
3625
TraceResult tr;
3626
3627
edict_t *pWorld = g_engfuncs.pfnPEntityOfEntIndex( 0 );
3628
3629
Vector start = pev->origin + pev->view_ofs;
3630
Vector end = start + gpGlobals->v_forward * 1024;
3631
UTIL_TraceLine( start, end, ignore_monsters, edict(), &tr );
3632
if ( tr.pHit )
3633
pWorld = tr.pHit;
3634
const char *pTextureName = TRACE_TEXTURE( pWorld, start, end );
3635
if ( pTextureName )
3636
ALERT( at_console, "Texture: %s\n", pTextureName );
3637
}
3638
break;
3639
case 195:// show shortest paths for entire level to nearest node
3640
{
3641
Create("node_viewer_fly", pev->origin, pev->angles);
3642
}
3643
break;
3644
case 196:// show shortest paths for entire level to nearest node
3645
{
3646
Create("node_viewer_large", pev->origin, pev->angles);
3647
}
3648
break;
3649
case 197:// show shortest paths for entire level to nearest node
3650
{
3651
Create("node_viewer_human", pev->origin, pev->angles);
3652
}
3653
break;
3654
case 199:// show nearest node and all connections
3655
{
3656
ALERT ( at_console, "%d\n", WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) );
3657
WorldGraph.ShowNodeConnections ( WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) );
3658
}
3659
break;
3660
case 202:// Random blood splatter
3661
UTIL_MakeVectors(pev->v_angle);
3662
UTIL_TraceLine ( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + gpGlobals->v_forward * 128, ignore_monsters, ENT(pev), & tr);
3663
3664
if ( tr.flFraction != 1.0 )
3665
{// line hit something, so paint a decal
3666
CBloodSplat *pBlood = GetClassPtr((CBloodSplat *)NULL);
3667
pBlood->Spawn( pev );
3668
}
3669
break;
3670
case 203:// remove creature.
3671
pEntity = FindEntityForward( this );
3672
if ( pEntity )
3673
{
3674
if ( pEntity->pev->takedamage )
3675
pEntity->SetThink(&CBaseEntity::SUB_Remove);
3676
}
3677
break;
3678
}
3679
#endif // HLDEMO_BUILD
3680
}
3681
3682
//
3683
// Add a weapon to the player (Item == Weapon == Selectable Object)
3684
//
3685
int CBasePlayer::AddPlayerItem( CBasePlayerItem *pItem )
3686
{
3687
CBasePlayerItem *pInsert;
3688
3689
pInsert = m_rgpPlayerItems[pItem->iItemSlot()];
3690
3691
while (pInsert)
3692
{
3693
if (FClassnameIs( pInsert->pev, STRING( pItem->pev->classname) ))
3694
{
3695
if (pItem->AddDuplicate( pInsert ))
3696
{
3697
g_pGameRules->PlayerGotWeapon ( this, pItem );
3698
pItem->CheckRespawn();
3699
3700
// ugly hack to update clip w/o an update clip message
3701
pInsert->UpdateItemInfo( );
3702
if (m_pActiveItem)
3703
m_pActiveItem->UpdateItemInfo( );
3704
3705
pItem->Kill( );
3706
}
3707
else if (gEvilImpulse101)
3708
{
3709
// FIXME: remove anyway for deathmatch testing
3710
pItem->Kill( );
3711
}
3712
return FALSE;
3713
}
3714
pInsert = pInsert->m_pNext;
3715
}
3716
3717
3718
if (pItem->AddToPlayer( this ))
3719
{
3720
g_pGameRules->PlayerGotWeapon ( this, pItem );
3721
pItem->CheckRespawn();
3722
3723
pItem->m_pNext = m_rgpPlayerItems[pItem->iItemSlot()];
3724
m_rgpPlayerItems[pItem->iItemSlot()] = pItem;
3725
3726
// should we switch to this item?
3727
if ( g_pGameRules->FShouldSwitchWeapon( this, pItem ) )
3728
{
3729
SwitchWeapon( pItem );
3730
}
3731
3732
return TRUE;
3733
}
3734
else if (gEvilImpulse101)
3735
{
3736
// FIXME: remove anyway for deathmatch testing
3737
pItem->Kill( );
3738
}
3739
return FALSE;
3740
}
3741
3742
3743
3744
int CBasePlayer::RemovePlayerItem( CBasePlayerItem *pItem )
3745
{
3746
if (m_pActiveItem == pItem)
3747
{
3748
ResetAutoaim( );
3749
pItem->Holster( );
3750
pItem->pev->nextthink = 0;// crowbar may be trying to swing again, etc.
3751
pItem->SetThink( NULL );
3752
m_pActiveItem = NULL;
3753
pev->viewmodel = 0;
3754
pev->weaponmodel = 0;
3755
}
3756
else if ( m_pLastItem == pItem )
3757
m_pLastItem = NULL;
3758
3759
CBasePlayerItem *pPrev = m_rgpPlayerItems[pItem->iItemSlot()];
3760
3761
if (pPrev == pItem)
3762
{
3763
m_rgpPlayerItems[pItem->iItemSlot()] = pItem->m_pNext;
3764
return TRUE;
3765
}
3766
else
3767
{
3768
while (pPrev && pPrev->m_pNext != pItem)
3769
{
3770
pPrev = pPrev->m_pNext;
3771
}
3772
if (pPrev)
3773
{
3774
pPrev->m_pNext = pItem->m_pNext;
3775
return TRUE;
3776
}
3777
}
3778
return FALSE;
3779
}
3780
3781
3782
//
3783
// Returns the unique ID for the ammo, or -1 if error
3784
//
3785
int CBasePlayer :: GiveAmmo( int iCount, const char *szName, int iMax )
3786
{
3787
if ( !szName )
3788
{
3789
// no ammo.
3790
return -1;
3791
}
3792
3793
if ( !g_pGameRules->CanHaveAmmo( this, szName, iMax ) )
3794
{
3795
// game rules say I can't have any more of this ammo type.
3796
return -1;
3797
}
3798
3799
int i = 0;
3800
3801
i = GetAmmoIndex( szName );
3802
3803
if ( i < 0 || i >= MAX_AMMO_SLOTS )
3804
return -1;
3805
3806
int iAdd = min( iCount, iMax - m_rgAmmo[i] );
3807
if ( iAdd < 1 )
3808
return i;
3809
3810
m_rgAmmo[ i ] += iAdd;
3811
3812
3813
if ( gmsgAmmoPickup ) // make sure the ammo messages have been linked first
3814
{
3815
// Send the message that ammo has been picked up
3816
MESSAGE_BEGIN( MSG_ONE, gmsgAmmoPickup, NULL, pev );
3817
WRITE_BYTE( GetAmmoIndex(szName) ); // ammo ID
3818
WRITE_BYTE( iAdd ); // amount
3819
MESSAGE_END();
3820
}
3821
3822
TabulateAmmo();
3823
3824
return i;
3825
}
3826
3827
3828
/*
3829
============
3830
ItemPreFrame
3831
3832
Called every frame by the player PreThink
3833
============
3834
*/
3835
void CBasePlayer::ItemPreFrame()
3836
{
3837
#if defined( CLIENT_WEAPONS )
3838
if ( m_flNextAttack > 0 )
3839
#else
3840
if ( gpGlobals->time < m_flNextAttack )
3841
#endif
3842
{
3843
return;
3844
}
3845
3846
if (!m_pActiveItem)
3847
return;
3848
3849
m_pActiveItem->ItemPreFrame( );
3850
}
3851
3852
3853
/*
3854
============
3855
ItemPostFrame
3856
3857
Called every frame by the player PostThink
3858
============
3859
*/
3860
void CBasePlayer::ItemPostFrame()
3861
{
3862
// check if the player is using a tank
3863
if ( m_pTank != 0 )
3864
return;
3865
3866
#if defined( CLIENT_WEAPONS )
3867
if ( m_flNextAttack > 0 )
3868
#else
3869
if ( gpGlobals->time < m_flNextAttack )
3870
#endif
3871
{
3872
return;
3873
}
3874
3875
ImpulseCommands();
3876
3877
if (!m_pActiveItem)
3878
return;
3879
3880
m_pActiveItem->ItemPostFrame( );
3881
}
3882
3883
int CBasePlayer::AmmoInventory( int iAmmoIndex )
3884
{
3885
if (iAmmoIndex == -1)
3886
{
3887
return -1;
3888
}
3889
3890
return m_rgAmmo[ iAmmoIndex ];
3891
}
3892
3893
int CBasePlayer::GetAmmoIndex(const char *psz)
3894
{
3895
int i;
3896
3897
if (!psz)
3898
return -1;
3899
3900
for (i = 1; i < MAX_AMMO_SLOTS; i++)
3901
{
3902
if ( !CBasePlayerItem::AmmoInfoArray[i].pszName )
3903
continue;
3904
3905
if (stricmp( psz, CBasePlayerItem::AmmoInfoArray[i].pszName ) == 0)
3906
return i;
3907
}
3908
3909
return -1;
3910
}
3911
3912
// Called from UpdateClientData
3913
// makes sure the client has all the necessary ammo info, if values have changed
3914
void CBasePlayer::SendAmmoUpdate(void)
3915
{
3916
for (int i=0; i < MAX_AMMO_SLOTS;i++)
3917
{
3918
if (m_rgAmmo[i] != m_rgAmmoLast[i])
3919
{
3920
m_rgAmmoLast[i] = m_rgAmmo[i];
3921
3922
ASSERT( m_rgAmmo[i] >= 0 );
3923
ASSERT( m_rgAmmo[i] < 255 );
3924
3925
// send "Ammo" update message
3926
MESSAGE_BEGIN( MSG_ONE, gmsgAmmoX, NULL, pev );
3927
WRITE_BYTE( i );
3928
WRITE_BYTE( max( min( m_rgAmmo[i], 254 ), 0 ) ); // clamp the value to one byte
3929
MESSAGE_END();
3930
}
3931
}
3932
}
3933
3934
/*
3935
=========================================================
3936
UpdateClientData
3937
3938
resends any changed player HUD info to the client.
3939
Called every frame by PlayerPreThink
3940
Also called at start of demo recording and playback by
3941
ForceClientDllUpdate to ensure the demo gets messages
3942
reflecting all of the HUD state info.
3943
=========================================================
3944
*/
3945
void CBasePlayer :: UpdateClientData( void )
3946
{
3947
if (m_fInitHUD)
3948
{
3949
m_fInitHUD = FALSE;
3950
gInitHUD = FALSE;
3951
3952
MESSAGE_BEGIN( MSG_ONE, gmsgResetHUD, NULL, pev );
3953
WRITE_BYTE( 0 );
3954
MESSAGE_END();
3955
3956
if ( !m_fGameHUDInitialized )
3957
{
3958
MESSAGE_BEGIN( MSG_ONE, gmsgInitHUD, NULL, pev );
3959
MESSAGE_END();
3960
3961
g_pGameRules->InitHUD( this );
3962
m_fGameHUDInitialized = TRUE;
3963
3964
m_iObserverLastMode = OBS_ROAMING;
3965
3966
if ( g_pGameRules->IsMultiplayer() )
3967
{
3968
FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 );
3969
}
3970
}
3971
3972
FireTargets( "game_playerspawn", this, this, USE_TOGGLE, 0 );
3973
3974
InitStatusBar();
3975
}
3976
3977
if ( m_iHideHUD != m_iClientHideHUD )
3978
{
3979
MESSAGE_BEGIN( MSG_ONE, gmsgHideWeapon, NULL, pev );
3980
WRITE_BYTE( m_iHideHUD );
3981
MESSAGE_END();
3982
3983
m_iClientHideHUD = m_iHideHUD;
3984
}
3985
3986
if ( m_iFOV != m_iClientFOV )
3987
{
3988
MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev );
3989
WRITE_BYTE( m_iFOV );
3990
MESSAGE_END();
3991
3992
// cache FOV change at end of function, so weapon updates can see that FOV has changed
3993
}
3994
3995
// HACKHACK -- send the message to display the game title
3996
if (gDisplayTitle)
3997
{
3998
MESSAGE_BEGIN( MSG_ONE, gmsgShowGameTitle, NULL, pev );
3999
WRITE_BYTE( 0 );
4000
MESSAGE_END();
4001
gDisplayTitle = 0;
4002
}
4003
4004
if (pev->health != m_iClientHealth)
4005
{
4006
#define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) )
4007
int iHealth = clamp( pev->health, 0, 255 ); // make sure that no negative health values are sent
4008
if ( pev->health > 0.0f && pev->health <= 1.0f )
4009
iHealth = 1;
4010
4011
// send "health" update message
4012
MESSAGE_BEGIN( MSG_ONE, gmsgHealth, NULL, pev );
4013
WRITE_BYTE( iHealth );
4014
MESSAGE_END();
4015
4016
m_iClientHealth = static_cast<int>(pev->health);
4017
}
4018
4019
4020
if (pev->armorvalue != m_iClientBattery)
4021
{
4022
m_iClientBattery = static_cast<int>(pev->armorvalue);
4023
4024
ASSERT( gmsgBattery > 0 );
4025
// send "health" update message
4026
MESSAGE_BEGIN( MSG_ONE, gmsgBattery, NULL, pev );
4027
WRITE_SHORT( (int)pev->armorvalue);
4028
MESSAGE_END();
4029
}
4030
4031
if (pev->dmg_take || pev->dmg_save || m_bitsHUDDamage != m_bitsDamageType)
4032
{
4033
// Comes from inside me if not set
4034
Vector damageOrigin = pev->origin;
4035
// send "damage" message
4036
// causes screen to flash, and pain compass to show direction of damage
4037
edict_t *other = pev->dmg_inflictor;
4038
if ( other )
4039
{
4040
CBaseEntity *pEntity = CBaseEntity::Instance(other);
4041
if ( pEntity )
4042
damageOrigin = pEntity->Center();
4043
}
4044
4045
// only send down damage type that have hud art
4046
int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD;
4047
4048
MESSAGE_BEGIN( MSG_ONE, gmsgDamage, NULL, pev );
4049
WRITE_BYTE( static_cast<int>(pev->dmg_save) );
4050
WRITE_BYTE( static_cast<int>(pev->dmg_take) );
4051
WRITE_LONG( visibleDamageBits );
4052
WRITE_COORD( damageOrigin.x );
4053
WRITE_COORD( damageOrigin.y );
4054
WRITE_COORD( damageOrigin.z );
4055
MESSAGE_END();
4056
4057
pev->dmg_take = 0;
4058
pev->dmg_save = 0;
4059
m_bitsHUDDamage = m_bitsDamageType;
4060
4061
// Clear off non-time-based damage indicators
4062
m_bitsDamageType &= DMG_TIMEBASED;
4063
}
4064
4065
// Update Flashlight
4066
if ((m_flFlashLightTime) && (m_flFlashLightTime <= gpGlobals->time))
4067
{
4068
if (FlashlightIsOn())
4069
{
4070
if (m_iFlashBattery)
4071
{
4072
m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time;
4073
m_iFlashBattery--;
4074
4075
if (!m_iFlashBattery)
4076
FlashlightTurnOff();
4077
}
4078
}
4079
else
4080
{
4081
if (m_iFlashBattery < 100)
4082
{
4083
m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time;
4084
m_iFlashBattery++;
4085
}
4086
else
4087
m_flFlashLightTime = 0;
4088
}
4089
4090
MESSAGE_BEGIN( MSG_ONE, gmsgFlashBattery, NULL, pev );
4091
WRITE_BYTE(m_iFlashBattery);
4092
MESSAGE_END();
4093
}
4094
4095
4096
if (m_iTrain & TRAIN_NEW)
4097
{
4098
ASSERT( gmsgTrain > 0 );
4099
// send "health" update message
4100
MESSAGE_BEGIN( MSG_ONE, gmsgTrain, NULL, pev );
4101
WRITE_BYTE(m_iTrain & 0xF);
4102
MESSAGE_END();
4103
4104
m_iTrain &= ~TRAIN_NEW;
4105
}
4106
4107
//
4108
// New Weapon?
4109
//
4110
if (!m_fKnownItem)
4111
{
4112
m_fKnownItem = TRUE;
4113
4114
// WeaponInit Message
4115
// byte = # of weapons
4116
//
4117
// for each weapon:
4118
// byte name str length (not including null)
4119
// bytes... name
4120
// byte Ammo Type
4121
// byte Ammo2 Type
4122
// byte bucket
4123
// byte bucket pos
4124
// byte flags
4125
// ???? Icons
4126
4127
// Send ALL the weapon info now
4128
int i;
4129
4130
for (i = 0; i < MAX_WEAPONS; i++)
4131
{
4132
ItemInfo& II = CBasePlayerItem::ItemInfoArray[i];
4133
4134
if ( !II.iId )
4135
continue;
4136
4137
const char *pszName;
4138
if (!II.pszName)
4139
pszName = "Empty";
4140
else
4141
pszName = II.pszName;
4142
4143
MESSAGE_BEGIN( MSG_ONE, gmsgWeaponList, NULL, pev );
4144
WRITE_STRING(pszName); // string weapon name
4145
WRITE_BYTE(GetAmmoIndex(II.pszAmmo1)); // byte Ammo Type
4146
WRITE_BYTE(II.iMaxAmmo1); // byte Max Ammo 1
4147
WRITE_BYTE(GetAmmoIndex(II.pszAmmo2)); // byte Ammo2 Type
4148
WRITE_BYTE(II.iMaxAmmo2); // byte Max Ammo 2
4149
WRITE_BYTE(II.iSlot); // byte bucket
4150
WRITE_BYTE(II.iPosition); // byte bucket pos
4151
WRITE_BYTE(II.iId); // byte id (bit index into pev->weapons)
4152
WRITE_BYTE(II.iFlags); // byte Flags
4153
MESSAGE_END();
4154
}
4155
}
4156
4157
4158
SendAmmoUpdate();
4159
4160
// Update all the items
4161
for ( int i = 0; i < MAX_ITEM_TYPES; i++ )
4162
{
4163
if ( m_rgpPlayerItems[i] ) // each item updates it's successors
4164
m_rgpPlayerItems[i]->UpdateClientData( this );
4165
}
4166
4167
// Cache and client weapon change
4168
m_pClientActiveItem = m_pActiveItem;
4169
m_iClientFOV = m_iFOV;
4170
4171
// Update Status Bar
4172
if ( m_flNextSBarUpdateTime < gpGlobals->time )
4173
{
4174
UpdateStatusBar();
4175
m_flNextSBarUpdateTime = gpGlobals->time + 0.2;
4176
}
4177
}
4178
4179
4180
//=========================================================
4181
// FBecomeProne - Overridden for the player to set the proper
4182
// physics flags when a barnacle grabs player.
4183
//=========================================================
4184
BOOL CBasePlayer :: FBecomeProne ( void )
4185
{
4186
m_afPhysicsFlags |= PFLAG_ONBARNACLE;
4187
return TRUE;
4188
}
4189
4190
//=========================================================
4191
// BarnacleVictimBitten - bad name for a function that is called
4192
// by Barnacle victims when the barnacle pulls their head
4193
// into its mouth. For the player, just die.
4194
//=========================================================
4195
void CBasePlayer :: BarnacleVictimBitten ( entvars_t *pevBarnacle )
4196
{
4197
TakeDamage ( pevBarnacle, pevBarnacle, pev->health + pev->armorvalue, DMG_SLASH | DMG_ALWAYSGIB );
4198
}
4199
4200
//=========================================================
4201
// BarnacleVictimReleased - overridden for player who has
4202
// physics flags concerns.
4203
//=========================================================
4204
void CBasePlayer :: BarnacleVictimReleased ( void )
4205
{
4206
m_afPhysicsFlags &= ~PFLAG_ONBARNACLE;
4207
}
4208
4209
4210
//=========================================================
4211
// Illumination
4212
// return player light level plus virtual muzzle flash
4213
//=========================================================
4214
int CBasePlayer :: Illumination( void )
4215
{
4216
int iIllum = CBaseEntity::Illumination( );
4217
4218
iIllum += m_iWeaponFlash;
4219
if (iIllum > 255)
4220
return 255;
4221
return iIllum;
4222
}
4223
4224
4225
void CBasePlayer :: EnableControl(BOOL fControl)
4226
{
4227
if (!fControl)
4228
pev->flags |= FL_FROZEN;
4229
else
4230
pev->flags &= ~FL_FROZEN;
4231
4232
}
4233
4234
4235
#define DOT_1DEGREE 0.9998476951564
4236
#define DOT_2DEGREE 0.9993908270191
4237
#define DOT_3DEGREE 0.9986295347546
4238
#define DOT_4DEGREE 0.9975640502598
4239
#define DOT_5DEGREE 0.9961946980917
4240
#define DOT_6DEGREE 0.9945218953683
4241
#define DOT_7DEGREE 0.9925461516413
4242
#define DOT_8DEGREE 0.9902680687416
4243
#define DOT_9DEGREE 0.9876883405951
4244
#define DOT_10DEGREE 0.9848077530122
4245
#define DOT_15DEGREE 0.9659258262891
4246
#define DOT_20DEGREE 0.9396926207859
4247
#define DOT_25DEGREE 0.9063077870367
4248
4249
//=========================================================
4250
// Autoaim
4251
// set crosshair position to point to enemey
4252
//=========================================================
4253
Vector CBasePlayer :: GetAutoaimVector( float flDelta )
4254
{
4255
if (g_iSkillLevel == SKILL_HARD)
4256
{
4257
UTIL_MakeVectors( pev->v_angle + pev->punchangle );
4258
return gpGlobals->v_forward;
4259
}
4260
4261
Vector vecSrc = GetGunPosition( );
4262
float flDist = 8192;
4263
4264
// always use non-sticky autoaim
4265
// UNDONE: use sever variable to chose!
4266
if (1 || g_iSkillLevel == SKILL_MEDIUM)
4267
{
4268
m_vecAutoAim = Vector( 0, 0, 0 );
4269
// flDelta *= 0.5;
4270
}
4271
4272
BOOL m_fOldTargeting = m_fOnTarget;
4273
Vector angles = AutoaimDeflection(vecSrc, flDist, flDelta );
4274
4275
// update ontarget if changed
4276
if ( !g_pGameRules->AllowAutoTargetCrosshair() )
4277
m_fOnTarget = 0;
4278
else if (m_fOldTargeting != m_fOnTarget)
4279
{
4280
m_pActiveItem->UpdateItemInfo( );
4281
}
4282
4283
if (angles.x > 180)
4284
angles.x -= 360;
4285
if (angles.x < -180)
4286
angles.x += 360;
4287
if (angles.y > 180)
4288
angles.y -= 360;
4289
if (angles.y < -180)
4290
angles.y += 360;
4291
4292
if (angles.x > 25)
4293
angles.x = 25;
4294
if (angles.x < -25)
4295
angles.x = -25;
4296
if (angles.y > 12)
4297
angles.y = 12;
4298
if (angles.y < -12)
4299
angles.y = -12;
4300
4301
4302
// always use non-sticky autoaim
4303
// UNDONE: use sever variable to chose!
4304
if (0 || g_iSkillLevel == SKILL_EASY)
4305
{
4306
m_vecAutoAim = m_vecAutoAim * 0.67 + angles * 0.33;
4307
}
4308
else
4309
{
4310
m_vecAutoAim = angles * 0.9;
4311
}
4312
4313
// m_vecAutoAim = m_vecAutoAim * 0.99;
4314
4315
// Don't send across network if sv_aim is 0
4316
if ( g_psv_aim->value != 0 )
4317
{
4318
if ( m_vecAutoAim.x != m_lastx ||
4319
m_vecAutoAim.y != m_lasty )
4320
{
4321
SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y );
4322
4323
m_lastx = static_cast<int>(m_vecAutoAim.x);
4324
m_lasty = static_cast<int>(m_vecAutoAim.y);
4325
}
4326
}
4327
4328
// ALERT( at_console, "%f %f\n", angles.x, angles.y );
4329
4330
UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim );
4331
return gpGlobals->v_forward;
4332
}
4333
4334
4335
Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta )
4336
{
4337
edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 );
4338
CBaseEntity *pEntity;
4339
float bestdot;
4340
Vector bestdir;
4341
edict_t *bestent;
4342
TraceResult tr;
4343
4344
if ( g_psv_aim->value == 0 )
4345
{
4346
m_fOnTarget = FALSE;
4347
return g_vecZero;
4348
}
4349
4350
UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim );
4351
4352
// try all possible entities
4353
bestdir = gpGlobals->v_forward;
4354
bestdot = flDelta; // +- 10 degrees
4355
bestent = NULL;
4356
4357
m_fOnTarget = FALSE;
4358
4359
UTIL_TraceLine( vecSrc, vecSrc + bestdir * flDist, dont_ignore_monsters, edict(), &tr );
4360
4361
4362
if ( tr.pHit && tr.pHit->v.takedamage != DAMAGE_NO)
4363
{
4364
// don't look through water
4365
if (!((pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3)
4366
|| (pev->waterlevel == 3 && tr.pHit->v.waterlevel == 0)))
4367
{
4368
if (tr.pHit->v.takedamage == DAMAGE_AIM)
4369
m_fOnTarget = TRUE;
4370
4371
return m_vecAutoAim;
4372
}
4373
}
4374
4375
for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ )
4376
{
4377
Vector center;
4378
Vector dir;
4379
float dot;
4380
4381
if ( pEdict->free ) // Not in use
4382
continue;
4383
4384
if (pEdict->v.takedamage != DAMAGE_AIM)
4385
continue;
4386
if (pEdict == edict())
4387
continue;
4388
// if (pev->team > 0 && pEdict->v.team == pev->team)
4389
// continue; // don't aim at teammate
4390
if ( !g_pGameRules->ShouldAutoAim( this, pEdict ) )
4391
continue;
4392
4393
pEntity = Instance( pEdict );
4394
if (pEntity == NULL)
4395
continue;
4396
4397
if (!pEntity->IsAlive())
4398
continue;
4399
4400
// don't look through water
4401
if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3)
4402
|| (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0))
4403
continue;
4404
4405
center = pEntity->BodyTarget( vecSrc );
4406
4407
dir = (center - vecSrc).Normalize( );
4408
4409
// make sure it's in front of the player
4410
if (DotProduct (dir, gpGlobals->v_forward ) < 0)
4411
continue;
4412
4413
dot = fabs( DotProduct (dir, gpGlobals->v_right ) )
4414
+ fabs( DotProduct (dir, gpGlobals->v_up ) ) * 0.5;
4415
4416
// tweek for distance
4417
dot *= 1.0 + 0.2 * ((center - vecSrc).Length() / flDist);
4418
4419
if (dot > bestdot)
4420
continue; // to far to turn
4421
4422
UTIL_TraceLine( vecSrc, center, dont_ignore_monsters, edict(), &tr );
4423
if (tr.flFraction != 1.0 && tr.pHit != pEdict)
4424
{
4425
// ALERT( at_console, "hit %s, can't see %s\n", STRING( tr.pHit->v.classname ), STRING( pEdict->v.classname ) );
4426
continue;
4427
}
4428
4429
// don't shoot at friends
4430
if (IRelationship( pEntity ) < 0)
4431
{
4432
if ( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch())
4433
// ALERT( at_console, "friend\n");
4434
continue;
4435
}
4436
4437
// can shoot at this one
4438
bestdot = dot;
4439
bestent = pEdict;
4440
bestdir = dir;
4441
}
4442
4443
if (bestent)
4444
{
4445
bestdir = UTIL_VecToAngles (bestdir);
4446
bestdir.x = -bestdir.x;
4447
bestdir = bestdir - pev->v_angle - pev->punchangle;
4448
4449
if (bestent->v.takedamage == DAMAGE_AIM)
4450
m_fOnTarget = TRUE;
4451
4452
return bestdir;
4453
}
4454
4455
return Vector( 0, 0, 0 );
4456
}
4457
4458
4459
void CBasePlayer :: ResetAutoaim( )
4460
{
4461
if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0)
4462
{
4463
m_vecAutoAim = Vector( 0, 0, 0 );
4464
SET_CROSSHAIRANGLE( edict(), 0, 0 );
4465
}
4466
m_fOnTarget = FALSE;
4467
}
4468
4469
/*
4470
=============
4471
SetCustomDecalFrames
4472
4473
UNDONE: Determine real frame limit, 8 is a placeholder.
4474
Note: -1 means no custom frames present.
4475
=============
4476
*/
4477
void CBasePlayer :: SetCustomDecalFrames( int nFrames )
4478
{
4479
if (nFrames > 0 &&
4480
nFrames < 8)
4481
m_nCustomSprayFrames = nFrames;
4482
else
4483
m_nCustomSprayFrames = -1;
4484
}
4485
4486
/*
4487
=============
4488
GetCustomDecalFrames
4489
4490
Returns the # of custom frames this player's custom clan logo contains.
4491
=============
4492
*/
4493
int CBasePlayer :: GetCustomDecalFrames( void )
4494
{
4495
return m_nCustomSprayFrames;
4496
}
4497
4498
4499
//=========================================================
4500
// DropPlayerItem - drop the named item, or if no name,
4501
// the active item.
4502
//=========================================================
4503
void CBasePlayer::DropPlayerItem ( char *pszItemName )
4504
{
4505
if ( !g_pGameRules->IsMultiplayer() || (weaponstay.value > 0) )
4506
{
4507
// no dropping in single player.
4508
return;
4509
}
4510
4511
if ( !strlen( pszItemName ) )
4512
{
4513
// if this string has no length, the client didn't type a name!
4514
// assume player wants to drop the active item.
4515
// make the string null to make future operations in this function easier
4516
pszItemName = NULL;
4517
}
4518
4519
CBasePlayerItem *pWeapon;
4520
int i;
4521
4522
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
4523
{
4524
pWeapon = m_rgpPlayerItems[ i ];
4525
4526
while ( pWeapon )
4527
{
4528
if ( pszItemName )
4529
{
4530
// try to match by name.
4531
if ( !strcmp( pszItemName, STRING( pWeapon->pev->classname ) ) )
4532
{
4533
// match!
4534
break;
4535
}
4536
}
4537
else
4538
{
4539
// trying to drop active item
4540
if ( pWeapon == m_pActiveItem )
4541
{
4542
// active item!
4543
break;
4544
}
4545
}
4546
4547
pWeapon = pWeapon->m_pNext;
4548
}
4549
4550
4551
// if we land here with a valid pWeapon pointer, that's because we found the
4552
// item we want to drop and hit a BREAK; pWeapon is the item.
4553
if ( pWeapon )
4554
{
4555
if ( !g_pGameRules->GetNextBestWeapon( this, pWeapon ) )
4556
return; // can't drop the item they asked for, may be our last item or something we can't holster
4557
4558
UTIL_MakeVectors ( pev->angles );
4559
4560
pev->weapons &= ~(1<<pWeapon->m_iId);// take item off hud
4561
4562
CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin + gpGlobals->v_forward * 10, pev->angles, edict() );
4563
pWeaponBox->pev->angles.x = 0;
4564
pWeaponBox->pev->angles.z = 0;
4565
pWeaponBox->PackWeapon( pWeapon );
4566
pWeaponBox->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100;
4567
4568
// drop half of the ammo for this weapon.
4569
int iAmmoIndex;
4570
4571
iAmmoIndex = GetAmmoIndex ( pWeapon->pszAmmo1() ); // ???
4572
4573
if ( iAmmoIndex != -1 )
4574
{
4575
// this weapon weapon uses ammo, so pack an appropriate amount.
4576
if ( pWeapon->iFlags() & ITEM_FLAG_EXHAUSTIBLE )
4577
{
4578
// pack up all the ammo, this weapon is its own ammo type
4579
pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] );
4580
m_rgAmmo[ iAmmoIndex ] = 0;
4581
4582
}
4583
else
4584
{
4585
// pack half of the ammo
4586
pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] / 2 );
4587
m_rgAmmo[ iAmmoIndex ] /= 2;
4588
}
4589
4590
}
4591
4592
return;// we're done, so stop searching with the FOR loop.
4593
}
4594
}
4595
}
4596
4597
//=========================================================
4598
// HasPlayerItem Does the player already have this item?
4599
//=========================================================
4600
BOOL CBasePlayer::HasPlayerItem( CBasePlayerItem *pCheckItem )
4601
{
4602
CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()];
4603
4604
while (pItem)
4605
{
4606
if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) ))
4607
{
4608
return TRUE;
4609
}
4610
pItem = pItem->m_pNext;
4611
}
4612
4613
return FALSE;
4614
}
4615
4616
//=========================================================
4617
// HasNamedPlayerItem Does the player already have this item?
4618
//=========================================================
4619
BOOL CBasePlayer::HasNamedPlayerItem( const char *pszItemName )
4620
{
4621
CBasePlayerItem *pItem;
4622
int i;
4623
4624
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
4625
{
4626
pItem = m_rgpPlayerItems[ i ];
4627
4628
while (pItem)
4629
{
4630
if ( !strcmp( pszItemName, STRING( pItem->pev->classname ) ) )
4631
{
4632
return TRUE;
4633
}
4634
pItem = pItem->m_pNext;
4635
}
4636
}
4637
4638
return FALSE;
4639
}
4640
4641
//=========================================================
4642
//
4643
//=========================================================
4644
BOOL CBasePlayer :: SwitchWeapon( CBasePlayerItem *pWeapon )
4645
{
4646
if ( !pWeapon->CanDeploy() )
4647
{
4648
return FALSE;
4649
}
4650
4651
ResetAutoaim( );
4652
4653
if (m_pActiveItem)
4654
{
4655
m_pActiveItem->Holster( );
4656
}
4657
4658
m_pActiveItem = pWeapon;
4659
pWeapon->Deploy( );
4660
4661
return TRUE;
4662
}
4663
4664
//=========================================================
4665
// Dead HEV suit prop
4666
//=========================================================
4667
class CDeadHEV : public CBaseMonster
4668
{
4669
public:
4670
void Spawn( void );
4671
int Classify ( void ) { return CLASS_HUMAN_MILITARY; }
4672
4673
void KeyValue( KeyValueData *pkvd );
4674
4675
int m_iPose;// which sequence to display -- temporary, don't need to save
4676
static const char *m_szPoses[4];
4677
};
4678
4679
const char *CDeadHEV::m_szPoses[] = { "deadback", "deadsitting", "deadstomach", "deadtable" };
4680
4681
void CDeadHEV::KeyValue( KeyValueData *pkvd )
4682
{
4683
if (FStrEq(pkvd->szKeyName, "pose"))
4684
{
4685
m_iPose = atoi(pkvd->szValue);
4686
pkvd->fHandled = TRUE;
4687
}
4688
else
4689
CBaseMonster::KeyValue( pkvd );
4690
}
4691
4692
LINK_ENTITY_TO_CLASS( monster_hevsuit_dead, CDeadHEV );
4693
4694
//=========================================================
4695
// ********** DeadHEV SPAWN **********
4696
//=========================================================
4697
void CDeadHEV :: Spawn( void )
4698
{
4699
PRECACHE_MODEL("models/player.mdl");
4700
SET_MODEL(ENT(pev), "models/player.mdl");
4701
4702
pev->effects = 0;
4703
pev->yaw_speed = 8;
4704
pev->sequence = 0;
4705
pev->body = 1;
4706
m_bloodColor = BLOOD_COLOR_RED;
4707
4708
pev->sequence = LookupSequence( m_szPoses[m_iPose] );
4709
4710
if (pev->sequence == -1)
4711
{
4712
ALERT ( at_console, "Dead hevsuit with bad pose\n" );
4713
pev->sequence = 0;
4714
pev->effects = EF_BRIGHTFIELD;
4715
}
4716
4717
// Corpses have less health
4718
pev->health = 8;
4719
4720
MonsterInitDead();
4721
}
4722
4723
4724
class CStripWeapons : public CPointEntity
4725
{
4726
public:
4727
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
4728
4729
private:
4730
};
4731
4732
LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons );
4733
4734
void CStripWeapons :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
4735
{
4736
CBasePlayer *pPlayer = NULL;
4737
4738
if ( pActivator && pActivator->IsPlayer() )
4739
{
4740
pPlayer = (CBasePlayer *)pActivator;
4741
}
4742
else if ( !g_pGameRules->IsDeathmatch() )
4743
{
4744
pPlayer = (CBasePlayer *)CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) );
4745
}
4746
4747
if ( pPlayer )
4748
pPlayer->RemoveAllItems( FALSE );
4749
}
4750
4751
4752
class CRevertSaved : public CPointEntity
4753
{
4754
public:
4755
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
4756
void EXPORT MessageThink( void );
4757
void EXPORT LoadThink( void );
4758
void KeyValue( KeyValueData *pkvd );
4759
4760
virtual int Save( CSave &save );
4761
virtual int Restore( CRestore &restore );
4762
static TYPEDESCRIPTION m_SaveData[];
4763
4764
inline float Duration( void ) { return pev->dmg_take; }
4765
inline float HoldTime( void ) { return pev->dmg_save; }
4766
inline float MessageTime( void ) { return m_messageTime; }
4767
inline float LoadTime( void ) { return m_loadTime; }
4768
4769
inline void SetDuration( float duration ) { pev->dmg_take = duration; }
4770
inline void SetHoldTime( float hold ) { pev->dmg_save = hold; }
4771
inline void SetMessageTime( float time ) { m_messageTime = time; }
4772
inline void SetLoadTime( float time ) { m_loadTime = time; }
4773
4774
private:
4775
float m_messageTime;
4776
float m_loadTime;
4777
};
4778
4779
LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved );
4780
4781
TYPEDESCRIPTION CRevertSaved::m_SaveData[] =
4782
{
4783
DEFINE_FIELD( CRevertSaved, m_messageTime, FIELD_FLOAT ), // These are not actual times, but durations, so save as floats
4784
DEFINE_FIELD( CRevertSaved, m_loadTime, FIELD_FLOAT ),
4785
};
4786
4787
IMPLEMENT_SAVERESTORE( CRevertSaved, CPointEntity );
4788
4789
void CRevertSaved :: KeyValue( KeyValueData *pkvd )
4790
{
4791
if (FStrEq(pkvd->szKeyName, "duration"))
4792
{
4793
SetDuration( atof(pkvd->szValue) );
4794
pkvd->fHandled = TRUE;
4795
}
4796
else if (FStrEq(pkvd->szKeyName, "holdtime"))
4797
{
4798
SetHoldTime( atof(pkvd->szValue) );
4799
pkvd->fHandled = TRUE;
4800
}
4801
else if (FStrEq(pkvd->szKeyName, "messagetime"))
4802
{
4803
SetMessageTime( atof(pkvd->szValue) );
4804
pkvd->fHandled = TRUE;
4805
}
4806
else if (FStrEq(pkvd->szKeyName, "loadtime"))
4807
{
4808
SetLoadTime( atof(pkvd->szValue) );
4809
pkvd->fHandled = TRUE;
4810
}
4811
else
4812
CPointEntity::KeyValue( pkvd );
4813
}
4814
4815
void CRevertSaved :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
4816
{
4817
UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), static_cast<int>(pev->renderamt), FFADE_OUT );
4818
pev->nextthink = gpGlobals->time + MessageTime();
4819
SetThink( &CRevertSaved::MessageThink );
4820
}
4821
4822
4823
void CRevertSaved :: MessageThink( void )
4824
{
4825
UTIL_ShowMessageAll( STRING(pev->message) );
4826
float nextThink = LoadTime() - MessageTime();
4827
if ( nextThink > 0 )
4828
{
4829
pev->nextthink = gpGlobals->time + nextThink;
4830
SetThink( &CRevertSaved::LoadThink );
4831
}
4832
else
4833
LoadThink();
4834
}
4835
4836
4837
void CRevertSaved :: LoadThink( void )
4838
{
4839
if ( !gpGlobals->deathmatch )
4840
{
4841
SERVER_COMMAND("reload\n");
4842
}
4843
}
4844
4845
4846
//=========================================================
4847
// Multiplayer intermission spots.
4848
//=========================================================
4849
class CInfoIntermission:public CPointEntity
4850
{
4851
void Spawn( void );
4852
void Think( void );
4853
};
4854
4855
void CInfoIntermission::Spawn( void )
4856
{
4857
UTIL_SetOrigin( pev, pev->origin );
4858
pev->solid = SOLID_NOT;
4859
pev->effects = EF_NODRAW;
4860
pev->v_angle = g_vecZero;
4861
4862
pev->nextthink = gpGlobals->time + 2;// let targets spawn!
4863
4864
}
4865
4866
void CInfoIntermission::Think ( void )
4867
{
4868
edict_t *pTarget;
4869
4870
// find my target
4871
pTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(pev->target) );
4872
4873
if ( !FNullEnt(pTarget) )
4874
{
4875
pev->v_angle = UTIL_VecToAngles( (pTarget->v.origin - pev->origin).Normalize() );
4876
pev->v_angle.x = -pev->v_angle.x;
4877
}
4878
}
4879
4880
LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission );
4881