/*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ /* ===== player.cpp ======================================================== functions dealing with the player */ #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #include "trains.h" #include "nodes.h" #include "weapons.h" #include "soundent.h" #include "monsters.h" #include "shake.h" #include "decals.h" #include "gamerules.h" #include "game.h" #include "hltv.h" #include "effects.h" //LRC #include "movewith.h" //LRC #include // #define DUCKFIX extern DLL_GLOBAL ULONG g_ulModelIndexPlayer; extern DLL_GLOBAL BOOL g_fGameOver; extern DLL_GLOBAL BOOL g_fDrawLines; int gEvilImpulse101; BOOL g_markFrameBounds = 0; //LRC extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle; BOOL gInitHUD = TRUE; extern void CopyToBodyQue(entvars_t* pev); extern void respawn(entvars_t *pev, BOOL fCopyCorpse); extern Vector VecBModelOrigin(entvars_t *pevBModel ); extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ); // the world node graph extern CGraph WorldGraph; #define GLOBAL_TIME_STEP 0.002f // ~ one minute is equal 10 seconds like in S.T.A.L.K.E.R // buz: flags for goal panel #define TP_FL_ENABLE 1 #define TP_FL_TITLE 2 #define TP_FL_IMAGE 4 #define TP_FL_POPUP 8 #define TRAIN_ACTIVE 0x80 #define TRAIN_NEW 0xc0 #define TRAIN_OFF 0x00 #define TRAIN_NEUTRAL 0x01 #define TRAIN_SLOW 0x02 #define TRAIN_MEDIUM 0x03 #define TRAIN_FAST 0x04 #define TRAIN_BACK 0x05 #define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes #define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit) #define GAS_DAMAGE_LENGTH 45.0f; // Global Savedata for player TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = { DEFINE_FIELD( CBasePlayer, m_flFlashLightTime, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_iFlashBattery, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_flStaminaValue, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_afButtonLast, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_afButtonPressed, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_afButtonReleased, FIELD_INTEGER ), DEFINE_ARRAY( CBasePlayer, m_rgItems, FIELD_INTEGER, MAX_ITEMS ), DEFINE_FIELD( CBasePlayer, m_afPhysicsFlags, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_flTimeStepSound, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_flTimeWeaponIdle, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_flSwimTime, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_flDuckTime, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_flWallJumpTime, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_flSuitUpdate, FIELD_TIME ), DEFINE_ARRAY( CBasePlayer, m_rgSuitPlayList, FIELD_INTEGER, CSUITPLAYLIST ), DEFINE_FIELD( CBasePlayer, m_iSuitPlayNext, FIELD_INTEGER ), DEFINE_ARRAY( CBasePlayer, m_rgiSuitNoRepeat, FIELD_INTEGER, CSUITNOREPEAT ), DEFINE_ARRAY( CBasePlayer, m_rgflSuitNoRepeatTime, FIELD_TIME, CSUITNOREPEAT ), DEFINE_FIELD( CBasePlayer, m_lastDamageAmount, FIELD_INTEGER ), DEFINE_ARRAY( CBasePlayer, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), DEFINE_FIELD( CBasePlayer, m_pActiveItem, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayer, m_pLastItem, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayer, m_pNextItem, FIELD_CLASSPTR ), DEFINE_ARRAY( CBasePlayer, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ), DEFINE_FIELD( CBasePlayer, m_idrowndmg, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_idrownrestored, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_tSneaking, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_iTrain, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_bitsHUDDamage, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_flFallVelocity, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, m_iTargetVolume, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_iWeaponVolume, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_iExtraSoundTypes, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_iWeaponFlash, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_fLongJump, FIELD_BOOLEAN ), DEFINE_FIELD( CBasePlayer, m_fInitHUD, FIELD_BOOLEAN ), DEFINE_FIELD( CBasePlayer, m_tbdPrev, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ), // NB: this points to a CFuncTank*Controls* now. --LRC DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ), // buz DEFINE_FIELD( CBasePlayer, m_iGasMaskOn, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_flGasMaskTime, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_iHeadShieldOn, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_pSpecTank, FIELD_CLASSPTR ), DEFINE_FIELD( CBasePlayer, m_strCurrentGoalName, FIELD_STRING ), DEFINE_FIELD( CBasePlayer, m_strCurrentGoalImageName, FIELD_STRING ), DEFINE_FIELD( CBasePlayer, m_strCurrentGoalTitleName, FIELD_STRING ), DEFINE_FIELD( CBasePlayer, m_iJumpHeight, FIELD_INTEGER ), // buz: rain stuff DEFINE_FIELD( CBasePlayer, Rain_dripsPerSecond, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, Rain_windX, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_windY, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_randX, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_randY, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_ideal_dripsPerSecond, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, Rain_ideal_windX, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_ideal_windY, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_ideal_randX, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_ideal_randY, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, Rain_endFade, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, Rain_nextFadeUpdate, FIELD_TIME ), DEFINE_FIELD( CBasePlayer, m_flBlurAmount, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, m_flCurBlurAmount, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, m_flLastBlurAmount, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, m_flBlurFadeTime, FIELD_TIME ), // buz: real lightlevel DEFINE_FIELD( CBasePlayer, m_fLightlevel, FIELD_FLOAT ), DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), //DEFINE_FIELD( CBasePlayer, m_fDeadTime, FIELD_FLOAT ), // only used in multiplayer games //DEFINE_FIELD( CBasePlayer, m_fGameHUDInitialized, FIELD_INTEGER ), // only used in multiplayer games //DEFINE_FIELD( CBasePlayer, m_flStopExtraSoundTime, FIELD_TIME ), //DEFINE_FIELD( CBasePlayer, m_fKnownItem, FIELD_INTEGER ), // reset to zero on load //DEFINE_FIELD( CBasePlayer, m_iPlayerSound, FIELD_INTEGER ), // Don't restore, set in Precache() //DEFINE_FIELD( CBasePlayer, m_pentSndLast, FIELD_EDICT ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_flSndRoomtype, FIELD_FLOAT ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_flSndRange, FIELD_FLOAT ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_fNewAmmo, FIELD_INTEGER ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_flgeigerRange, FIELD_FLOAT ), // Don't restore, reset in Precache() //DEFINE_FIELD( CBasePlayer, m_flgeigerDelay, FIELD_FLOAT ), // Don't restore, reset in Precache() //DEFINE_FIELD( CBasePlayer, m_igeigerRangePrev, FIELD_FLOAT ), // Don't restore, reset in Precache() //DEFINE_FIELD( CBasePlayer, m_iStepLeft, FIELD_INTEGER ), // Don't need to restore //DEFINE_FIELD( CBasePlayer, m_fNoPlayerSound, FIELD_BOOLEAN ), // Don't need to restore, debug //DEFINE_FIELD( CBasePlayer, m_iUpdateTime, FIELD_INTEGER ), // Don't need to restore //DEFINE_FIELD( CBasePlayer, m_iClientHealth, FIELD_INTEGER ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_iClientBattery, FIELD_INTEGER ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_iClientHideHUD, FIELD_INTEGER ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_fWeapon, FIELD_BOOLEAN ), // Don't restore, client needs reset //DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), // Don't restore, depends on server message after spawning and only matters in multiplayer //DEFINE_FIELD( CBasePlayer, m_vecAutoAim, FIELD_VECTOR ), // Don't save/restore - this is recomputed //DEFINE_ARRAY( CBasePlayer, m_rgAmmoLast, FIELD_INTEGER, MAX_AMMO_SLOTS ), // Don't need to restore //DEFINE_FIELD( CBasePlayer, m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), DEFINE_ARRAY( CBasePlayer, m_szAnimExtention, FIELD_CHARACTER, 32 ), }; int giPrecacheGrunt = 0; int gmsgShake = 0; int gmsgFade = 0; int gmsgSelAmmo = 0; int gmsgFlashlight = 0; int gmsgFlashBattery = 0; int gmsgResetHUD = 0; int gmsgInitHUD = 0; int gmsgSetFog = 0; //LRC int gmsgKeyedDLight = 0;//LRC int gmsgSetSky = 0; //LRC int gmsgHUDColor = 0; //LRC int gmsgParticle = 0; // LRC int gmsgDelParticle = 0; // buz int gmsgShowGameTitle = 0; int gmsgCurWeapon = 0; int gmsgHealth = 0; int gmsgStamina = 0; int gmsgDamage = 0; int gmsgBattery = 0; int gmsgTrain = 0; int gmsgLogo = 0; int gmsgWeaponList = 0; int gmsgAmmoX = 0; int gmsgHudText = 0; int gmsgDeathMsg = 0; int gmsgScoreInfo = 0; int gmsgTeamInfo = 0; int gmsgTeamScore = 0; int gmsgGameMode = 0; int gmsgMOTD = 0; int gmsgServerName = 0; int gmsgAmmoPickup = 0; int gmsgWeapPickup = 0; int gmsgItemPickup = 0; int gmsgHideWeapon = 0; int gmsgSetCurWeap = 0; int gmsgSayText = 0; int gmsgTextMsg = 0; int gmsgSetFOV = 0; int gmsgShowMenu = 0; int gmsgGeigerRange = 0; int gmsgTeamNames = 0; int gmsgStatusIcon = 0; //LRC int gmsgStatusText = 0; int gmsgStatusValue = 0; int gmsgGasMask = 0; // buz int gmsgSpecTank = 0; // buz int gmsgTextWindow = 0; // buz int gmsgRainData = 0; // rain int gmsgHeadShield = 0; // buz int gmsgRadioIcon = 0; // buz int gmsgTabPanel = 0; // buz int gmsgCustomDecal = 0; // buz int gmsgStudioDecal = 0; // g-cont int gmsgCustomDLight = 0; // buz int gmsgSkymarker_Sky = 0; // buz int gmsgSkymarker_World = 0; // buz int gmsgWaterSplash = 0; // lev int gmsgNewExplode = 0; // lev int gmsgMusicFade = 0; int gmsgWeaponAnim = 0; int gmsgWeaponBody = 0; int gmsgWeaponSkin = 0; int gmsgPartEffect = 0; int gmsgBlurEffect = 0; int gmsgLevelTime = 0; // Wargon: Иконка юза. int gmsgCanUse = 0; void LinkUserMessages( void ) { // Already taken care of? if ( gmsgSelAmmo ) { return; } gmsgSelAmmo = REG_USER_MSG("SelAmmo", sizeof(SelAmmo)); gmsgCurWeapon = REG_USER_MSG("CurWeapon", 3); gmsgGeigerRange = REG_USER_MSG("Geiger", 1); gmsgFlashlight = REG_USER_MSG("Flashlight", 2); gmsgFlashBattery = REG_USER_MSG("FlashBat", 1); gmsgStamina = REG_USER_MSG( "Stamina", 2); gmsgHealth = REG_USER_MSG( "Health", 1 ); gmsgDamage = REG_USER_MSG( "Damage", 12 ); gmsgBattery = REG_USER_MSG( "Battery", 2); gmsgTrain = REG_USER_MSG( "Train", 1); gmsgHudText = REG_USER_MSG( "HudText", -1 ); gmsgSayText = REG_USER_MSG( "SayText", -1 ); gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); gmsgWeaponList = REG_USER_MSG("WeaponList", -1); gmsgResetHUD = REG_USER_MSG("ResetHUD", 1); // called every respawn gmsgInitHUD = REG_USER_MSG("InitHUD", 0 ); // called every time a new player joins the server gmsgSetFog = REG_USER_MSG("SetFog", 9 ); //LRC gmsgKeyedDLight = REG_USER_MSG("KeyedDLight", -1 ); //LRC gmsgSetSky = REG_USER_MSG( "SetSky", 7 ); //LRC gmsgHUDColor = REG_USER_MSG( "HUDColor", 4 ); //LRC gmsgParticle = REG_USER_MSG( "Particle", -1); //LRC gmsgDelParticle = REG_USER_MSG( "DelParticle", -1); //buz gmsgShowGameTitle = REG_USER_MSG("GameTitle", 1); gmsgDeathMsg = REG_USER_MSG( "DeathMsg", -1 ); gmsgScoreInfo = REG_USER_MSG( "ScoreInfo", 9 ); gmsgTeamInfo = REG_USER_MSG( "TeamInfo", -1 ); // sets the name of a player's team gmsgTeamScore = REG_USER_MSG( "TeamScore", -1 ); // sets the score of a team on the scoreboard gmsgGameMode = REG_USER_MSG( "GameMode", 1 ); gmsgMOTD = REG_USER_MSG( "MOTD", -1 ); gmsgServerName = REG_USER_MSG( "ServerName", -1 ); gmsgAmmoPickup = REG_USER_MSG( "AmmoPickup", -1); //2 ); buz - ammo name string also sended gmsgWeapPickup = REG_USER_MSG( "WeapPickup", 1 ); gmsgItemPickup = REG_USER_MSG( "ItemPickup", -1 ); gmsgHideWeapon = REG_USER_MSG( "HideWeapon", 1 ); gmsgSetFOV = REG_USER_MSG( "SetFOV", 1 ); gmsgShowMenu = REG_USER_MSG( "ShowMenu", -1 ); gmsgShake = REG_USER_MSG("ScreenShake", sizeof(ScreenShake)); gmsgFade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade)); gmsgAmmoX = REG_USER_MSG("AmmoX", 2); gmsgTeamNames = REG_USER_MSG( "TeamNames", -1 ); gmsgStatusIcon = REG_USER_MSG( "StatusIcon", -1 ); gmsgStatusText = REG_USER_MSG("StatusText", -1); gmsgStatusValue = REG_USER_MSG("StatusValue", 3); gmsgGasMask = REG_USER_MSG("GasMask", 1); // buz gmsgSpecTank = REG_USER_MSG("SpecTank", -1); // buz gmsgTextWindow = REG_USER_MSG("TextWindow", -1); // buz gmsgRainData = REG_USER_MSG("RainData", 16); // buz rain gmsgHeadShield = REG_USER_MSG("HeadShield", 1); // buz gmsgRadioIcon = REG_USER_MSG("RadioIcon", -1); // buz gmsgTabPanel = REG_USER_MSG("TabPanel", -1); // buz gmsgCustomDecal = REG_USER_MSG("CustomDecal", -1); // buz gmsgStudioDecal = REG_USER_MSG("StudioDecal", -1); // g-cont gmsgCustomDLight = REG_USER_MSG("mydlight", -1); // buz gmsgSkymarker_Sky = REG_USER_MSG( "SkyMarker", -1 ); // buz gmsgSkymarker_World = REG_USER_MSG( "WorldMarker", -1 ); // buz gmsgWaterSplash = REG_USER_MSG( "WaterSplash", -1 ); // lev gmsgNewExplode = REG_USER_MSG( "NewExplode", -1 ); // lev gmsgMusicFade = REG_USER_MSG( "MusicFade", 2 ); gmsgWeaponAnim = REG_USER_MSG( "WeaponAnim", 2 ); // anim + framerate gmsgWeaponBody = REG_USER_MSG( "WeaponBody", 1 ); gmsgWeaponSkin = REG_USER_MSG( "WeaponSkin", 1 ); gmsgPartEffect = REG_USER_MSG( "PartEffect", -1 ); gmsgBlurEffect = REG_USER_MSG( "BlurEffect", 2 ); gmsgLevelTime = REG_USER_MSG( "LevelTime", 4 ); // Wargon: Иконка юза. gmsgCanUse = REG_USER_MSG("CanUse", -1); } LINK_ENTITY_TO_CLASS( player, CBasePlayer ); void CBasePlayer :: Pain( void ) { float flRndSound;//sound randomizer flRndSound = RANDOM_FLOAT ( 0 , 1 ); if ( flRndSound <= 0.33 ) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); else if ( flRndSound <= 0.66 ) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); else EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); } /* * */ Vector VecVelocityForDamage(float flDamage) { Vector vec(RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); if (flDamage > -50) vec = vec * 0.7; else if (flDamage > -200) vec = vec * 2; else vec = vec * 10; return vec; } #if 0 /* static void ThrowGib(entvars_t *pev, char *szGibModel, float flDamage) { edict_t *pentNew = CREATE_ENTITY(); entvars_t *pevNew = VARS(pentNew); pevNew->origin = pev->origin; SET_MODEL(ENT(pevNew), szGibModel); UTIL_SetSize(pevNew, g_vecZero, g_vecZero); pevNew->velocity = VecVelocityForDamage(flDamage); pevNew->movetype = MOVETYPE_BOUNCE; pevNew->solid = SOLID_NOT; pevNew->avelocity.x = RANDOM_FLOAT(0,600); pevNew->avelocity.y = RANDOM_FLOAT(0,600); pevNew->avelocity.z = RANDOM_FLOAT(0,600); CHANGE_METHOD(ENT(pevNew), em_think, SUB_Remove); pevNew->ltime = gpGlobals->time; pevNew->nextthink = gpGlobals->time + RANDOM_FLOAT(10,20); pevNew->frame = 0; pevNew->flags = 0; } static void ThrowHead(entvars_t *pev, char *szGibModel, floatflDamage) { SET_MODEL(ENT(pev), szGibModel); pev->frame = 0; pev->nextthink = -1; pev->movetype = MOVETYPE_BOUNCE; pev->takedamage = DAMAGE_NO; pev->solid = SOLID_NOT; pev->view_ofs = Vector(0,0,8); UTIL_SetSize(pev, Vector(-16,-16,0), Vector(16,16,56)); pev->velocity = VecVelocityForDamage(flDamage); pev->avelocity = RANDOM_FLOAT(-1,1) * Vector(0,600,0); pev->origin.z -= 24; ClearBits(pev->flags, FL_ONGROUND); } */ #endif int TrainSpeed(int iSpeed, int iMax) { float fSpeed, fMax; int iRet = 0; fMax = (float)iMax; fSpeed = iSpeed; fSpeed = fSpeed/fMax; if (iSpeed < 0) iRet = TRAIN_BACK; else if (iSpeed == 0) iRet = TRAIN_NEUTRAL; else if (fSpeed < 0.33) iRet = TRAIN_SLOW; else if (fSpeed < 0.66) iRet = TRAIN_MEDIUM; else iRet = TRAIN_FAST; return iRet; } void CBasePlayer :: DeathSound( void ) { // temporarily using pain sounds for death sounds switch (RANDOM_LONG(1,5)) { case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM); break; case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM); break; case 3: EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM); break; } } // override takehealth // bitsDamageType indicates type of damage healed. int CBasePlayer :: TakeHealth( float flHealth, int bitsDamageType ) { return CBaseMonster :: TakeHealth (flHealth, bitsDamageType); } Vector CBasePlayer :: GetGunPosition( ) { return pev->origin + pev->view_ofs; } //========================================================= // TraceAttack //========================================================= void CBasePlayer :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) { if ( pev->takedamage ) { m_LastHitGroup = ptr->iHitgroup; switch ( ptr->iHitgroup ) { case HITGROUP_GENERIC: break; case HITGROUP_HEAD: // buz: no head damage if wearing shield if (m_iHeadShieldOn) { UTIL_Ricochet( ptr->vecEndPos, 1.0 ); pev->punchangle.x -= 2; return; } flDamage *= gSkillData.plrHead; break; case HITGROUP_CHEST: flDamage *= gSkillData.plrChest; break; case HITGROUP_STOMACH: flDamage *= gSkillData.plrStomach; break; case HITGROUP_LEFTARM: case HITGROUP_RIGHTARM: flDamage *= gSkillData.plrArm; break; case HITGROUP_LEFTLEG: case HITGROUP_RIGHTLEG: flDamage *= gSkillData.plrLeg; break; default: break; } // buz: player dont bleeds // SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. // TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); } } /* Take some damage. NOTE: each call to TakeDamage with bitsDamageType set to a time-based damage type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC. */ #define ARMOR_RATIO 0.2 // Armor Takes 80% of the damage #define ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health int CBasePlayer :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { // have suit diagnose the problem - ie: report damage type int bitsDamage = bitsDamageType; int ffound = TRUE; int fmajor; int fcritical; int fTookDamage; int ftrivial; float flRatio; float flBonus; float flHealthPrev = pev->health; flBonus = ARMOR_BONUS; flRatio = ARMOR_RATIO; UTIL_ScreenFade((CBaseEntity*)this,Vector(150,0,0),flDamage/16,0.1,25,0); if ( ( bitsDamageType & DMG_BLAST ) && g_pGameRules->IsMultiplayer() ) { // blasts damage armor more. flBonus *= 2; } // buz: refuse gas damage while wearing gasmask if (m_iGasMaskOn && ( bitsDamageType & DMG_NERVEGAS )) return 0; // Already dead if ( !IsAlive() ) return 0; // go take the damage first CBaseEntity *pAttacker = CBaseEntity::Instance(pevAttacker); if ( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker ) ) { // Refuse the damage return 0; } // keep track of amount of damage last sustained m_lastDamageAmount = flDamage; if( bitsDamageType & DMG_NERVEGAS && !m_flBlurAmount ) { m_flBlurAmount += flDamage * 0.03f; m_flCurBlurAmount = -m_flBlurAmount; // start pos m_flLastBlurAmount = m_flBlurAmount; if( !m_flBlurFadeTime ) // fire once EMIT_SOUND( ENT(pev), CHAN_VOICE, "player/pl_gas_hard.wav", 1, ATTN_NORM ); m_flBlurFadeTime = gpGlobals->time; } // Armor. // buz: броня не спасает от ударов вплотную // Wargon: Броня также не защищает от газа. (1.1) if (pev->armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN | DMG_CLUB | DMG_SLASH | DMG_NERVEGAS)) )// armor doesn't protect against fall or drown damage! { float flNew = flDamage * flRatio; float flArmor; flArmor = (flDamage - flNew) * flBonus; // Does this use more armor than we have? if (flArmor > pev->armorvalue) { flArmor = pev->armorvalue; flArmor *= (1/flBonus); flNew = flDamage - flArmor; pev->armorvalue = 0; } else pev->armorvalue -= flArmor; flDamage = flNew; } // this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that // as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc) fTookDamage = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, (int)flDamage, bitsDamageType); // reset damage time countdown for each type of time based damage player just sustained { for (int i = 0; i < CDMG_TIMEBASED; i++) if (bitsDamageType & (DMG_PARALYZE << i)) m_rgbTimeBasedDamage[i] = 0; } // tell director about it MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR ); WRITE_BYTE ( 9 ); // command length in bytes WRITE_BYTE ( DRC_CMD_EVENT ); // take damage event WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity WRITE_LONG( 5 ); // eventflags (priority and flags) MESSAGE_END(); // how bad is it, doc? ftrivial = (pev->health > 75 || m_lastDamageAmount < 5); fmajor = (m_lastDamageAmount > 25); fcritical = (pev->health < 30); // g-cont. dropping active item if damage is too much if( fmajor && (bitsDamage & (DMG_FALL | DMG_CRUSH | DMG_SLASH))) DropPlayerItem( NULL ); // handle all bits set in this damage message, // let the suit give player the diagnosis // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) // UNDONE: still need to record damage and heal messages for the following types // DMG_BURN // DMG_FREEZE // DMG_BLAST // DMG_SHOCK m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client m_bitsHUDDamage = -1; // make sure the damage bits get resent // buz: new punch system if (bitsDamage & (DMG_BULLET | DMG_BLAST) && pevInflictor && pevInflictor != pev) { float punch, pmax, divide; if (bitsDamage & DMG_BLAST ) { pmax = CVAR_GET_FLOAT("blast_punch_max"); divide = CVAR_GET_FLOAT("blast_punch_divide"); } else { pmax = CVAR_GET_FLOAT("bullet_punch_max"); divide = CVAR_GET_FLOAT("bullet_punch_divide"); } if (divide == 0) divide = 1; punch = (flDamage > pmax ? pmax : flDamage) / divide; Vector to = pevInflictor->origin - pev->origin; to.z = 0; to = to.Normalize(); Vector tempAngle = Vector(0, pev->v_angle.y, 0); UTIL_MakeVectors(tempAngle); ViewPunch(DotProduct(gpGlobals->v_forward, to) * punch, DotProduct(gpGlobals->v_right, to) * punch, DotProduct(gpGlobals->v_right, to) * punch); } else { // pev->punchangle.x = -2; // old punchangle ViewPunch(1, 0, 0); } return fTookDamage; // buz" no hev sounds while (fTookDamage && (!ftrivial || (bitsDamage & DMG_TIMEBASED)) && ffound && bitsDamage) { ffound = FALSE; if (bitsDamage & DMG_CLUB) { if (fmajor) SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture bitsDamage &= ~DMG_CLUB; ffound = TRUE; } if (bitsDamage & (DMG_FALL | DMG_CRUSH)) { if (fmajor) SetSuitUpdate("!HEV_DMG5", FALSE, SUIT_NEXT_IN_30SEC); // major fracture else SetSuitUpdate("!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC); // minor fracture bitsDamage &= ~(DMG_FALL | DMG_CRUSH); ffound = TRUE; } if (bitsDamage & DMG_BULLET) { if (m_lastDamageAmount > 5) SetSuitUpdate("!HEV_DMG6", FALSE, SUIT_NEXT_IN_30SEC); // blood loss detected //else // SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration bitsDamage &= ~DMG_BULLET; ffound = TRUE; } if (bitsDamage & DMG_SLASH) { if (fmajor) SetSuitUpdate("!HEV_DMG1", FALSE, SUIT_NEXT_IN_30SEC); // major laceration else SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration bitsDamage &= ~DMG_SLASH; ffound = TRUE; } if (bitsDamage & DMG_SONIC) { if (fmajor) SetSuitUpdate("!HEV_DMG2", FALSE, SUIT_NEXT_IN_1MIN); // internal bleeding bitsDamage &= ~DMG_SONIC; ffound = TRUE; } if (bitsDamage & (DMG_POISON | DMG_PARALYZE)) { SetSuitUpdate("!HEV_DMG3", FALSE, SUIT_NEXT_IN_1MIN); // blood toxins detected bitsDamage &= ~(DMG_POISON | DMG_PARALYZE); ffound = TRUE; } if (bitsDamage & DMG_ACID) { SetSuitUpdate("!HEV_DET1", FALSE, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected bitsDamage &= ~DMG_ACID; ffound = TRUE; } if (bitsDamage & DMG_NERVEGAS) { SetSuitUpdate("!HEV_DET0", FALSE, SUIT_NEXT_IN_1MIN); // biohazard detected bitsDamage &= ~DMG_NERVEGAS; ffound = TRUE; } if (bitsDamage & DMG_RADIATION) { SetSuitUpdate("!HEV_DET2", FALSE, SUIT_NEXT_IN_1MIN); // radiation detected bitsDamage &= ~DMG_RADIATION; ffound = TRUE; } if (bitsDamage & DMG_SHOCK) { bitsDamage &= ~DMG_SHOCK; ffound = TRUE; } } if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) { // first time we take major damage... // turn automedic on if not on SetSuitUpdate("!HEV_MED1", FALSE, SUIT_NEXT_IN_30MIN); // automedic on // give morphine shot if not given recently SetSuitUpdate("!HEV_HEAL7", FALSE, SUIT_NEXT_IN_30MIN); // morphine shot } if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75) { // already took major damage, now it's critical... if (pev->health < 6) SetSuitUpdate("!HEV_HLTH3", FALSE, SUIT_NEXT_IN_10MIN); // near death else if (pev->health < 20) SetSuitUpdate("!HEV_HLTH2", FALSE, SUIT_NEXT_IN_10MIN); // health critical // give critical health warnings if (!RANDOM_LONG(0,3) && flHealthPrev < 50) SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention } // if we're taking time based damage, warn about its continuing effects if (fTookDamage && (bitsDamageType & DMG_TIMEBASED) && flHealthPrev < 75) { if (flHealthPrev < 50) { if (!RANDOM_LONG(0,3)) SetSuitUpdate("!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN); //seek medical attention } else SetSuitUpdate("!HEV_HLTH1", FALSE, SUIT_NEXT_IN_10MIN); // health dropping } return fTookDamage; } //========================================================= // PackDeadPlayerItems - call this when a player dies to // pack up the appropriate weapons and ammo items, and to // destroy anything that shouldn't be packed. // // This is pretty brute force :( //========================================================= void CBasePlayer :: PackDeadPlayerItems( void ) { int iWeaponRules; int iAmmoRules; int i; CBasePlayerItem *rgpPackWeapons[MAX_WEAPONS]; int iPackAmmo[ MAX_AMMO_SLOTS + 1]; int iPW = 0;// index into packweapons array int iPA = 0;// index into packammo array memset(rgpPackWeapons, NULL, sizeof(rgpPackWeapons) ); memset(iPackAmmo, -1, sizeof(iPackAmmo) ); // get the game rules iWeaponRules = g_pGameRules->DeadPlayerWeapons( this ); iAmmoRules = g_pGameRules->DeadPlayerAmmo( this ); if ( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO ) { // nothing to pack. Remove the weapons and return. Don't call create on the box! RemoveAllItems( TRUE ); return; } // go through all of the weapons and make a list of the ones to pack for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { if ( m_rgpPlayerItems[ i ] ) { // there's a weapon here. Should I pack it? CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[ i ]; while ( pPlayerItem ) { switch( iWeaponRules ) { case GR_PLR_DROP_GUN_ACTIVE: if ( m_pActiveItem && pPlayerItem == m_pActiveItem ) { // this is the active item. Pack it. rgpPackWeapons[ iPW++ ] = pPlayerItem; } break; case GR_PLR_DROP_GUN_ALL: rgpPackWeapons[ iPW++ ] = pPlayerItem; break; default: break; } pPlayerItem = pPlayerItem->m_pNext; } } } // now go through ammo and make a list of which types to pack. if ( iAmmoRules != GR_PLR_DROP_AMMO_NO ) { for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) { if ( m_rgAmmo[ i ] > 0 ) { // player has some ammo of this type. switch ( iAmmoRules ) { case GR_PLR_DROP_AMMO_ALL: iPackAmmo[ iPA++ ] = i; break; case GR_PLR_DROP_AMMO_ACTIVE: if ( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() ) { // this is the primary ammo type for the active weapon iPackAmmo[ iPA++ ] = i; } else if ( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() ) { // this is the secondary ammo type for the active weapon iPackAmmo[ iPA++ ] = i; } break; default: break; } } } } // create a box to pack the stuff into. CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin, pev->angles, edict() ); pWeaponBox->pev->angles.x = 0;// don't let weaponbox tilt. pWeaponBox->pev->angles.z = 0; pWeaponBox->SetThink(& CWeaponBox::Kill ); pWeaponBox->SetNextThink( 120 ); // back these two lists up to their first elements iPA = 0; iPW = 0; // pack the ammo while ( iPackAmmo[ iPA ] != -1 ) { pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerAmmo::AmmoInfoArray[ iPackAmmo[ iPA ] ].pszName ), m_rgAmmo[ iPackAmmo[ iPA ] ] ); iPA++; } // now pack all of the items in the lists while ( rgpPackWeapons[ iPW ] ) { // weapon unhooked from the player. Pack it into der box. pWeaponBox->PackWeapon( rgpPackWeapons[ iPW ] ); iPW++; } pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some. RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above. } void CBasePlayer :: RemoveAllItems( BOOL removeSuit ) { if (m_pActiveItem) { ResetAutoaim( ); m_pActiveItem->Holster( true ); m_pActiveItem = NULL; } m_pLastItem = NULL; int i; CBasePlayerItem *pPendingItem; for (i = 0; i < MAX_ITEM_TYPES; i++) { m_pActiveItem = m_rgpPlayerItems[i]; while (m_pActiveItem) { pPendingItem = m_pActiveItem->m_pNext; m_pActiveItem->Drop( ); m_pActiveItem = pPendingItem; } m_rgpPlayerItems[i] = NULL; } m_pActiveItem = NULL; pev->viewmodel = 0; pev->weaponmodel = 0; if ( removeSuit ) ClearBits( m_iHideHUD, ITEM_SUIT ); ClearBits( pev->weapons, WEAPON_ALLWEAPONS ); for ( i = 0; i < MAX_AMMO_SLOTS;i++) m_rgAmmo[i] = 0; UpdateClientData(); // send Selected Weapon Message to our client MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); WRITE_BYTE( 0 ); WRITE_BYTE( 0 ); WRITE_BYTE( 0 ); MESSAGE_END(); } /* * GLOBALS ASSUMED SET: g_ulModelIndexPlayer * * ENTITY_METHOD(PlayerDie) */ entvars_t *g_pevLastInflictor; // Set in combat.cpp. Used to pass the damage inflictor for death messages. // Better solution: Add as parameter to all Killed() functions. void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) { // Wargon: Если игрока убили хедшотом, то выводится соответствующая мессага. if (m_LastHitGroup == HITGROUP_HEAD) UTIL_ShowMessage("#TIPS_HEADSHOOT", this ); CSound *pSound; // Holster weapon immediately, to allow it to cleanup if ( m_pActiveItem ) m_pActiveItem->Holster( true ); m_pNextItem = NULL; g_pGameRules->PlayerKilled( this, pevAttacker, g_pevLastInflictor ); if ( m_pTank != NULL ) { m_pTank->Use( this, this, USE_OFF, 0 ); m_pTank = NULL; } // buz: spec tank if (m_pSpecTank) { m_pSpecTank->Use( this, this, USE_OFF, 0 ); m_pSpecTank = NULL; } // this client isn't going to be thinking for a while, so reset the sound until they respawn pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); { if ( pSound ) { pSound->Reset(); } } EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/gameover.wav", 1, ATTN_NORM); SetAnimation( PLAYER_DIE ); m_iRespawnFrames = 0; pev->modelindex = g_ulModelIndexPlayer; // don't use eyes pev->deadflag = DEAD_DYING; pev->movetype = MOVETYPE_TOSS; ClearBits( pev->flags, FL_ONGROUND ); if (pev->velocity.z < 10) pev->velocity.z += RANDOM_FLOAT(0,300); // clear out the suit message cache so we don't keep chattering SetSuitUpdate(NULL, FALSE, 0); // send "health" update message to zero m_iClientHealth = 0; MESSAGE_BEGIN( MSG_ONE, gmsgHealth, NULL, pev ); WRITE_BYTE( m_iClientHealth ); MESSAGE_END(); m_iClientStamina = 0; MESSAGE_BEGIN( MSG_ONE, gmsgStamina, NULL, pev ); WRITE_SHORT( m_iClientStamina ); MESSAGE_END(); // Tell Ammo Hud that the player is dead MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); WRITE_BYTE(0); WRITE_BYTE(0XFF); WRITE_BYTE(0xFF); MESSAGE_END(); // reset FOV pev->fov = m_iFOV = m_iClientFOV = 0; MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev ); WRITE_BYTE(0); MESSAGE_END(); m_flBlurAmount = 0.0f; // reset blur //I don't _think_ we need to anything about fog here. - LRC // UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12 // UTIL_ScreenFade( edict(), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); if ( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS ) { pev->solid = SOLID_NOT; GibMonster(); // This clears pev->model pev->effects |= EF_NODRAW; return; } DeathSound(); pev->angles.x = 0; pev->angles.z = 0; SetThink(&CBasePlayer::PlayerDeathThink); SetNextThink( 0.1 ); } // Set the activity based on an event or current state void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) { int animDesired; float speed; char szAnim[64]; speed = pev->velocity.Length2D(); if (pev->flags & FL_FROZEN) { speed = 0; playerAnim = PLAYER_IDLE; } switch (playerAnim) { case PLAYER_JUMP: m_IdealActivity = ACT_HOP; pev->fuser1 = 0.0f; break; case PLAYER_SUPERJUMP: m_IdealActivity = ACT_LEAP; pev->fuser1 = 0.0f; break; case PLAYER_DIE: m_IdealActivity = ACT_DIESIMPLE; m_IdealActivity = GetDeathActivity( ); break; case PLAYER_ATTACK1: switch( m_Activity ) { case ACT_HOVER: case ACT_SWIM: case ACT_HOP: case ACT_LEAP: case ACT_DIESIMPLE: m_IdealActivity = m_Activity; break; default: m_IdealActivity = ACT_RANGE_ATTACK1; break; } break; case PLAYER_IDLE: case PLAYER_WALK: if ( !FBitSet( pev->flags, FL_ONGROUND ) && (m_Activity == ACT_HOP || m_Activity == ACT_LEAP) ) // Still jumping { m_IdealActivity = m_Activity; } else if ( pev->waterlevel > 1 && pev->watertype != CONTENTS_FOG) { if ( speed == 0 ) m_IdealActivity = ACT_HOVER; else m_IdealActivity = ACT_SWIM; } else { m_IdealActivity = ACT_WALK; } break; } switch (m_IdealActivity) { case ACT_HOVER: case ACT_LEAP: case ACT_SWIM: case ACT_HOP: case ACT_DIESIMPLE: default: if ( m_Activity == m_IdealActivity) return; m_Activity = m_IdealActivity; animDesired = LookupActivity( m_Activity ); // Already using the desired animation? if (pev->sequence == animDesired) return; pev->gaitsequence = 0; pev->sequence = animDesired; pev->frame = 0; ResetSequenceInfo( ); return; case ACT_RANGE_ATTACK1: if ( FBitSet( pev->flags, FL_DUCKING ) ) // crouching strcpy( szAnim, "crouch_shoot_" ); else strcpy( szAnim, "ref_shoot_" ); strcat( szAnim, m_szAnimExtention ); animDesired = LookupSequence( szAnim ); if (animDesired == -1) animDesired = 0; if ( pev->sequence != animDesired || !m_fSequenceLoops ) { pev->frame = 0; } if (!m_fSequenceLoops) { pev->effects |= EF_NOINTERP; } m_Activity = m_IdealActivity; pev->sequence = animDesired; ResetSequenceInfo( ); break; case ACT_WALK: if (m_Activity != ACT_RANGE_ATTACK1 || m_fSequenceFinished) { if ( FBitSet( pev->flags, FL_DUCKING ) ) // crouching strcpy( szAnim, "crouch_aim_" ); else strcpy( szAnim, "ref_aim_" ); strcat( szAnim, m_szAnimExtention ); animDesired = LookupSequence( szAnim ); if (animDesired == -1) animDesired = 0; m_Activity = ACT_WALK; } else { animDesired = pev->sequence; } } if ( FBitSet( pev->flags, FL_DUCKING ) ) { if ( speed == 0) { pev->gaitsequence = LookupActivity( ACT_CROUCHIDLE ); // pev->gaitsequence = LookupActivity( ACT_CROUCH ); } else { pev->gaitsequence = LookupActivity( ACT_CROUCH ); } } else if ( speed > 200 ) { pev->gaitsequence = LookupActivity( ACT_RUN ); if (m_pActiveItem) m_pActiveItem->PlayerRun(); } else if (speed > 0) { pev->gaitsequence = LookupActivity( ACT_WALK ); if (m_pActiveItem) m_pActiveItem->PlayerWalk(); } else { // pev->gaitsequence = LookupActivity( ACT_WALK ); pev->gaitsequence = LookupSequence( "deep_idle" ); } // Already using the desired animation? if (pev->sequence == animDesired) return; //ALERT( at_console, "Set animation to %d\n", animDesired ); // Reset to first frame of desired animation pev->sequence = animDesired; pev->frame = 0; ResetSequenceInfo( ); } /* =========== WaterMove ============ */ #define AIRTIME 12 // lung full of air lasts this many seconds void CBasePlayer::WaterMove() { int air; if (pev->movetype == MOVETYPE_NOCLIP) return; if (pev->health < 0) return; // waterlevel 0 - not in water // waterlevel 1 - feet in water // waterlevel 2 - waist in water // waterlevel 3 - head in water if (pev->waterlevel != 3 || pev->watertype <= CONTENTS_FLYFIELD) { // not underwater // play 'up for air' sound if (pev->air_finished < gpGlobals->time) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade1.wav", 1, ATTN_NORM); else if (pev->air_finished < gpGlobals->time + 9) EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/pl_wade2.wav", 1, ATTN_NORM); pev->air_finished = gpGlobals->time + AIRTIME; pev->dmg = 2; // if we took drowning damage, give it back slowly if (m_idrowndmg > m_idrownrestored) { // set drowning damage bit. hack - dmg_drownrecover actually // makes the time based damage code 'give back' health over time. // make sure counter is cleared so we start count correctly. // NOTE: this actually causes the count to continue restarting // until all drowning damage is healed. m_bitsDamageType |= DMG_DROWNRECOVER; m_bitsDamageType &= ~DMG_DROWN; m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; } } else if (pev->watertype > CONTENTS_FLYFIELD) // FLYFIELD, FLYFIELD_GRAVITY & FOG aren't really water... { // fully under water // stop restoring damage while underwater m_bitsDamageType &= ~DMG_DROWNRECOVER; m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; if (pev->air_finished < gpGlobals->time) // drown! { if (pev->pain_finished < gpGlobals->time) { // take drowning damage pev->dmg += 1; if (pev->dmg > 5) pev->dmg = 5; TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), pev->dmg, DMG_DROWN); pev->pain_finished = gpGlobals->time + 1; // track drowning damage, give it back when // player finally takes a breath m_idrowndmg += pev->dmg; } } else { m_bitsDamageType &= ~DMG_DROWN; } } if (!pev->waterlevel || pev->watertype <= CONTENTS_FLYFIELD ) { if (FBitSet(pev->flags, FL_INWATER)) { ClearBits(pev->flags, FL_INWATER); } return; } // make bubbles air = (int)(pev->air_finished - gpGlobals->time); if (!RANDOM_LONG(0,0x1f) && RANDOM_LONG(0,AIRTIME-1) >= air) { switch (RANDOM_LONG(0,3)) { case 0: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim1.wav", 0.8, ATTN_NORM); break; case 1: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim2.wav", 0.8, ATTN_NORM); break; case 2: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim3.wav", 0.8, ATTN_NORM); break; case 3: EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_swim4.wav", 0.8, ATTN_NORM); break; } UTIL_MakeVectors( pev->v_angle ); // g-cont. add some bubbles CBaseEntity::Create( "player_bubbles", EyePosition() + gpGlobals->v_forward * 2.0f, pev->v_angle, edict() ); } if (pev->watertype == CONTENTS_LAVA) // do damage { if (pev->dmgtime < gpGlobals->time) TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 10 * pev->waterlevel, DMG_BURN); } else if (pev->watertype == CONTENTS_SLIME) // do damage { pev->dmgtime = gpGlobals->time + 1; TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), 4 * pev->waterlevel, DMG_ACID); } if (!FBitSet(pev->flags, FL_INWATER)) { SetBits(pev->flags, FL_INWATER); pev->dmgtime = 0; } } // TRUE if the player is attached to a ladder BOOL CBasePlayer::IsOnLadder( void ) { return ( pev->movetype == MOVETYPE_FLY ); } void CBasePlayer::PlayerDeathThink(void) { float flForward; if (FBitSet(pev->flags, FL_ONGROUND)) { flForward = pev->velocity.Length() - 20; if (flForward <= 0) pev->velocity = g_vecZero; else pev->velocity = flForward * pev->velocity.Normalize(); } if ( HasWeapons() ) { // we drop the guns here because weapons that have an area effect and can kill their user // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the // player class sometimes is freed. It's safer to manipulate the weapons once we know // we aren't calling into any of their code anymore through the player pointer. PackDeadPlayerItems(); } if (pev->modelindex && (!m_fSequenceFinished) && (pev->deadflag == DEAD_DYING)) { StudioFrameAdvance( ); m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands if ( m_iRespawnFrames < 120 ) // Animations should be no longer than this return; } // 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 // 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 if ( pev->movetype != MOVETYPE_NONE && FBitSet(pev->flags, FL_ONGROUND) ) pev->movetype = MOVETYPE_NONE; if (pev->deadflag == DEAD_DYING) pev->deadflag = DEAD_DEAD; StopAnimation(); pev->effects |= EF_NOINTERP; pev->framerate = 0.0; BOOL fAnyButtonDown = (pev->button & ~IN_SCORE ); // wait for all buttons released if (pev->deadflag == DEAD_DEAD) { if (fAnyButtonDown) return; if ( g_pGameRules->FPlayerCanRespawn( this ) ) { m_fDeadTime = gpGlobals->time; pev->deadflag = DEAD_RESPAWNABLE; } return; } // if the player has been dead for one second longer than allowed by forcerespawn, // forcerespawn isn't on. Send the player off to an intermission camera until they // choose to respawn. if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > (m_fDeadTime + 6) ) && !(m_afPhysicsFlags & PFLAG_OBSERVER) ) { // go to dead camera. StartDeathCam(); } // wait for any button down, or mp_forcerespawn is set and the respawn time is up if (!fAnyButtonDown && !( g_pGameRules->IsMultiplayer() && forcerespawn.value > 0 && (gpGlobals->time > (m_fDeadTime + 5))) ) return; pev->button = 0; m_iRespawnFrames = 0; //ALERT(at_console, "Respawn\n"); respawn(pev, !(m_afPhysicsFlags & PFLAG_OBSERVER) );// don't copy a corpse if we're in deathcam. DontThink(); } //========================================================= // StartDeathCam - find an intermission spot and send the // player off into observer mode //========================================================= void CBasePlayer::StartDeathCam( void ) { CBaseEntity *pSpot, *pNewSpot; int iRand; if ( pev->view_ofs == g_vecZero ) { // don't accept subsequent attempts to StartDeathCam() return; } pSpot = UTIL_FindEntityByClassname( NULL, "info_intermission"); if ( pSpot ) { // at least one intermission spot in the world. iRand = RANDOM_LONG( 0, 3 ); while ( iRand > 0 ) { pNewSpot = UTIL_FindEntityByTargetname( pSpot, "info_intermission"); if ( pNewSpot ) { pSpot = pNewSpot; } iRand--; } CopyToBodyQue( pev ); StartObserver( pSpot->pev->origin, pSpot->pev->v_angle ); } else { // no intermission spot. Push them up in the air, looking down at their corpse TraceResult tr; CopyToBodyQue( pev ); UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, 128 ), ignore_monsters, edict(), &tr ); StartObserver( tr.vecEndPos, UTIL_VecToAngles( tr.vecEndPos - pev->origin ) ); return; } } void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle ) { m_afPhysicsFlags |= PFLAG_OBSERVER; pev->view_ofs = g_vecZero; pev->angles = pev->v_angle = vecViewAngle; pev->fixangle = TRUE; pev->solid = SOLID_NOT; pev->takedamage = DAMAGE_NO; pev->movetype = MOVETYPE_NONE; pev->modelindex = 0; UTIL_SetOrigin( this, vecPosition ); } // // PlayerUse - handles USE keypress // #define PLAYER_SEARCH_RADIUS (float)64 #define PLAYER_DISTUSE_RADIUS (float)128 // Wargon: Расстояние дальнего юза уменьшено. void CBasePlayer::PlayerUse ( void ) { // Was use pressed or released? if ( ! ((pev->button | m_afButtonPressed | m_afButtonReleased) & IN_USE) ) return; // Hit Use on a train? if ( m_afButtonPressed & IN_USE ) { if ( m_pTank != NULL ) { // Stop controlling the tank // TODO: Send HUD Update m_pTank->Use( this, this, USE_OFF, 0 ); m_pTank = NULL; return; } // buz: spec tank else if (m_pSpecTank) { m_pSpecTank->Use( this, this, USE_OFF, 0 ); m_pSpecTank = NULL; return; } else { if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) { m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; return; } else { // Start controlling the train! CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); if ( pTrain && !(pev->button & IN_JUMP) && FBitSet(pev->flags, FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(pev) ) { m_afPhysicsFlags |= PFLAG_ONTRAIN; m_iTrain = TrainSpeed(pTrain->pev->speed, pTrain->pev->impulse); m_iTrain |= TRAIN_NEW; // EMIT_SOUND( ENT(pev), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM); return; } } } } CBaseEntity *pObject = NULL; CBaseEntity *pClosest = NULL; Vector vecLOS; float flMaxDot = VIEW_FIELD_NARROW; float flDot; TraceResult tr; int caps; UTIL_MakeVectors ( pev->v_angle );// so we know which way we are facing //LRC- try to get an exact entity to use. // (is this causing "use-buttons-through-walls" problems? Surely not!) UTIL_TraceLine( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + (gpGlobals->v_forward * PLAYER_SEARCH_RADIUS), dont_ignore_monsters, ENT(pev), &tr ); if (tr.pHit) { pObject = CBaseEntity::Instance(tr.pHit); if (!pObject || !(pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE))) { pObject = NULL; } } if (!pObject) //LRC- couldn't find a direct solid object to use, try the normal method { while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL) { caps = pObject->ObjectCaps(); if (caps & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE) && !(caps & FCAP_ONLYDIRECT_USE)) //LRC - we can't see 'direct use' entities in this section { // buz: test special usability by sequence bbox for monsters CBaseMonster *pMonster = pObject->MyMonsterPointer(); if (pMonster) { Vector mins, maxs; pMonster->ExtractBbox(pMonster->pev->sequence, mins, maxs); vecLOS = (((mins + maxs) * 0.5) + pMonster->pev->origin - (pev->origin + pev->view_ofs)); vecLOS = UTIL_ClampVectorToBox( vecLOS, ((mins + maxs) * 0.5) ); } else { vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs)); // ALERT(at_console, "absmin %f %f %f, absmax %f %f %f, mins %f %f %f, maxs %f %f %f, size %f %f %f\n", pObject->pev->absmin.x, pObject->pev->absmin.y, pObject->pev->absmin.z, pObject->pev->absmax.x, pObject->pev->absmax.y, pObject->pev->absmax.z, pObject->pev->mins.x, pObject->pev->mins.y, pObject->pev->mins.z, pObject->pev->maxs.x, pObject->pev->maxs.y, pObject->pev->maxs.z, pObject->pev->size.x, pObject->pev->size.y, pObject->pev->size.z);//LRCTEMP // This essentially moves the origin of the target to the corner nearest the player to test to see // if it's "hull" is in the view cone vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 ); } flDot = DotProduct (vecLOS , gpGlobals->v_forward); if (flDot > flMaxDot || vecLOS == g_vecZero ) // LRC - if the player is standing inside this entity, it's also ok to use it. {// only if the item is in front of the user pClosest = pObject; flMaxDot = flDot; // ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); } // ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot ); } } pObject = pClosest; if( pObject && pObject->ObjectCaps() & FCAP_USE_ONLY ) { // make sure what item not blocked by entity UTIL_TraceLine( pev->origin + pev->view_ofs, pObject->pev->origin, dont_ignore_monsters, ENT(pev), &tr ); if( tr.flFraction < 1.0f ) pObject = NULL; // blocked } } if (!pObject) // buz - try distance use { UTIL_TraceLine( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + (gpGlobals->v_forward * PLAYER_DISTUSE_RADIUS), dont_ignore_monsters, ENT(pev), &tr ); if (tr.pHit) { pObject = CBaseEntity::Instance(tr.pHit); if (!pObject || !(pObject->ObjectCaps() & FCAP_DISTANCE_USE)) { pObject = NULL; } } } // Found an object if (pObject ) { //!!!UNDONE: traceline here to prevent USEing buttons through walls caps = pObject->ObjectCaps(); if ( m_afButtonPressed & IN_USE ) EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM); if ( ( (pev->button & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) || ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) ) { if ( caps & FCAP_CONTINUOUS_USE ) m_afPhysicsFlags |= PFLAG_USING; pObject->Use( this, this, USE_SET, 1 ); } // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away // (actually, nothing uses on/off. They're either continuous - rechargers and momentary // buttons - or they're impulse - buttons, doors, tanks, trains, etc.) --LRC else if ( (m_afButtonReleased & IN_USE) && (pObject->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use { pObject->Use( this, this, USE_SET, 0 ); } } else { if ( m_afButtonPressed & IN_USE ) EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_denyselect.wav", 0.4, ATTN_NORM); } } void CBasePlayer::Jump() { Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping Vector vecAdjustedVelocity; Vector vecSpot; TraceResult tr; if (FBitSet(pev->flags, FL_WATERJUMP)) return; if (pev->waterlevel >= 2 && pev->watertype != CONTENTS_FOG) { return; } // jump velocity is sqrt( height * gravity * 2) // If this isn't the first frame pressing the jump button, break out. if ( !FBitSet( m_afButtonPressed, IN_JUMP ) ) return; // don't pogo stick if ( !(pev->flags & FL_ONGROUND) || !pev->groundentity ) { return; } // many features in this function use v_forward, so makevectors now. UTIL_MakeVectors (pev->angles); // ClearBits(pev->flags, FL_ONGROUND); // don't stairwalk SetAnimation( PLAYER_JUMP ); if ( m_fLongJump && (pev->button & IN_DUCK) && ( pev->flDuckTime > 0 ) && pev->velocity.Length() > 50 ) { SetAnimation( PLAYER_SUPERJUMP ); } // buz: report active item about jumping if (m_pActiveItem) m_pActiveItem->PlayerJump(); // If you're standing on a conveyor, add its velocity to yours (for momentum) entvars_t *pevGround = VARS(pev->groundentity); if ( pevGround && (pevGround->flags & FL_CONVEYOR) ) { pev->velocity = pev->velocity + pev->basevelocity; } } // This is a glorious hack to find free space when you've crouched into some solid space // Our crouching collisions do not work correctly for some reason and this is easier // than fixing the problem :( void FixPlayerCrouchStuck( edict_t *pPlayer ) { TraceResult trace; // Move up as many as 18 pixels if the player is stuck. for ( int i = 0; i < 18; i++ ) { UTIL_TraceHull( pPlayer->v.origin, pPlayer->v.origin, dont_ignore_monsters, head_hull, pPlayer, &trace ); if ( trace.fStartSolid ) pPlayer->v.origin.z ++; else break; } } void CBasePlayer::Duck( ) { if (pev->button & IN_DUCK) { if ( m_IdealActivity != ACT_LEAP ) { SetAnimation( PLAYER_WALK ); } } } // // ID's player as such. // int CBasePlayer::Classify ( void ) { return CLASS_PLAYER; } void CBasePlayer::AddPoints( int score, BOOL bAllowNegativeScore ) { // Positive score always adds if ( score < 0 ) { if ( !bAllowNegativeScore ) { if ( pev->frags < 0 ) // Can't go more negative return; if ( -score > pev->frags ) // Will this go negative? { score = -pev->frags; // Sum will be 0 } } } pev->frags += score; MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo ); WRITE_BYTE( ENTINDEX(edict()) ); WRITE_SHORT( pev->frags ); WRITE_SHORT( m_iDeaths ); WRITE_SHORT( 0 ); WRITE_SHORT( g_pGameRules->GetTeamIndex( m_szTeamName ) + 1 ); MESSAGE_END(); } void CBasePlayer::AddPointsToTeam( int score, BOOL bAllowNegativeScore ) { int index = entindex(); for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer && i != index ) { if ( g_pGameRules->PlayerRelationship( this, pPlayer ) == GR_TEAMMATE ) { pPlayer->AddPoints( score, bAllowNegativeScore ); } } } } //Player ID void CBasePlayer::InitStatusBar() { m_flStatusBarDisappearDelay = 0; m_SbarString1[0] = m_SbarString0[0] = 0; } void CBasePlayer::UpdateStatusBar() { int newSBarState[ SBAR_END ]; char sbuf0[ SBAR_STRING_SIZE ]; char sbuf1[ SBAR_STRING_SIZE ]; memset( newSBarState, 0, sizeof(newSBarState) ); strcpy( sbuf0, m_SbarString0 ); strcpy( sbuf1, m_SbarString1 ); // Find an ID Target TraceResult tr; UTIL_MakeVectors( pev->v_angle + pev->punchangle ); Vector vecSrc = EyePosition(); Vector vecEnd = vecSrc + (gpGlobals->v_forward * MAX_ID_RANGE); UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, edict(), &tr); if (tr.flFraction != 1.0) { if ( !FNullEnt( tr.pHit ) ) { CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); if (pEntity->Classify() == CLASS_PLAYER ) { newSBarState[ SBAR_ID_TARGETNAME ] = ENTINDEX( pEntity->edict() ); strcpy( sbuf1, "1 %p1\n2 Health: %i2%%\n3 Armor: %i3%%" ); // allies and medics get to see the targets health if ( g_pGameRules->PlayerRelationship( this, pEntity ) == GR_TEAMMATE ) { newSBarState[ SBAR_ID_TARGETHEALTH ] = 100 * (pEntity->pev->health / pEntity->pev->max_health); newSBarState[ SBAR_ID_TARGETARMOR ] = pEntity->pev->armorvalue; //No need to get it % based since 100 it's the max. } m_flStatusBarDisappearDelay = gpGlobals->time + 1.0; } } else if ( m_flStatusBarDisappearDelay > gpGlobals->time ) { // hold the values for a short amount of time after viewing the object newSBarState[ SBAR_ID_TARGETNAME ] = m_izSBarState[ SBAR_ID_TARGETNAME ]; newSBarState[ SBAR_ID_TARGETHEALTH ] = m_izSBarState[ SBAR_ID_TARGETHEALTH ]; newSBarState[ SBAR_ID_TARGETARMOR ] = m_izSBarState[ SBAR_ID_TARGETARMOR ]; } } BOOL bForceResend = FALSE; if ( strcmp( sbuf0, m_SbarString0 ) ) { MESSAGE_BEGIN( MSG_ONE, gmsgStatusText, NULL, pev ); WRITE_BYTE( 0 ); WRITE_STRING( sbuf0 ); MESSAGE_END(); strcpy( m_SbarString0, sbuf0 ); // make sure everything's resent bForceResend = TRUE; } if ( strcmp( sbuf1, m_SbarString1 ) ) { MESSAGE_BEGIN( MSG_ONE, gmsgStatusText, NULL, pev ); WRITE_BYTE( 1 ); WRITE_STRING( sbuf1 ); MESSAGE_END(); strcpy( m_SbarString1, sbuf1 ); // make sure everything's resent bForceResend = TRUE; } // Check values and send if they don't match for (int i = 1; i < SBAR_END; i++) { if ( newSBarState[i] != m_izSBarState[i] || bForceResend ) { MESSAGE_BEGIN( MSG_ONE, gmsgStatusValue, NULL, pev ); WRITE_BYTE( i ); WRITE_SHORT( newSBarState[i] ); MESSAGE_END(); m_izSBarState[i] = newSBarState[i]; } } } #define CLIMB_SHAKE_FREQUENCY 22 // how many frames in between screen shakes when climbing #define MAX_CLIMB_SPEED 200 // fastest vertical climbing speed possible #define CLIMB_SPEED_DEC 15 // climbing deceleration rate #define CLIMB_PUNCH_X -7 // how far to 'punch' client X axis when climbing #define CLIMB_PUNCH_Z 7 // how far to 'punch' client Z axis when climbing void CBasePlayer::PreThink(void) { int buttonsChanged = (m_afButtonLast ^ pev->button); // These buttons have changed this frame // Debounced button codes for pressed/released // UNDONE: Do we need auto-repeat? m_afButtonPressed = buttonsChanged & pev->button; // The ones that changed and are now down are "pressed" m_afButtonReleased = buttonsChanged & (~pev->button); // The ones that changed and aren't down are "released" g_pGameRules->PlayerThink( this ); // CheckDesiredList( ); //LRC //CheckAssistList(); //LRC if ( g_fGameOver ) return; // intermission or finale UTIL_MakeVectors(pev->v_angle); // is this still used? ItemPreFrame( ); WaterMove(); if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; else m_iHideHUD |= HIDEHUD_FLASHLIGHT; // JOHN: checks if new client data (for HUD and view control) needs to be sent to the client UpdateClientData(); CheckTimeBasedDamage(); CheckSuitUpdate(); if (pev->deadflag >= DEAD_DYING) { PlayerDeathThink(); return; } // So the correct flags get sent to client asap. // if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) pev->flags |= FL_ONTRAIN; else pev->flags &= ~FL_ONTRAIN; // Train speed control if ( m_afPhysicsFlags & PFLAG_ONTRAIN ) { CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); float vel; if ( !pTrain ) { TraceResult trainTrace; // Maybe this is on the other side of a level transition UTIL_TraceLine( pev->origin, pev->origin + Vector(0,0,-38), ignore_monsters, ENT(pev), &trainTrace ); // HACKHACK - Just look for the func_tracktrain classname if ( trainTrace.flFraction != 1.0 && trainTrace.pHit ) pTrain = CBaseEntity::Instance( trainTrace.pHit ); if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(pev) ) { //ALERT( at_error, "In train mode with no train!\n" ); m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; return; } } else if ( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || (pev->button & (IN_MOVELEFT|IN_MOVERIGHT) ) ) { // Turn off the train if you jump, strafe, or the train controls go dead m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; return; } pev->velocity = g_vecZero; vel = 0; if ( m_afButtonPressed & IN_FORWARD ) { vel = 1; pTrain->Use( this, this, USE_SET, (float)vel ); } else if ( m_afButtonPressed & IN_BACK ) { vel = -1; pTrain->Use( this, this, USE_SET, (float)vel ); } if (vel) { m_iTrain = TrainSpeed(pTrain->pev->speed, pTrain->pev->impulse); m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; } } else if (m_iTrain & TRAIN_ACTIVE) m_iTrain = TRAIN_NEW; // turn off train if (pev->button & IN_JUMP) { // If on a ladder, jump off the ladder // else Jump Jump(); } // If trying to duck, already ducked, or in the process of ducking if ((pev->button & IN_DUCK) || FBitSet(pev->flags,FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) ) Duck(); if ( !FBitSet ( pev->flags, FL_ONGROUND ) ) { m_flFallVelocity = -pev->velocity.z; } // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? // Clear out ladder pointer m_hEnemy = NULL; if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) { pev->velocity = g_vecZero; } if (m_flStaminaValue < 1 && pev->button & IN_RUN) { CLIENT_COMMAND(ENT(pev), "-sprint\n"); //ALERT(at_console, "low stamina!\n"); return; } if ( pev->button & IN_RUN && m_flStaminaValue > 0 && pev->velocity.Length2D() > 100) { m_flStaminaValue -= 0.25; } else if (m_flStaminaValue < 100) { if (pev->velocity.Length2D() < 100) m_flStaminaValue += 0.25; else if (pev->velocity.Length2D() < 290) m_flStaminaValue += 0.1; } pev->skin = (int)m_flStaminaValue; } /* Time based Damage works as follows: 1) There are several types of timebased damage: #define DMG_PARALYZE (1 << 14) // slows affected creature down #define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad #define DMG_POISON (1 << 16) // blood poisioning #define DMG_RADIATION (1 << 17) // radiation exposure #define DMG_DROWNRECOVER (1 << 18) // drown recovery #define DMG_ACID (1 << 19) // toxic chemicals or acid burns #define DMG_SLOWBURN (1 << 20) // in an oven #define DMG_SLOWFREEZE (1 << 21) // in a subzero freezer 2) A new hit inflicting tbd restarts the tbd counter - each monster has an 8bit counter, per damage type. The counter is decremented every second, so the maximum time an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius of a damaging effect like fire, nervegas, radiation will continually reset the counter to max. 3) Every second that a tbd counter is running, the player takes damage. The damage is determined by the type of tdb. Paralyze - 1/2 movement rate, 30 second duration. Nervegas - 5 points per second, 16 second duration = 80 points max dose. Poison - 2 points per second, 25 second duration = 50 points max dose. Radiation - 1 point per second, 50 second duration = 50 points max dose. Drown - 5 points per second, 2 second duration. Acid/Chemical - 5 points per second, 10 second duration = 50 points max. Burn - 10 points per second, 2 second duration. Freeze - 3 points per second, 10 second duration = 30 points max. 4) Certain actions or countermeasures counteract the damaging effects of tbds: Armor/Heater/Cooler - Chemical(acid),burn, freeze all do damage to armor power, then to body - recharged by suit recharger Air In Lungs - drowning damage is done to air in lungs first, then to body - recharged by poking head out of water - 10 seconds if swiming fast Air In SCUBA - drowning damage is done to air in tanks first, then to body - 2 minutes in tanks. Need new tank once empty. Radiation Syringe - Each syringe full provides protection vs one radiation dosage Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison). Health kit - Immediate stop to acid/chemical, fire or freeze damage. Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage. */ // If player is taking time based damage, continue doing damage to player - // this simulates the effect of being poisoned, gassed, dosed with radiation etc - // anything that continues to do damage even after the initial contact stops. // Update all time based damage counters, and shut off any that are done. // The m_bitsDamageType bit MUST be set if any damage is to be taken. // This routine will detect the initial on value of the m_bitsDamageType // and init the appropriate counter. Only processes damage every second. //#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage //#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval //#define NERVEGAS_DURATION 16 //#define NERVEGAS_DAMAGE 5.0 //#define POISON_DURATION 25 //#define POISON_DAMAGE 2.0 //#define RADIATION_DURATION 50 //#define RADIATION_DAMAGE 1.0 //#define ACID_DURATION 10 //#define ACID_DAMAGE 5.0 //#define SLOWBURN_DURATION 2 //#define SLOWBURN_DAMAGE 1.0 //#define SLOWFREEZE_DURATION 1.0 //#define SLOWFREEZE_DAMAGE 3.0 /* */ void CBasePlayer::CheckTimeBasedDamage() { int i; BYTE bDuration = 0; static float gtbdPrev = 0.0; if (!(m_bitsDamageType & DMG_TIMEBASED)) return; // only check for time based damage approx. every 2 seconds if (abs(gpGlobals->time - m_tbdPrev) < 2.0) return; m_tbdPrev = gpGlobals->time; for (i = 0; i < CDMG_TIMEBASED; i++) { // make sure bit is set for damage type if (m_bitsDamageType & (DMG_PARALYZE << i)) { switch (i) { case itbd_Paralyze: // UNDONE - flag movement as half-speed bDuration = PARALYZE_DURATION; break; case itbd_NerveGas: // TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); bDuration = NERVEGAS_DURATION; break; case itbd_Poison: TakeDamage(pev, pev, POISON_DAMAGE, DMG_GENERIC); bDuration = POISON_DURATION; break; case itbd_Radiation: // TakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC); bDuration = RADIATION_DURATION; break; case itbd_DrownRecover: // NOTE: this hack is actually used to RESTORE health // after the player has been drowning and finally takes a breath if (m_idrowndmg > m_idrownrestored) { int idif = min(m_idrowndmg - m_idrownrestored, 10); TakeHealth(idif, DMG_GENERIC); m_idrownrestored += idif; } bDuration = 4; // get up to 5*10 = 50 points back break; case itbd_Acid: // TakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC); bDuration = ACID_DURATION; break; case itbd_SlowBurn: // TakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC); bDuration = SLOWBURN_DURATION; break; case itbd_SlowFreeze: // TakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC); bDuration = SLOWFREEZE_DURATION; break; default: bDuration = 0; } if (m_rgbTimeBasedDamage[i]) { // use up an antitoxin on poison or nervegas after a few seconds of damage if (((i == itbd_NerveGas) && (m_rgbTimeBasedDamage[i] < NERVEGAS_DURATION)) || ((i == itbd_Poison) && (m_rgbTimeBasedDamage[i] < POISON_DURATION))) { if (m_rgItems[ITEM_ANTIDOTE]) { m_rgbTimeBasedDamage[i] = 0; m_rgItems[ITEM_ANTIDOTE]--; // SetSuitUpdate("!HEV_HEAL4", FALSE, SUIT_REPEAT_OK); // buz: no hev sounds } } // decrement damage duration, detect when done. if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0) { m_rgbTimeBasedDamage[i] = 0; // if we're done, clear damage bits m_bitsDamageType &= ~(DMG_PARALYZE << i); } } else // first time taking this damage type - init damage duration m_rgbTimeBasedDamage[i] = bDuration; } } } /* THE POWER SUIT The Suit provides 3 main functions: Protection, Notification and Augmentation. Some functions are automatic, some require power. The player gets the suit shortly after getting off the train in C1A0 and it stays with him for the entire game. Protection Heat/Cold When the player enters a hot/cold area, the heating/cooling indicator on the suit will come on and the battery will drain while the player stays in the area. After the battery is dead, the player starts to take damage. This feature is built into the suit and is automatically engaged. Radiation Syringe This will cause the player to be immune from the effects of radiation for N seconds. Single use item. Anti-Toxin Syringe This will cure the player from being poisoned. Single use item. Health Small (1st aid kits, food, etc.) Large (boxes on walls) Armor The armor works using energy to create a protective field that deflects a percentage of damage projectile and explosive attacks. After the armor has been deployed, it will attempt to recharge itself to full capacity with the energy reserves from the battery. It takes the armor N seconds to fully charge. Notification (via the HUD) x Health x Ammo x Automatic Health Care Notifies the player when automatic healing has been engaged. x Geiger counter Classic Geiger counter sound and status bar at top of HUD alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal. x Poison Armor Displays the current level of armor. Augmentation Reanimation (w/adrenaline) Causes the player to come back to life after he has been dead for 3 seconds. Will not work if player was gibbed. Single use. Long Jump Used by hitting the ??? key(s). Caused the player to further than normal. SCUBA Used automatically after picked up and after player enters the water. Works for N seconds. Single use. Things powered by the battery Armor Uses N watts for every M units of damage. Heat/Cool Uses N watts for every second in hot/cold area. Long Jump Uses N watts for every jump. Alien Cloak Uses N watts for each use. Each use lasts M seconds. Alien Shield Augments armor. Reduces Armor drain by one half */ // if in range of radiation source, ping geiger counter #define GEIGERDELAY 0.25 void CBasePlayer :: UpdateGeigerCounter( void ) { BYTE range; // delay per update ie: don't flood net with these msgs if (gpGlobals->time < m_flgeigerDelay) return; m_flgeigerDelay = gpGlobals->time + GEIGERDELAY; // send range to radition source to client range = (BYTE) (m_flgeigerRange / 4); if (range != m_igeigerRangePrev) { m_igeigerRangePrev = range; MESSAGE_BEGIN( MSG_ONE, gmsgGeigerRange, NULL, pev ); WRITE_BYTE( range ); MESSAGE_END(); } // reset counter and semaphore if (!RANDOM_LONG(0,3)) m_flgeigerRange = 1000; } /* ================ CheckSuitUpdate Play suit update if it's time ================ */ #define SUITUPDATETIME 3.5 #define SUITFIRSTUPDATETIME 0.1 void CBasePlayer :: CheckSuitUpdate() { // Ignore suit updates if no suit if ( !FBitSet( m_iHideHUD, ITEM_SUIT )) return; // if in range of radiation source, ping geiger counter UpdateGeigerCounter(); } // add sentence to suit playlist queue. if fgroup is true, then // name is a sentence group (HEV_AA), otherwise name is a specific // sentence name ie: !HEV_AA0. If iNoRepeat is specified in // seconds, then we won't repeat playback of this word or sentence // for at least that number of seconds. void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) { int i; int isentence; int iempty = -1; // Ignore suit updates if no suit if ( !FBitSet( m_iHideHUD, ITEM_SUIT ) ) return; if ( g_pGameRules->IsMultiplayer() ) { // due to static channel design, etc. We don't play HEV sounds in multiplayer right now. return; } // if name == NULL, then clear out the queue if (!name) { for (i = 0; i < CSUITPLAYLIST; i++) m_rgSuitPlayList[i] = 0; return; } // get sentence or group number if (!fgroup) { isentence = SENTENCEG_Lookup(name, NULL); if (isentence < 0) { ALERT(at_debug,"HEV couldn't find sentence %s\n",name); return; } } else // mark group number as negative isentence = -SENTENCEG_GetIndex(name); // check norepeat list - this list lets us cancel // the playback of words or sentences that have already // been played within a certain time. for (i = 0; i < CSUITNOREPEAT; i++) { if (isentence == m_rgiSuitNoRepeat[i]) { // this sentence or group is already in // the norepeat list if (m_rgflSuitNoRepeatTime[i] < gpGlobals->time) { // norepeat time has expired, clear it out m_rgiSuitNoRepeat[i] = 0; m_rgflSuitNoRepeatTime[i] = 0.0; iempty = i; break; } else { // don't play, still marked as norepeat return; } } // keep track of empty slot if (!m_rgiSuitNoRepeat[i]) iempty = i; } // sentence is not in norepeat list, save if norepeat time was given if (iNoRepeatTime) { if (iempty < 0) iempty = RANDOM_LONG(0, CSUITNOREPEAT-1); // pick random slot to take over m_rgiSuitNoRepeat[iempty] = isentence; m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->time; } // find empty spot in queue, or overwrite last spot m_rgSuitPlayList[m_iSuitPlayNext++] = isentence; if (m_iSuitPlayNext == CSUITPLAYLIST) m_iSuitPlayNext = 0; if (m_flSuitUpdate <= gpGlobals->time) { if (m_flSuitUpdate == 0) // play queue is empty, don't delay too long before playback m_flSuitUpdate = gpGlobals->time + SUITFIRSTUPDATETIME; else m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME; } } /* ================ CheckPowerups Check for turning off powerups GLOBALS ASSUMED SET: g_ulModelIndexPlayer ================ */ static void CheckPowerups(entvars_t *pev) { if (pev->health <= 0) return; pev->modelindex = g_ulModelIndexPlayer; // don't use eyes } //========================================================= // UpdatePlayerSound - updates the position of the player's // reserved sound slot in the sound list. //========================================================= void CBasePlayer :: UpdatePlayerSound ( void ) { int iBodyVolume; int iVolume; CSound *pSound; // blur fadeout code if( m_flBlurFadeTime > 0.0f ) { float elapsed = gpGlobals->time - m_flBlurFadeTime; float f = elapsed / GAS_DAMAGE_LENGTH; // fadeout time f = bound( 0.0f, f, 1.0f ); m_flBlurAmount = m_flLastBlurAmount + m_flCurBlurAmount * f; if( f == 1.0f ) m_flBlurFadeTime = 0.0f; // hit 100% fade } else if( m_flLastBlurAmount && m_flCurBlurAmount ) { // fixup changelevel issues m_flBlurAmount = m_flLastBlurAmount = m_flCurBlurAmount = 0.0f; } pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt :: ClientSoundIndex( edict() ) ); if ( !pSound ) { ALERT ( at_debug, "Client lost reserved sound!\n" ); return; } pSound->m_iType = bits_SOUND_NONE; // now calculate the best target volume for the sound. If the player's weapon // is louder than his body/movement, use the weapon volume, else, use the body volume. if ( FBitSet ( pev->flags, FL_ONGROUND ) ) { iBodyVolume = pev->velocity.Length(); // clamp the noise that can be made by the body, in case a push trigger, // weapon recoil, or anything shoves the player abnormally fast. if ( iBodyVolume > 512 ) { iBodyVolume = 512; } } else { iBodyVolume = 0; } if ( pev->button & IN_JUMP ) { iBodyVolume += 100; } // convert player move speed and actions into sound audible by monsters. if ( m_iWeaponVolume > iBodyVolume ) { m_iTargetVolume = m_iWeaponVolume; // OR in the bits for COMBAT sound if the weapon is being louder than the player. pSound->m_iType |= bits_SOUND_COMBAT; } else { m_iTargetVolume = iBodyVolume; } // decay weapon volume over time so bits_SOUND_COMBAT stays set for a while m_iWeaponVolume -= 250 * gpGlobals->frametime; if ( m_iWeaponVolume < 0 ) { iVolume = 0; } // if target volume is greater than the player sound's current volume, we paste the new volume in // immediately. If target is less than the current volume, current volume is not set immediately to the // lower volume, rather works itself towards target volume over time. This gives monsters a much better chance // to hear a sound, especially if they don't listen every frame. iVolume = pSound->m_iVolume; if ( m_iTargetVolume > iVolume ) { iVolume = m_iTargetVolume; } else if ( iVolume > m_iTargetVolume ) { iVolume -= 250 * gpGlobals->frametime; if ( iVolume < m_iTargetVolume ) { iVolume = 0; } } if ( m_fNoPlayerSound ) { // debugging flag, lets players move around and shoot without monsters hearing. iVolume = 0; } if ( gpGlobals->time > m_flStopExtraSoundTime ) { // 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 // after actual emission to make sure it gets heard. m_iExtraSoundTypes = 0; } if ( pSound ) { pSound->m_vecOrigin = pev->origin; pSound->m_iType |= ( bits_SOUND_PLAYER | m_iExtraSoundTypes ); pSound->m_iVolume = iVolume; } // keep track of virtual muzzle flash m_iWeaponFlash -= 256 * gpGlobals->frametime; if (m_iWeaponFlash < 0) m_iWeaponFlash = 0; //UTIL_MakeVectors ( pev->angles ); //gpGlobals->v_forward.z = 0; // Below are a couple of useful little bits that make it easier to determine just how much noise the // player is making. // UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 ); //ALERT ( at_console, "%d/%d\n", iVolume, m_iTargetVolume ); } void CBasePlayer::PostThink() { if ( g_fGameOver ) return; // intermission or finale if ( !IsAlive( )) return; // buz test // ALERT(at_console, "lighting is: %d, m_iWeaponFlash is: %d\n", Illumination(), m_iWeaponFlash); // Handle Tank controlling if ( m_pTank != NULL ) { // if they've moved too far from the gun, or selected a weapon, unuse the gun if ( m_pTank->OnControls( pev ) && !pev->weaponmodel ) { //LRC - This is now handled with the Think function, by TrackTarget // m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun } else { // they've moved off the platform m_pTank->Use( this, this, USE_OFF, 0 ); m_pTank = NULL; } } // buz: spec tank if (m_pSpecTank) { if ( m_pSpecTank->OnControls( pev ) && !pev->weaponmodel ) { m_pSpecTank->Use( this, this, USE_SET, 2 ); // try fire the gun } else { // they've moved off the platform m_pSpecTank->Use( this, this, USE_OFF, 0 ); m_pSpecTank = NULL; } } // do weapon stuff ItemPostFrame( ); // buz: turn off gasmask when underwater if (m_iGasMaskOn && pev->waterlevel == 3) { EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_GASMASK_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM ); m_iGasMaskOn = 0; m_iUpdateGasMask = 1; // stop last sound char sz[128]; sprintf(sz, "items/gasm_breath%i.wav", m_iLastGasMaskSound); STOP_SOUND( ENT(pev), CHAN_VOICE, sz ); } // buz: play gasmask sounds if (gpGlobals->time > m_flNextBreathTime && m_iGasMaskOn) { m_iLastGasMaskSound = RANDOM_LONG( 1, 4 ); char sz[128]; sprintf(sz, "items/gasm_breath%i.wav", m_iLastGasMaskSound); EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, sz, 1.0, RANDOM_FLOAT( 0.7, 1 ), 0, PITCH_NORM ); m_flNextBreathTime = gpGlobals->time + RANDOM_FLOAT( 5, 7 ); } // check to see if player landed hard enough to make a sound // falling farther than half of the maximum safe distance, but not as far a max safe distance will // play a bootscrape sound, and no damage will be inflicted. Fallling a distance shorter than half // of maximum safe distance will make no sound. Falling farther than max safe distance will play a // fallpain sound, and damage will be inflicted based on how far the player fell if ( (FBitSet(pev->flags, FL_ONGROUND)) && (pev->health > 0) && m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD ) { // ALERT ( at_console, "%f\n", m_flFallVelocity ); if (pev->watertype == CONTENTS_WATER) { // Did he hit the world or a non-moving entity? // BUG - this happens all the time in water, especially when // BUG - water has current force // if ( !pev->groundentity || VARS(pev->groundentity)->velocity.z == 0 ) // EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM); } else if ( m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED ) {// after this point, we start doing damage float flFallDamage = g_pGameRules->FlPlayerFallDamage( this ); if ( flFallDamage > pev->health ) {//splat // note: play on item channel because we play footstep landing on body channel EMIT_SOUND(ENT(pev), CHAN_ITEM, "common/bodysplat.wav", 1, ATTN_NORM); } if ( flFallDamage > 0 ) { TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL ); pev->punchangle.x = 0; } } if ( IsAlive() ) { SetAnimation( PLAYER_WALK ); } } if (FBitSet(pev->flags, FL_ONGROUND)) { if (m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer()) { CSoundEnt::InsertSound ( bits_SOUND_PLAYER, pev->origin, m_flFallVelocity, 0.2 ); // ALERT( at_console, "fall %f\n", m_flFallVelocity ); } m_flFallVelocity = 0; } // select the proper animation for the player character if ( IsAlive() ) { if (!pev->velocity.x && !pev->velocity.y) SetAnimation( PLAYER_IDLE ); else if ((pev->velocity.x || pev->velocity.y) && (FBitSet(pev->flags, FL_ONGROUND))) SetAnimation( PLAYER_WALK ); else if (pev->waterlevel > 1) SetAnimation( PLAYER_WALK ); } // calc gait animation StudioGaitFrameAdvance( ); // calc player animation StudioFrameAdvance( ); CheckPowerups(pev); UpdatePlayerSound(); // Track button info so we can detect 'pressed' and 'released' buttons next frame m_afButtonLast = pev->button; } // checks if the spot is clear of players BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot ) { CBaseEntity *ent = NULL; if ( pSpot->GetState( pPlayer ) != STATE_ON ) { return FALSE; } while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL ) { // if ent is a client, don't spawn on 'em if ( ent->IsPlayer() && ent != pPlayer ) return FALSE; } return TRUE; } DLL_GLOBAL CBaseEntity *g_pLastSpawn; //LRC- moved to cbase.h //inline int FNullEnt( CBaseEntity *ent ) { return (ent == NULL) || FNullEnt( ent->edict() ); } /* ============ EntSelectSpawnPoint Returns the entity to spawn at USES AND SETS GLOBAL g_pLastSpawn ============ */ edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ) { CBaseEntity *pSpot; edict_t *player; player = pPlayer->edict(); // choose a info_player_deathmatch point if (g_pGameRules->IsCoOp()) { pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_coop"); if ( !FNullEnt(pSpot) ) goto ReturnSpot; pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_start"); if ( !FNullEnt(pSpot) ) goto ReturnSpot; } else if ( g_pGameRules->IsDeathmatch() ) { pSpot = g_pLastSpawn; // Randomize the start spot for ( int i = RANDOM_LONG(1,5); i > 0; i-- ) pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" ); if ( FNullEnt( pSpot ) ) // skip over the null point pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" ); CBaseEntity *pFirstSpot = pSpot; do { if ( pSpot ) { // check if pSpot is valid if ( IsSpawnPointValid( pPlayer, pSpot ) ) { if ( pSpot->pev->origin == Vector( 0, 0, 0 ) ) { pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" ); continue; } // if so, go to pSpot goto ReturnSpot; } } // increment pSpot pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" ); } while ( pSpot != pFirstSpot ); // loop if we're not back to the start // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there if ( !FNullEnt( pSpot ) ) { CBaseEntity *ent = NULL; while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL ) { // if ent is a client, kill em (unless they are ourselves) if ( ent->IsPlayer() && !(ent->edict() == player) ) ent->TakeDamage( VARS(INDEXENT(0)), VARS(INDEXENT(0)), 300, DMG_GENERIC ); } goto ReturnSpot; } } // If startspot is set, (re)spawn there. if ( FStringNull( gpGlobals->startspot ) || !strlen(STRING(gpGlobals->startspot))) { pSpot = UTIL_FindEntityByClassname(NULL, "info_player_start"); if ( !FNullEnt(pSpot) ) goto ReturnSpot; } else { pSpot = UTIL_FindEntityByTargetname( NULL, STRING(gpGlobals->startspot) ); if ( !FNullEnt(pSpot) ) goto ReturnSpot; } ReturnSpot: if ( FNullEnt( pSpot ) ) { ALERT(at_error, "PutClientInServer: no info_player_start on level"); return INDEXENT(0); } g_pLastSpawn = pSpot; return pSpot->edict(); } void CBasePlayer::Spawn( void ) { // ALERT(at_console, "PLAYER spawns at time %f\n", gpGlobals->time); // ALERT(at_console, "KTyJIxy DaeT MHe CuJIbI!\n"); pev->classname = MAKE_STRING("player"); pev->health = 100; pev->armorvalue = 0; pev->takedamage = DAMAGE_AIM; pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_WALK; pev->max_health = pev->health; pev->flags &= FL_PROXY; // keep proxy flag sey by engine pev->flags |= FL_CLIENT; pev->air_finished = gpGlobals->time + 12; pev->dmg = 2; // initial water damage pev->deadflag = DEAD_NO; pev->dmg_take = 0; pev->dmg_save = 0; pev->friction = 1.0; pev->gravity = 1.0; m_bitsHUDDamage = -1; m_bitsDamageType = 0; m_afPhysicsFlags = 0; m_fLongJump = FALSE;// no longjump module. m_flStaminaValue = 100; // buz m_iGasMaskOn = 0; m_flGasMaskTime = 0; // m_iJumpHeight = 100; // g_engfuncs.pfnSetPhysicsKeyValue( edict(), "jh", "100" ); SetJumpHeight(100); // buz rain Rain_dripsPerSecond = 0; Rain_windX = 0; Rain_windY = 0; Rain_randX = 0; Rain_randY = 0; Rain_ideal_dripsPerSecond = 0; Rain_ideal_windX = 0; Rain_ideal_windY = 0; Rain_ideal_randX = 0; Rain_ideal_randY = 0; Rain_endFade = 0; Rain_nextFadeUpdate = 0; g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); pev->fov = m_iFOV = 0;// init field of view. m_iClientFOV = -1; // make sure fov reset is sent m_flClientBlurAmount = -1.0f; m_iClientStamina = -1; m_flNextDecalTime = 0;// let this player decal as soon as he spawns. m_flClientLevelTime = 0; m_flgeigerDelay = gpGlobals->time + 2.0; // wait a few seconds until user-defined message registrations // are recieved by all clients m_flTimeStepSound = 0; m_iStepLeft = 0; m_flFieldOfView = 0.5;// some monsters use this to determine whether or not the player is looking at them. m_bloodColor = BLOOD_COLOR_RED; m_flNextAttack = UTIL_WeaponTimeBase(); StartSneaking(); m_iFlashBattery = 99; m_flFlashLightTime = 1; // force first message // dont let uninitialized value here hurt the player m_flFallVelocity = 0; g_pGameRules->SetDefaultPlayerTeam( this ); edict_t* startent = g_pGameRules->GetPlayerSpawnSpot( this ); //LRC- support the new "start with HEV" flag... // buz - moved here if (startent->v.spawnflags & 1) // the START WITH SUIT flag SetBits( m_iHideHUD, ITEM_SUIT ); if (startent->v.spawnflags & 2) // buz - the START WITH head shield flag SetBits( m_iHideHUD, ITEM_HEADSHIELD ); SET_MODEL(ENT(pev), "models/player.mdl"); g_ulModelIndexPlayer = pev->modelindex; pev->sequence = LookupActivity( ACT_IDLE ); if ( FBitSet(pev->flags, FL_DUCKING) ) UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); else UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); // buz: Paranoia's speed adjustment pev->maxspeed = gSkillData.plrPrimaryMaxSpeed; pev->view_ofs = VEC_VIEW; Precache(); m_HackedGunPos = Vector( 0, 32, 0 ); if ( m_iPlayerSound == SOUNDLIST_EMPTY ) { ALERT ( at_debug, "Couldn't alloc player sound slot!\n" ); } m_fNoPlayerSound = FALSE;// normal sound behavior. m_pLastItem = NULL; m_fInitHUD = TRUE; m_iClientHideHUD = -1; // force this to be recalculated m_fWeapon = FALSE; m_pClientActiveItem = NULL; m_iClientBattery = -1; // reset all ammo values to 0 for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) { m_rgAmmo[i] = 0; m_rgAmmoLast[i] = 0; // client ammo values also have to be reset (the death hud clear messages does on the client side) } m_lastx = m_lasty = 0; m_flNextChatTime = gpGlobals->time; g_pGameRules->PlayerSpawn( this ); } void CBasePlayer :: Precache( void ) { // in the event that the player JUST spawned, and the level node graph // was loaded, fix all of the node graph pointers before the game starts. // !!!BUGBUG - now that we have multiplayer, this needs to be moved! if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) { if ( !WorldGraph.FSetGraphPointers() ) { ALERT ( at_debug, "**Graph pointers were not set!\n"); } else { ALERT ( at_debug, "**Graph Pointers Set!\n" ); } } // SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific) // because they need to precache before any clients have connected // init geiger counter vars during spawn and each time // we cross a level transition m_flgeigerRange = 1000; m_igeigerRangePrev = 1000; m_bitsDamageType = 0; m_bitsHUDDamage = -1; m_iClientBattery = -1; m_flClientBlurAmount = -1.0f; m_flClientLevelTime = -1.0f; m_iTrain = TRAIN_NEW; m_iGoalNeedsUpdate = 2; // buz m_iUpdateHeadShield = 2; // buz - 2 is turn on immediatly m_iUpdateGasMask = 2; // g-cont - 2 is turn on immediatly m_iInitMessagesSent = 0; // buz // Make sure any necessary user messages have been registered LinkUserMessages(); m_iUpdateTime = 5; // won't update for 1/2 a second Rain_needsUpdate = 1; // buz rain m_iLastGasMaskSound = 1; if ( gInitHUD ) m_fInitHUD = TRUE; } int CBasePlayer::Save( CSave &save ) { if ( !CBaseMonster::Save(save) ) return 0; return save.WriteFields( "cPLAYER", "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); } // // Marks everything as new so the player will resend this to the hud. // void CBasePlayer::RenewItems(void) { } int CBasePlayer::Restore( CRestore &restore ) { if ( !CBaseMonster::Restore(restore) ) return 0; int status = restore.ReadFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE(m_playerSaveData) ); SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData; // landmark isn't present. if ( !pSaveData->fUseLandmark ) { ALERT( at_debug, "No Landmark:%s\n", pSaveData->szLandmarkName ); // default to normal spawn edict_t* pentSpawnSpot = EntSelectSpawnPoint( this ); pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1); pev->angles = VARS(pentSpawnSpot)->angles; } pev->v_angle.z = 0; // Clear out roll pev->angles = pev->v_angle; pev->fixangle = TRUE; // turn this way immediately // Copied from spawn() for now m_bloodColor = BLOOD_COLOR_RED; g_ulModelIndexPlayer = pev->modelindex; if ( FBitSet(pev->flags, FL_DUCKING) ) { // Use the crouch HACK //FixPlayerCrouchStuck( edict() ); // Don't need to do this with new player prediction code. UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); } else { UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); } g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); if ( m_fLongJump ) { g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "1" ); } else { g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); } // buz: restore jump height SetJumpHeight(m_iJumpHeight); RenewItems(); return status; } void CBasePlayer::SelectNextItem( int iItem ) { CBasePlayerItem *pItem; pItem = m_rgpPlayerItems[ iItem ]; if (!pItem) return; if (pItem == m_pActiveItem) { // select the next one in the chain pItem = m_pActiveItem->m_pNext; if (! pItem) { return; } CBasePlayerItem *pLast; pLast = pItem; while (pLast->m_pNext) pLast = pLast->m_pNext; // relink chain pLast->m_pNext = m_pActiveItem; m_pActiveItem->m_pNext = NULL; m_rgpPlayerItems[ iItem ] = pItem; } ResetAutoaim( ); if ( m_pActiveItem ) m_pActiveItem->Holster( ); QueueItem( pItem ); if ( m_pActiveItem ) m_pActiveItem->Deploy( ); } void CBasePlayer::QueueItem( CBasePlayerItem *pItem ) { if( !m_pActiveItem ) // no active weapon { m_pActiveItem = pItem; return; // just set this item as active } else { m_pLastItem = m_pActiveItem; m_pActiveItem = NULL;// clear current } m_pNextItem = pItem; // add item to queue } void CBasePlayer :: SelectItem( const char *pstr ) { if ( !pstr ) return; if( m_pActiveItem && m_pActiveItem->m_fInReload ) return; CBasePlayerItem *pItem = NULL; for( int i = 0; i < MAX_ITEM_TYPES; i++ ) { if( m_rgpPlayerItems[i] ) { pItem = m_rgpPlayerItems[i]; while( pItem ) { if(FStrEq( STRING( pItem->pev->netname ), pstr )) break; pItem = pItem->m_pNext; } } if (pItem) break; } if (!pItem) return; if (pItem == m_pActiveItem) return; ResetAutoaim( ); if ( m_pActiveItem ) m_pActiveItem->Holster( ); QueueItem( pItem ); if ( m_pActiveItem ) m_pActiveItem->Deploy( ); } void CBasePlayer::SelectLastItem( void ) { if ( !m_pLastItem ) { return; } // buz fix: dont allow to switch to guns with no bullets if ( !m_pLastItem->CanDeploy( )) return; if ( m_pActiveItem && !m_pActiveItem->CanHolster() ) { return; } if( m_pActiveItem && m_pActiveItem->m_fInReload ) return; ResetAutoaim( ); if ( m_pActiveItem ) m_pActiveItem->Holster( ); QueueItem( m_pLastItem ); if ( m_pActiveItem ) m_pActiveItem->Deploy( ); } //============================================== // HasWeapons - do I have any weapons at all? //============================================== BOOL CBasePlayer::HasWeapons( void ) { int i; for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { if ( m_rgpPlayerItems[ i ] ) { return TRUE; } } return FALSE; } void CBasePlayer::SelectPrevItem( int iItem ) { } const char *CBasePlayer::TeamID( void ) { if ( pev == NULL ) // Not fully connected yet return ""; // return their team name return m_szTeamName; } //============================================== // !!!UNDONE:ultra temporary SprayCan entity to apply // decal frame at a time. For PreAlpha CD //============================================== class CSprayCan : public CBaseEntity { public: void Spawn ( entvars_t *pevOwner ); void Think( void ); virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } }; void CSprayCan::Spawn ( entvars_t *pevOwner ) { pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 ); pev->angles = pevOwner->v_angle; pev->owner = ENT(pevOwner); pev->frame = 0; SetNextThink( 0.1 ); EMIT_SOUND(ENT(pev), CHAN_VOICE, "player/sprayer.wav", 1, ATTN_NORM); } void CSprayCan::Think( void ) { TraceResult tr; float angle = 0.0; UTIL_MakeVectors( pev->angles ); gpGlobals->trace_flags = FTRACE_IGNORE_ALPHATEST; UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_forward * 128, dont_ignore_monsters, pev->owner, &tr ); CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); if( fabs( tr.vecPlaneNormal.z ) > 0.7f ) angle = pev->angles.y; if( pEntity && GET_MODEL_PTR( pEntity->edict() )) UTIL_StudioDecalTrace( &tr, "decal_ban4" ); else { UTIL_TraceCustomDecal( &tr, "decal_ban4", angle ); UTIL_DecalTrace( &tr, "decal_ban4" ); } UTIL_Remove( this ); } class CBloodSplat : public CBaseEntity { public: void Spawn ( entvars_t *pevOwner ); void Spray ( void ); }; void CBloodSplat::Spawn ( entvars_t *pevOwner ) { pev->origin = pevOwner->origin + Vector ( 0 , 0 , 32 ); pev->angles = pevOwner->v_angle; pev->owner = ENT(pevOwner); SetThink(&CBloodSplat:: Spray ); SetNextThink( 0.1 ); } void CBloodSplat::Spray ( void ) { TraceResult tr; if ( g_Language != LANGUAGE_GERMAN ) { UTIL_MakeVectors(pev->angles); gpGlobals->trace_flags = FTRACE_IGNORE_ALPHATEST; UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_forward * 256, dont_ignore_monsters, pev->owner, & tr); CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); if( pEntity && GET_MODEL_PTR( pEntity->edict() )) UTIL_BloodStudioDecalTrace( &tr, BLOOD_COLOR_RED ); else UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); } SetThink(&CBloodSplat:: SUB_Remove ); SetNextThink( 0.1 ); } //============================================== void CBasePlayer :: GiveNamedItem( const char *pszName ) { edict_t *pent = CREATE_NAMED_ENTITY( ALLOC_STRING( pszName )); if ( FNullEnt( pent )) { ALERT ( at_debug, "NULL Ent in GiveNamedItem!\n" ); return; } VARS( pent )->origin = pev->origin; pent->v.spawnflags |= SF_NORESPAWN; DispatchSpawn( pent ); DispatchTouch( pent, ENT( pev )); } CBaseEntity *FindEntityForward( CBaseEntity *pMe ) { TraceResult tr; UTIL_MakeVectors(pMe->pev->v_angle); 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 ); if ( tr.flFraction != 1.0 && !FNullEnt( tr.pHit) ) { CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); return pHit; } return NULL; } BOOL CBasePlayer :: FlashlightIsOn( void ) { return FBitSet(pev->effects, EF_DIMLIGHT); } void CBasePlayer :: FlashlightTurnOn( void ) { if ( !g_pGameRules->FAllowFlashlight() ) { return; } if ( FBitSet( m_iHideHUD, ITEM_SUIT ) ) { EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_ON, 1.0, ATTN_NORM, 0, PITCH_NORM ); SetBits(pev->effects, EF_DIMLIGHT); MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); WRITE_BYTE(1); WRITE_BYTE(m_iFlashBattery); MESSAGE_END(); m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time; } } void CBasePlayer :: FlashlightTurnOff( void ) { if (FlashlightIsOn()) EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM ); ClearBits(pev->effects, EF_DIMLIGHT); MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev ); WRITE_BYTE(0); WRITE_BYTE(m_iFlashBattery); MESSAGE_END(); m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time; } // buz: gasmask turn on/off void CBasePlayer :: ToggleGasMask( void ) { if (!FBitSet( m_iHideHUD, ITEM_GASMASK ) || pev->waterlevel == 3) return; // no gasmask if (gpGlobals->time < m_flGasMaskTime) return; // not too fast if (m_iGasMaskOn) { EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_GASMASK_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM ); m_iGasMaskOn = 0; // stop last sound char sz[128]; sprintf(sz, "items/gasm_breath%i.wav", m_iLastGasMaskSound); STOP_SOUND( ENT(pev), CHAN_VOICE, sz ); // ALERT(at_console, "gasmask off\n"); } else { if (m_pActiveItem && m_pActiveItem->GetMode() == 3) { UTIL_ShowMessage("#GAS_AND_SCOPE", this ); return; } if (m_iHeadShieldOn) { UTIL_ShowMessage("#GAS_AND_SHIELD", this ); return; } EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_GASMASK_ON, 1.0, ATTN_NORM, 0, PITCH_NORM ); m_iGasMaskOn = 1; m_flNextBreathTime = gpGlobals->time + 3; // ALERT(at_console, "gasmask on\n"); } m_flGasMaskTime = gpGlobals->time + 0.5; m_iUpdateGasMask = 1; } // buz: head shield turn on/off void CBasePlayer :: ToggleHeadShield( void ) { if (!FBitSet( m_iHideHUD, ITEM_HEADSHIELD )) return; // no shield if (gpGlobals->time < m_flHeadShieldTime) return; // not too fast if (m_iHeadShieldOn) { m_iHeadShieldOn = 0; EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_SHIELD_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM ); // ALERT(at_console, "head shield off\n"); } else { if (m_pActiveItem && m_pActiveItem->GetMode() == 3) { UTIL_ShowMessage("#SCOPE_AND_SHIELD", this ); return; } if (m_iGasMaskOn) { UTIL_ShowMessage("#GAS_AND_SHIELD", this ); return; } EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, SOUND_SHIELD_ON, 1.0, ATTN_NORM, 0, PITCH_NORM ); m_iHeadShieldOn = 1; // ALERT(at_console, "head shield on\n"); } m_flHeadShieldTime = gpGlobals->time + 0.5; m_iUpdateHeadShield = 1; } // buz void CBasePlayer :: SendInitMessages( void ) { edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); CBaseEntity *pEntity; if ( !pEdict ) return; for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) { if ( pEdict->free ) // Not in use continue; pEntity = CBaseEntity::Instance(pEdict); if ( !pEntity ) continue; pEntity->SendInitMessage( this ); } } /* =============== ForceClientDllUpdate When recording a demo, we need to have the server tell us the entire client state so that the client side .dll can behave correctly. Reset stuff so that the state is transmitted. =============== */ void CBasePlayer :: ForceClientDllUpdate( void ) { m_iClientHealth = -1; m_iClientStamina = -1; m_iClientBattery = -1; m_flClientBlurAmount = -1.0f; m_iTrain |= TRAIN_NEW; // Force new train message. m_fWeapon = FALSE; // Force weapon send m_fKnownItem = FALSE; // Force weaponinit messages. m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message // Now force all the necessary messages // to be sent. UpdateClientData(); } /* ============ ImpulseCommands ============ */ extern float g_flWeaponCheat; int firemode; void CBasePlayer::ImpulseCommands( ) { TraceResult tr;// UNDONE: kill me! This is temporary for PreAlpha CDs // Handle use events PlayerUse(); int iImpulse = (int)pev->impulse; switch (iImpulse) { case 99: { int iOn; if (!gmsgLogo) { iOn = 1; gmsgLogo = REG_USER_MSG("Logo", 1); } else { iOn = 0; } ASSERT( gmsgLogo > 0 ); // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgLogo, NULL, pev ); WRITE_BYTE(iOn); MESSAGE_END(); if(!iOn) gmsgLogo = 0; break; } case 100: // temporary flashlight for level designers if ( FlashlightIsOn()) { FlashlightTurnOff(); } else { FlashlightTurnOn(); } break; case 45: if( m_pActiveItem ) m_pActiveItem->WeaponToggleMode(); break; case 201:// paint decal if( gpGlobals->time < m_flNextDecalTime ) { // too early! break; } UTIL_MakeVectors( pev->v_angle ); gpGlobals->trace_flags = FTRACE_IGNORE_ALPHATEST; UTIL_TraceLine( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + gpGlobals->v_forward * 128, dont_ignore_monsters, ENT(pev), & tr); if ( tr.flFraction != 1.0 ) { // line hit something, so paint a decal m_flNextDecalTime = gpGlobals->time + decalfrequency.value; CSprayCan *pCan = GetClassPtr((CSprayCan *)NULL); pCan->Spawn( pev ); } break; case 198: // gasmask ToggleGasMask(); break; case 204: // Demo recording, update client dll specific data again. ForceClientDllUpdate(); break; default: // check all of the cheat impulse commands now CheatImpulseCommands( iImpulse ); break; } pev->impulse = 0; } const char *pHitboxNames[] = { "Generic", "Head", "Chest", "Stomach", "Left Arm", "Right Arm", "Left Leg", "Right Leg", "Unknown8", "Unknown9", "Unknown10", "Unknown11", "Unknown12", "Unknown13", "Unknown14", "Unknown15", "", }; //========================================================= //========================================================= void CBasePlayer::CheatImpulseCommands( int iImpulse ) { #if !defined( HLDEMO_BUILD ) if ( g_flWeaponCheat == 0.0 ) { return; } CBaseEntity *pEntity; TraceResult tr; switch ( iImpulse ) { case 76: { if (!giPrecacheGrunt) { giPrecacheGrunt = 1; ALERT(at_debug, "You must now restart to use Grunt-o-matic.\n"); } else { UTIL_MakeVectors( Vector( 0, pev->v_angle.y, 0 ) ); Create("monster_human_grunt", pev->origin + gpGlobals->v_forward * 128, pev->angles); } break; } case 90: //LRC - send USE_TOGGLE { char *impulsetarget = (char *)CVAR_GET_STRING( "sohl_impulsetarget" ); if (impulsetarget) FireTargets(impulsetarget, this, this, USE_TOGGLE, 0); break; } case 91: //LRC - send USE_ON { char *impulsetarget = (char *)CVAR_GET_STRING( "sohl_impulsetarget" ); if (impulsetarget) FireTargets(impulsetarget, this, this, USE_ON, 0); break; } case 92: //LRC - send USE_OFF { char *impulsetarget = (char *)CVAR_GET_STRING( "sohl_impulsetarget" ); if (impulsetarget) FireTargets(impulsetarget, this, this, USE_OFF, 0); break; } case 101: { gEvilImpulse101 = TRUE; char *afile = (char *)LOAD_FILE( "scripts/weapons/impulse101.txt", NULL ); char *pfile = afile; char token[256]; while( pfile != NULL ) { // parsing impulse101.txt pfile = COM_ParseFile( pfile, token ); if( !Q_strlen( token )) continue; GiveNamedItem( token ); } if( afile ) FREE_FILE( afile ); gEvilImpulse101 = FALSE; } break; case 102: // Gibbage!!! CGib::SpawnRandomGibs( pev, 1, 1 ); break; case 103: // What the hell are you doing? pEntity = FindEntityForward( this ); if ( pEntity ) { CBaseMonster *pMonster = pEntity->MyMonsterPointer(); if ( pMonster ) pMonster->ReportAIState(); } break; case 104: // Dump all of the global state varaibles (and global entity names) gGlobalState.DumpGlobals(); break; case 105:// player makes no sound for monsters to hear. { if ( m_fNoPlayerSound ) { ALERT ( at_debug, "Player is audible\n" ); m_fNoPlayerSound = FALSE; } else { ALERT ( at_debug, "Player is silent\n" ); m_fNoPlayerSound = TRUE; } break; } case 106: // Give me the classname and targetname of this entity. pEntity = FindEntityForward( this ); if ( pEntity ) { ALERT ( at_debug, "Classname: %s", STRING( pEntity->pev->classname ) ); if ( !FStringNull ( pEntity->pev->targetname ) ) { ALERT ( at_debug, " - Targetname: %s\n", STRING( pEntity->pev->targetname ) ); } else { ALERT ( at_debug, " - TargetName: No Targetname\n" ); } ALERT ( at_debug, "Model: %s\n", STRING( pEntity->pev->model ) ); if ( pEntity->pev->globalname ) ALERT ( at_debug, "Globalname: %s\n", STRING( pEntity->pev->globalname ) ); ALERT(at_debug, "State: %s\n", GetStringForState( pEntity->GetState() )); //LRC ALERT ( at_debug, "rendermode: %d\n", pEntity->pev->rendermode ); ALERT ( at_debug, "renderamt: %g\n", pEntity->pev->renderamt ); ALERT ( at_debug, "renderfx: %d\n", pEntity->pev->renderfx ); ALERT ( at_debug, "origin: %g %g %g\n", pEntity->pev->origin.x, pEntity->pev->origin.y, pEntity->pev->origin.z ); ALERT ( at_debug, "angles: %g %g %g\n", pEntity->pev->angles.x, pEntity->pev->angles.y, pEntity->pev->angles.z ); } break; case 107: { TraceResult tr; edict_t *pWorld = g_engfuncs.pfnPEntityOfEntIndex( 0 ); Vector start = pev->origin + pev->view_ofs; Vector end = start + gpGlobals->v_forward * 1024; UTIL_TraceLine( start, end, dont_ignore_monsters, edict(), &tr ); if( tr.pHit ) pWorld = tr.pHit; if( pWorld->v.solid == SOLID_CUSTOM ) { // hitting env_static, so tr.surf may be valid if( tr.pMat ) { char basename[64], texname[64]; COM_FileBase( STRING( pWorld->v.model ), basename ); COM_FileBase( tr.pMat->pSource->name, texname ); ALERT( at_debug, "Material: %s/%s\n", basename, texname ); } } else { const char *pTextureName = TRACE_TEXTURE( pWorld, start, end ); if( pTextureName ) ALERT( at_debug, "Material: %s\n", pTextureName ); } } break; case 195:// show shortest paths for entire level to nearest node { Create("node_viewer_fly", pev->origin, pev->angles); } break; case 196:// show shortest paths for entire level to nearest node { Create("node_viewer_large", pev->origin, pev->angles); } break; case 197:// show shortest paths for entire level to nearest node { Create("node_viewer_human", pev->origin, pev->angles); } break; case 199:// show nearest node and all connections { ALERT ( at_debug, "%d\n", WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) ); WorldGraph.ShowNodeConnections ( WorldGraph.FindNearestNode ( pev->origin, bits_NODE_GROUP_REALM ) ); } break; case 202:// Random blood splatter UTIL_MakeVectors(pev->v_angle); gpGlobals->trace_flags = FTRACE_IGNORE_ALPHATEST; UTIL_TraceLine ( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + gpGlobals->v_forward * 256, dont_ignore_monsters, ENT(pev), & tr); if ( tr.flFraction != 1.0 ) {// line hit something, so paint a decal CBloodSplat *pBlood = GetClassPtr((CBloodSplat *)NULL); pBlood->Spawn( pev ); } break; case 203:// remove creature. pEntity = FindEntityForward( this ); if ( pEntity ) { if ( pEntity->pev->takedamage ) { pEntity->SetThink(&CBaseEntity::SUB_Remove); pEntity->SetNextThink( 0.1 ); } else UTIL_Remove( pEntity ); } break; } #endif // HLDEMO_BUILD } // // Add a weapon to the player (Item == Weapon == Selectable Object) // int CBasePlayer :: AddPlayerItem( CBasePlayerItem *pItem ) { CBasePlayerItem *pInsert; pInsert = m_rgpPlayerItems[pItem->iItemSlot()]; while( pInsert ) { if ( FStrEq( STRING( pInsert->pev->netname ), STRING( pItem->pev->netname ) )) { if (pItem->AddDuplicate( pInsert )) { g_pGameRules->PlayerGotWeapon ( this, pItem ); pItem->CheckRespawn(); // ugly hack to update clip w/o an update clip message pInsert->UpdateItemInfo( ); if (m_pActiveItem) m_pActiveItem->UpdateItemInfo( ); pItem->Kill( ); } else if (gEvilImpulse101) { // FIXME: remove anyway for deathmatch testing pItem->Kill( ); } return FALSE; } pInsert = pInsert->m_pNext; } if (pItem->AddToPlayer( this )) { g_pGameRules->PlayerGotWeapon ( this, pItem ); pItem->CheckRespawn(); pItem->m_pNext = m_rgpPlayerItems[pItem->iItemSlot()]; m_rgpPlayerItems[pItem->iItemSlot()] = pItem; // should we switch to this item? if ( g_pGameRules->FShouldSwitchWeapon( this, pItem ) ) { SwitchWeapon( pItem ); } return TRUE; } else if (gEvilImpulse101) { // FIXME: remove anyway for deathmatch testing pItem->Kill( ); } return FALSE; } int CBasePlayer::RemovePlayerItem( CBasePlayerItem *pItem ) { if (m_pActiveItem == pItem) { ResetAutoaim( ); pItem->Holster( true ); pItem->DontThink();// crowbar may be trying to swing again, etc. pItem->SetThink( NULL ); m_pActiveItem = NULL; pev->viewmodel = 0; pev->weaponmodel = 0; } else if ( m_pLastItem == pItem ) m_pLastItem = NULL; CBasePlayerItem *pPrev = m_rgpPlayerItems[pItem->iItemSlot()]; if (pPrev == pItem) { m_rgpPlayerItems[pItem->iItemSlot()] = pItem->m_pNext; return TRUE; } else { while (pPrev && pPrev->m_pNext != pItem) { pPrev = pPrev->m_pNext; } if (pPrev) { pPrev->m_pNext = pItem->m_pNext; return TRUE; } } return FALSE; } // // Returns the unique ID for the ammo, or -1 if error // int CBasePlayer :: GiveAmmo( int iCount, const char *szAmmoName ) { if( !szAmmoName || !*szAmmoName ) { // no ammo. return -1; } AmmoInfo *pAmmo = UTIL_FindAmmoType( szAmmoName ); if( !pAmmo || !g_pGameRules->CanHaveAmmo( this, szAmmoName, pAmmo->iMaxCarry )) { // game rules say I can't have any more of this ammo type. return -1; } int i = 0; i = GetAmmoIndex( szAmmoName ); if ( i < 0 || i >= MAX_AMMO_SLOTS ) return -1; int iAdd = Q_min( iCount, pAmmo->iMaxCarry - m_rgAmmo[i] ); if ( iAdd < 1 ) return i; m_rgAmmo[ i ] += iAdd; if ( gmsgAmmoPickup ) // make sure the ammo messages have been linked first { // Send the message that ammo has been picked up MESSAGE_BEGIN( MSG_ONE, gmsgAmmoPickup, NULL, pev ); WRITE_BYTE( GetAmmoIndex( szAmmoName )); // ammo ID WRITE_BYTE( iAdd ); // amount WRITE_STRING( szAmmoName ); // buz: send also ammo name to display in hud.. MESSAGE_END(); } return i; } /* ============ ItemPreFrame Called every frame by the player PreThink ============ */ void CBasePlayer::ItemPreFrame() { if ( gpGlobals->time < m_flNextAttack ) return; if ( !m_pActiveItem ) { // last item needs to playing holster again if( m_pLastItem && m_pLastItem->WaitForHolster( )) { m_pLastItem->Holster(); } else if( m_pNextItem ) { m_pActiveItem = m_pNextItem; m_pActiveItem->Deploy(); m_pNextItem = NULL; } } if ( !m_pActiveItem ) return; m_pActiveItem->ItemPreFrame( ); } /* ============ ItemPostFrame Called every frame by the player PostThink ============ */ void CBasePlayer::ItemPostFrame() { // buz test // ALERT(at_console, "pl speed: %f\n", pev->maxspeed); // check if the player is using a tank if ( m_pTank != NULL ) return; // buz: spec tank if ( m_pSpecTank ) return; if ( gpGlobals->time < m_flNextAttack ) return; ImpulseCommands(); if (!m_pActiveItem) return; m_pActiveItem->ItemPostFrame( ); } int CBasePlayer::AmmoInventory( int iAmmoIndex ) { if (iAmmoIndex == -1) { return -1; } return m_rgAmmo[ iAmmoIndex ]; } int CBasePlayer :: GetAmmoIndex( const char *psz ) { if( !psz ) return -1; for( int i = 1; i < MAX_AMMO_SLOTS; i++ ) { if( !CBasePlayerAmmo::AmmoInfoArray[i].pszName ) continue; if( !Q_stricmp( CBasePlayerAmmo::AmmoInfoArray[i].pszName, psz )) return i; } return -1; } const char *CBasePlayer :: GetAmmoName(int index ) { if( index == -1 ) return NULL; return CBasePlayerAmmo :: AmmoInfoArray[index].pszName; } // Called from UpdateClientData // makes sure the client has all the necessary ammo info, if values have changed void CBasePlayer::SendAmmoUpdate(void) { for (int i=0; i < MAX_AMMO_SLOTS;i++) { if (m_rgAmmo[i] != m_rgAmmoLast[i]) { m_rgAmmoLast[i] = m_rgAmmo[i]; ASSERT( m_rgAmmo[i] >= 0 ); ASSERT( m_rgAmmo[i] < 255 ); // send "Ammo" update message MESSAGE_BEGIN( MSG_ONE, gmsgAmmoX, NULL, pev ); WRITE_BYTE( i ); WRITE_BYTE( max( min( m_rgAmmo[i], 254 ), 0 ) ); // clamp the value to one byte MESSAGE_END(); } } } /* ========================================================= UpdateClientData resends any changed player HUD info to the client. Called every frame by Player::PreThink Also called at start of demo recording and playback by ForceClientDllUpdate to ensure the demo gets messages reflecting all of the HUD state info. ========================================================= */ void CBasePlayer :: UpdateClientData( void ) { if (m_fInitHUD) { m_fInitHUD = FALSE; gInitHUD = FALSE; MESSAGE_BEGIN( MSG_ONE, gmsgResetHUD, NULL, pev ); WRITE_BYTE( 0 ); MESSAGE_END(); if ( !m_fGameHUDInitialized ) { MESSAGE_BEGIN( MSG_ONE, gmsgInitHUD, NULL, pev ); MESSAGE_END(); g_pGameRules->InitHUD( this ); m_fGameHUDInitialized = TRUE; if ( g_pGameRules->IsMultiplayer() ) { FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 ); } } FireTargets( "game_playerspawn", this, this, USE_TOGGLE, 0 ); InitStatusBar(); } if ( m_iHideHUD != m_iClientHideHUD ) { MESSAGE_BEGIN( MSG_ONE, gmsgHideWeapon, NULL, pev ); WRITE_BYTE( m_iHideHUD ); MESSAGE_END(); m_iClientHideHUD = m_iHideHUD; } if ( m_iFOV != m_iClientFOV ) { MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev ); WRITE_BYTE( m_iFOV ); MESSAGE_END(); // cache FOV change at end of function, so weapon updates can see that FOV has changed } // HACKHACK -- send the message to display the game title if (gDisplayTitle) { MESSAGE_BEGIN( MSG_ONE, gmsgShowGameTitle, NULL, pev ); WRITE_BYTE( 0 ); MESSAGE_END(); gDisplayTitle = 0; } if (pev->health != m_iClientHealth) { int iHealth = max( pev->health, 0 ); // make sure that no negative health values are sent // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgHealth, NULL, pev ); WRITE_BYTE( iHealth ); MESSAGE_END(); m_iClientHealth = pev->health; } if ((int)m_flStaminaValue != m_iClientStamina) { int iStamina = (int)m_flStaminaValue; // send "stamina" update message MESSAGE_BEGIN( MSG_ONE, gmsgStamina, NULL, pev ); WRITE_SHORT(iStamina ); MESSAGE_END(); m_iClientStamina = iStamina; } if( m_flBlurAmount != m_flClientBlurAmount ) { m_flClientBlurAmount = m_flBlurAmount; MESSAGE_BEGIN( MSG_ONE, gmsgBlurEffect, NULL, pev ); WRITE_SHORT( m_flBlurAmount * 10000 ); MESSAGE_END(); } if (pev->armorvalue != m_iClientBattery) { m_iClientBattery = pev->armorvalue; ASSERT( gmsgBattery > 0 ); // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgBattery, NULL, pev ); WRITE_SHORT( (int)pev->armorvalue); MESSAGE_END(); } if (pev->dmg_take || pev->dmg_save || m_bitsHUDDamage != m_bitsDamageType) { // Comes from inside me if not set Vector damageOrigin = pev->origin; // send "damage" message // causes screen to flash, and pain compass to show direction of damage edict_t *other = pev->dmg_inflictor; if ( other ) { CBaseEntity *pEntity = CBaseEntity::Instance(other); if ( pEntity ) damageOrigin = pEntity->Center(); } // only send down damage type that have hud art int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD; MESSAGE_BEGIN( MSG_ONE, gmsgDamage, NULL, pev ); WRITE_BYTE( pev->dmg_save ); WRITE_BYTE( pev->dmg_take ); WRITE_LONG( visibleDamageBits ); WRITE_COORD( damageOrigin.x ); WRITE_COORD( damageOrigin.y ); WRITE_COORD( damageOrigin.z ); MESSAGE_END(); pev->dmg_take = 0; pev->dmg_save = 0; m_bitsHUDDamage = m_bitsDamageType; // Clear off non-time-based damage indicators m_bitsDamageType &= DMG_TIMEBASED; } // RAIN START // calculate and update rain fading if (Rain_endFade > 0) { if (gpGlobals->time < Rain_endFade) { // we're in fading process if (Rain_nextFadeUpdate <= gpGlobals->time) { int secondsLeft = Rain_endFade - gpGlobals->time + 1; Rain_dripsPerSecond += (Rain_ideal_dripsPerSecond - Rain_dripsPerSecond) / secondsLeft; Rain_windX += (Rain_ideal_windX - Rain_windX) / (float)secondsLeft; Rain_windY += (Rain_ideal_windY - Rain_windY) / (float)secondsLeft; Rain_randX += (Rain_ideal_randX - Rain_randX) / (float)secondsLeft; Rain_randY += (Rain_ideal_randY - Rain_randY) / (float)secondsLeft; Rain_nextFadeUpdate = gpGlobals->time + 1; // update once per second Rain_needsUpdate = 1; ALERT(at_aiconsole, "Rain fading: curdrips: %i, idealdrips %i\n", Rain_dripsPerSecond, Rain_ideal_dripsPerSecond); } } else { // finish fading process Rain_nextFadeUpdate = 0; Rain_endFade = 0; Rain_dripsPerSecond = Rain_ideal_dripsPerSecond; Rain_windX = Rain_ideal_windX; Rain_windY = Rain_ideal_windY; Rain_randX = Rain_ideal_randX; Rain_randY = Rain_ideal_randY; Rain_needsUpdate = 1; ALERT(at_aiconsole, "Rain fading finished at %i drips\n", Rain_dripsPerSecond); } } // send rain message if (Rain_needsUpdate) { //search for rain_settings entity edict_t *pFind; pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "rain_settings" ); if (!FNullEnt( pFind )) { // rain allowed on this map CBaseEntity *pEnt = CBaseEntity::Instance( pFind ); CRainSettings *pRainSettings = (CRainSettings *)pEnt; float raindistance = pRainSettings->Rain_Distance; float rainheight = pRainSettings->pev->origin[2]; int rainmode = pRainSettings->Rain_Mode; // search for constant rain_modifies pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "rain_modify" ); while ( !FNullEnt( pFind ) ) { if (pFind->v.spawnflags & 1) { // copy settings to player's data and clear fading CBaseEntity *pEnt = CBaseEntity::Instance( pFind ); CRainModify *pRainModify = (CRainModify *)pEnt; Rain_dripsPerSecond = pRainModify->Rain_Drips; Rain_windX = pRainModify->Rain_windX; Rain_windY = pRainModify->Rain_windY; Rain_randX = pRainModify->Rain_randX; Rain_randY = pRainModify->Rain_randY; Rain_endFade = 0; break; } pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "rain_modify" ); } MESSAGE_BEGIN(MSG_ONE, gmsgRainData, NULL, pev); WRITE_SHORT(Rain_dripsPerSecond); WRITE_COORD(raindistance); WRITE_COORD(Rain_windX); WRITE_COORD(Rain_windY); WRITE_COORD(Rain_randX); WRITE_COORD(Rain_randY); WRITE_SHORT(rainmode); WRITE_COORD(rainheight); MESSAGE_END(); if (Rain_dripsPerSecond) ALERT(at_aiconsole, "Sending enabling rain message\n"); else ALERT(at_aiconsole, "Sending disabling rain message\n"); } else { // no rain on this map Rain_dripsPerSecond = 0; Rain_windX = 0; Rain_windY = 0; Rain_randX = 0; Rain_randY = 0; Rain_ideal_dripsPerSecond = 0; Rain_ideal_windX = 0; Rain_ideal_windY = 0; Rain_ideal_randX = 0; Rain_ideal_randY = 0; Rain_endFade = 0; Rain_nextFadeUpdate = 0; ALERT(at_aiconsole, "Clearing rain data\n"); } Rain_needsUpdate = 0; } // RAIN END // Update Flashlight // buz: infinite flashlight /* if ((m_flFlashLightTime) && (m_flFlashLightTime <= gpGlobals->time)) { if (FlashlightIsOn()) { if (m_iFlashBattery) { m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time; m_iFlashBattery--; if (!m_iFlashBattery) FlashlightTurnOff(); } } else { if (m_iFlashBattery < 100) { m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time; m_iFlashBattery++; } else m_flFlashLightTime = 0; } MESSAGE_BEGIN( MSG_ONE, gmsgFlashBattery, NULL, pev ); WRITE_BYTE(m_iFlashBattery); MESSAGE_END(); }*/ // buz: update gasmask if (m_iUpdateGasMask) { if (m_iUpdateGasMask == 2) { if (m_iGasMaskOn) { MESSAGE_BEGIN( MSG_ONE, gmsgGasMask, NULL, pev ); WRITE_BYTE(2); MESSAGE_END(); } // dont send "fast off" update, because client sets it by default } else { if (m_iGasMaskOn) { MESSAGE_BEGIN( MSG_ONE, gmsgGasMask, NULL, pev ); WRITE_BYTE(1); MESSAGE_END(); } else { MESSAGE_BEGIN( MSG_ONE, gmsgGasMask, NULL, pev ); WRITE_BYTE(0); MESSAGE_END(); } } m_iUpdateGasMask = 0; } // buz: update head shield // 0 off, 1 on, 2 fast on if (m_iUpdateHeadShield) { if (m_iUpdateHeadShield == 2) { if (m_iHeadShieldOn) { MESSAGE_BEGIN( MSG_ONE, gmsgHeadShield, NULL, pev ); WRITE_BYTE(2); MESSAGE_END(); } // dont send "fast off" update, because client sets it by default } else { if (m_iHeadShieldOn) { MESSAGE_BEGIN( MSG_ONE, gmsgHeadShield, NULL, pev ); WRITE_BYTE(1); MESSAGE_END(); } else { MESSAGE_BEGIN( MSG_ONE, gmsgHeadShield, NULL, pev ); WRITE_BYTE(0); MESSAGE_END(); } } m_iUpdateHeadShield = 0; } // buz: update goal window if (m_iGoalNeedsUpdate) { if (FStringNull(m_strCurrentGoalName)) { MESSAGE_BEGIN( MSG_ONE, gmsgTabPanel, NULL, pev ); WRITE_BYTE(0); // turn off MESSAGE_END(); ALERT(at_aiconsole, "Player goal window cleared\n"); } else { int flags = 1; if (!FStringNull(m_strCurrentGoalTitleName)) flags |= TP_FL_TITLE; if (!FStringNull(m_strCurrentGoalImageName)) flags |= TP_FL_IMAGE; if (m_iGoalNeedsUpdate != 2) flags |= TP_FL_POPUP; MESSAGE_BEGIN( MSG_ONE, gmsgTabPanel, NULL, pev ); WRITE_BYTE(flags); // show WRITE_STRING(STRING(m_strCurrentGoalName)); if (flags & TP_FL_TITLE) WRITE_STRING(STRING(m_strCurrentGoalTitleName)); if (flags & TP_FL_IMAGE) WRITE_STRING(STRING(m_strCurrentGoalImageName)); MESSAGE_END(); ALERT(at_aiconsole, "Player goal description set\n"); } m_iGoalNeedsUpdate = 0; } // buz: send particle systems if (!m_iInitMessagesSent) { SendInitMessages(); m_iInitMessagesSent = 1; // initialize level-time here m_flLevelTime = gGlobalState.EntityGetTime( MAKE_STRING( "GLOBAL_TIME" )); } if (m_iTrain & TRAIN_NEW) { ASSERT( gmsgTrain > 0 ); // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgTrain, NULL, pev ); WRITE_BYTE(m_iTrain & 0xF); MESSAGE_END(); m_iTrain &= ~TRAIN_NEW; } // if time is used on a map if( m_flLevelTime != -1.0f ) { m_flLevelTime += gpGlobals->frametime * GLOBAL_TIME_STEP; // evaluate level time if( m_flLevelTime > 24.0f ) m_flLevelTime = 0.0f; // clamp the time // probably it will be think every frame if( fabs( m_flClientLevelTime - m_flLevelTime ) > GLOBAL_TIME_STEP * GLOBAL_TIME_STEP ) { // ALERT( at_console, "CurTime: %g\n", m_flLevelTime ); MESSAGE_BEGIN( MSG_ONE, gmsgLevelTime, NULL, pev ); WRITE_LONG( *(int *)&m_flLevelTime ); // convert float to long MESSAGE_END(); gGlobalState.EntitySetTime( MAKE_STRING( "GLOBAL_TIME" ), m_flLevelTime ); m_flClientLevelTime = m_flLevelTime; } } // // New Weapon? // if (!m_fKnownItem) { m_fKnownItem = TRUE; // WeaponInit Message // byte = # of weapons // // for each weapon: // byte name str length (not including null) // bytes... name // byte Ammo Type // byte Ammo2 Type // byte bucket // byte bucket pos // byte flags // ???? Icons // Send ALL the weapon info now for (int i = 0; i < MAX_WEAPONS; i++) { ItemInfo& II = CBasePlayerItem::ItemInfoArray[i]; if ( !II.iId ) continue; const char *pszName; if (!II.pszName) pszName = "Empty"; else pszName = II.pszName; MESSAGE_BEGIN( MSG_ONE, gmsgWeaponList, NULL, pev ); WRITE_STRING(pszName); // string weapon name WRITE_BYTE(GetAmmoIndex(II.pszAmmo1)); // byte Ammo Type WRITE_BYTE(II.iMaxAmmo1); // byte Max Ammo 1 WRITE_BYTE(GetAmmoIndex(II.pszAmmo2)); // byte Ammo2 Type WRITE_BYTE(II.iMaxAmmo2); // byte Max Ammo 2 WRITE_BYTE(II.iSlot); // byte bucket WRITE_BYTE(II.iPosition); // byte bucket pos WRITE_BYTE(II.iId); // byte id (bit index into pev->weapons) WRITE_SHORT( II.iFlags ); // short Flags MESSAGE_END(); } } SendAmmoUpdate(); // buz: update spec machinegun state if (m_pSpecTank) { m_pSpecTank->Use( this, this, USE_SET, 3 ); // update client data } // Update all the items for ( int i = 0; i < MAX_ITEM_TYPES; i++ ) { if ( m_rgpPlayerItems[i] ) // each item updates it's successors m_rgpPlayerItems[i]->UpdateClientData( this ); } // buz: fix bug with crosshairs: send weapon update message if // no one item didnt sent it before if ( !m_fWeapon ) { MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev ); WRITE_BYTE( 0 ); WRITE_BYTE( 0 ); WRITE_BYTE( 0 ); MESSAGE_END(); // m_iClientClip = m_iClip; // m_iClientWeaponState = state; m_fWeapon = TRUE; } // Cache and client weapon change m_pClientActiveItem = m_pActiveItem; m_iClientFOV = m_iFOV; // Update Status Bar if ( m_flNextSBarUpdateTime < gpGlobals->time ) { UpdateStatusBar(); m_flNextSBarUpdateTime = gpGlobals->time + 0.2; } // Wargon: Если костюма нет, то фонарик выключается. if (!FBitSet( m_iHideHUD, ITEM_SUIT ) && FlashlightIsOn()) FlashlightTurnOff(); // if ( pev->waterlevel == 3 && FlashlightIsOn()) // FlashlightTurnOff(); // Wargon: Серверный код иконки юза. CBaseEntity *pObject = NULL; CBaseEntity *pClosest = NULL; Vector vecLOS; float flMaxDot = VIEW_FIELD_NARROW; float flDot; int caps; TraceResult tr; UTIL_MakeVectors( pev->v_angle ); UTIL_TraceLine( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + (gpGlobals->v_forward * PLAYER_SEARCH_RADIUS), dont_ignore_monsters, ENT(pev), &tr ); if (tr.pHit) { pObject = CBaseEntity::Instance(tr.pHit); if (!pObject || !(pObject->ObjectCaps() & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE)) || (pObject->ObjectCaps() & FCAP_HIDE_USE)) pObject = NULL; } if (!pObject) { while ((pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS )) != NULL) { caps = pObject->ObjectCaps(); if (caps & (FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE) && !(caps & FCAP_ONLYDIRECT_USE) && !(pObject->ObjectCaps() & FCAP_HIDE_USE)) { CBaseMonster *pMonster = pObject->MyMonsterPointer(); if (pMonster) { Vector mins, maxs; pMonster->ExtractBbox(pMonster->pev->sequence, mins, maxs); vecLOS = (((mins + maxs) * 0.5) + pMonster->pev->origin - (pev->origin + pev->view_ofs)); vecLOS = UTIL_ClampVectorToBox( vecLOS, ((mins + maxs) * 0.5) ); } else { vecLOS = (VecBModelOrigin( pObject->pev ) - (pev->origin + pev->view_ofs)); vecLOS = UTIL_ClampVectorToBox( vecLOS, pObject->pev->size * 0.5 ); } flDot = DotProduct (vecLOS, gpGlobals->v_forward); if (flDot > flMaxDot || vecLOS == g_vecZero ) { pClosest = pObject; flMaxDot = flDot; } } } pObject = pClosest; if( pObject && pObject->ObjectCaps() & FCAP_USE_ONLY ) { // make sure what item not blocked by entity UTIL_TraceLine( pev->origin + pev->view_ofs, pObject->pev->origin, dont_ignore_monsters, ENT(pev), &tr ); if( tr.flFraction < 1.0f ) pObject = NULL; // blocked } } if (!pObject) { UTIL_TraceLine( pev->origin + pev->view_ofs, pev->origin + pev->view_ofs + (gpGlobals->v_forward * PLAYER_DISTUSE_RADIUS), dont_ignore_monsters, ENT(pev), &tr ); if (tr.pHit) { pObject = CBaseEntity::Instance(tr.pHit); if (!pObject || !(pObject->ObjectCaps() & FCAP_DISTANCE_USE) || (pObject->ObjectCaps() & FCAP_HIDE_USE)) pObject = NULL; } } if ((pObject) && !((m_pTank != NULL) || (m_pSpecTank))) { MESSAGE_BEGIN( MSG_ONE, gmsgCanUse, NULL, pev ); WRITE_BYTE(1); MESSAGE_END(); } else { MESSAGE_BEGIN( MSG_ONE, gmsgCanUse, NULL, pev ); WRITE_BYTE(0); MESSAGE_END(); } } //========================================================= // buz: PlayerSetGoalDesc - sets goal description for this client //========================================================= void CBasePlayer::PlayerSetGoalDesc( string_t strindex, string_t title, string_t imgname ) { m_strCurrentGoalName = strindex; m_strCurrentGoalTitleName = title; m_strCurrentGoalImageName = imgname; m_iGoalNeedsUpdate = 1; } //========================================================= // FBecomeProne - Overridden for the player to set the proper // physics flags when a barnacle grabs player. //========================================================= BOOL CBasePlayer :: FBecomeProne ( void ) { m_afPhysicsFlags |= PFLAG_ONBARNACLE; return TRUE; } //========================================================= // BarnacleVictimBitten - bad name for a function that is called // by Barnacle victims when the barnacle pulls their head // into its mouth. For the player, just die. //========================================================= void CBasePlayer :: BarnacleVictimBitten ( entvars_t *pevBarnacle ) { TakeDamage ( pevBarnacle, pevBarnacle, pev->health + pev->armorvalue, DMG_SLASH | DMG_ALWAYSGIB ); } //========================================================= // BarnacleVictimReleased - overridden for player who has // physics flags concerns. //========================================================= void CBasePlayer :: BarnacleVictimReleased ( void ) { m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; } //========================================================= // Illumination // return player light level plus virtual muzzle flash //========================================================= int CBasePlayer :: Illumination( void ) { int iIllum = CBaseEntity::Illumination( ); iIllum += m_iWeaponFlash; if (iIllum > 255) return 255; return iIllum; } void CBasePlayer :: EnableControl(BOOL fControl) { if (!fControl) { pev->flags |= FL_FROZEN; pev->velocity = g_vecZero; //LRC - stop view bobbing } else pev->flags &= ~FL_FROZEN; } #define DOT_1DEGREE 0.9998476951564 #define DOT_2DEGREE 0.9993908270191 #define DOT_3DEGREE 0.9986295347546 #define DOT_4DEGREE 0.9975640502598 #define DOT_5DEGREE 0.9961946980917 #define DOT_6DEGREE 0.9945218953683 #define DOT_7DEGREE 0.9925461516413 #define DOT_8DEGREE 0.9902680687416 #define DOT_9DEGREE 0.9876883405951 #define DOT_10DEGREE 0.9848077530122 #define DOT_15DEGREE 0.9659258262891 #define DOT_20DEGREE 0.9396926207859 #define DOT_25DEGREE 0.9063077870367 //========================================================= // Autoaim // set crosshair position to point to enemey //========================================================= Vector CBasePlayer :: GetAutoaimVector( float flDelta ) { if (g_iSkillLevel == SKILL_HARD) { UTIL_MakeVectors( pev->v_angle + pev->punchangle ); return gpGlobals->v_forward; } Vector vecSrc = GetGunPosition( ); float flDist = 8192; // always use non-sticky autoaim // UNDONE: use sever variable to chose! if (1 || g_iSkillLevel == SKILL_MEDIUM) { m_vecAutoAim = Vector( 0, 0, 0 ); // flDelta *= 0.5; } BOOL m_fOldTargeting = m_fOnTarget; Vector angles = AutoaimDeflection(vecSrc, flDist, flDelta ); // update ontarget if changed if ( !g_pGameRules->AllowAutoTargetCrosshair() ) m_fOnTarget = 0; else if (m_fOldTargeting != m_fOnTarget) { m_pActiveItem->UpdateItemInfo( ); } if (angles.x > 180) angles.x -= 360; if (angles.x < -180) angles.x += 360; if (angles.y > 180) angles.y -= 360; if (angles.y < -180) angles.y += 360; if (angles.x > 25) angles.x = 25; if (angles.x < -25) angles.x = -25; if (angles.y > 12) angles.y = 12; if (angles.y < -12) angles.y = -12; // always use non-sticky autoaim // UNDONE: use sever variable to chose! if (0 || g_iSkillLevel == SKILL_EASY) { m_vecAutoAim = m_vecAutoAim * 0.67 + angles * 0.33; } else { m_vecAutoAim = angles * 0.9; } // m_vecAutoAim = m_vecAutoAim * 0.99; // Don't send across network if sv_aim is 0 if ( g_psv_aim->value != 0 ) { if ( m_vecAutoAim.x != m_lastx || m_vecAutoAim.y != m_lasty ) { SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y ); m_lastx = m_vecAutoAim.x; m_lasty = m_vecAutoAim.y; } } // ALERT( at_console, "%f %f\n", angles.x, angles.y ); UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); return gpGlobals->v_forward; } Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta ) { edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); CBaseEntity *pEntity; float bestdot; Vector bestdir; edict_t *bestent; TraceResult tr; if ( g_psv_aim->value == 0 ) { m_fOnTarget = FALSE; return g_vecZero; } UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); // try all possible entities bestdir = gpGlobals->v_forward; bestdot = flDelta; // +- 10 degrees bestent = NULL; m_fOnTarget = FALSE; UTIL_TraceLine( vecSrc, vecSrc + bestdir * flDist, dont_ignore_monsters, edict(), &tr ); if ( tr.pHit && tr.pHit->v.takedamage != DAMAGE_NO) { // don't look through water if (!((pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3) || (pev->waterlevel == 3 && tr.pHit->v.waterlevel == 0))) { if (tr.pHit->v.takedamage == DAMAGE_AIM) m_fOnTarget = TRUE; return m_vecAutoAim; } } for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) { Vector center; Vector dir; float dot; if ( pEdict->free ) // Not in use continue; if (pEdict->v.takedamage != DAMAGE_AIM) continue; if (pEdict == edict()) continue; // if (pev->team > 0 && pEdict->v.team == pev->team) // continue; // don't aim at teammate if ( !g_pGameRules->ShouldAutoAim( this, pEdict ) ) continue; pEntity = Instance( pEdict ); if (pEntity == NULL) continue; if (!pEntity->IsAlive()) continue; // don't look through water if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0)) continue; center = pEntity->BodyTarget( vecSrc ); dir = (center - vecSrc).Normalize( ); // make sure it's in front of the player if (DotProduct (dir, gpGlobals->v_forward ) < 0) continue; dot = fabs( DotProduct (dir, gpGlobals->v_right ) ) + fabs( DotProduct (dir, gpGlobals->v_up ) ) * 0.5; // tweek for distance dot *= 1.0 + 0.2 * ((center - vecSrc).Length() / flDist); if (dot > bestdot) continue; // to far to turn UTIL_TraceLine( vecSrc, center, dont_ignore_monsters, edict(), &tr ); if (tr.flFraction != 1.0 && tr.pHit != pEdict) { // ALERT( at_console, "hit %s, can't see %s\n", STRING( tr.pHit->v.classname ), STRING( pEdict->v.classname ) ); continue; } // don't shoot at friends if (IRelationship( pEntity ) < 0) { if ( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch()) // ALERT( at_console, "friend\n"); continue; } // can shoot at this one bestdot = dot; bestent = pEdict; bestdir = dir; } if (bestent) { bestdir = UTIL_VecToAngles (bestdir); bestdir.x = -bestdir.x; bestdir = bestdir - pev->v_angle - pev->punchangle; if (bestent->v.takedamage == DAMAGE_AIM) m_fOnTarget = TRUE; return bestdir; } return Vector( 0, 0, 0 ); } void CBasePlayer :: ResetAutoaim( ) { if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0) { m_vecAutoAim = Vector( 0, 0, 0 ); SET_CROSSHAIRANGLE( edict(), 0, 0 ); } m_fOnTarget = FALSE; } /* ============= SetCustomDecalFrames UNDONE: Determine real frame limit, 8 is a placeholder. Note: -1 means no custom frames present. ============= */ void CBasePlayer :: SetCustomDecalFrames( int nFrames ) { if (nFrames > 0 && nFrames < 8) m_nCustomSprayFrames = nFrames; else m_nCustomSprayFrames = -1; } /* ============= GetCustomDecalFrames Returns the # of custom frames this player's custom clan logo contains. ============= */ int CBasePlayer :: GetCustomDecalFrames( void ) { return m_nCustomSprayFrames; } //========================================================= // DropPlayerItem - drop the named item, or if no name, // the active item. //========================================================= void CBasePlayer::DropPlayerItem ( char *pszItemName ) { /* if ( !g_pGameRules->IsMultiplayer() || (weaponstay.value > 0) ) { // no dropping in single player. return; } */ if ( !Q_strlen( pszItemName ) ) { // if this string has no length, the client didn't type a name! // assume player wants to drop the active item. // make the string null to make future operations in this function easier pszItemName = NULL; } CBasePlayerItem *pWeapon; int i; for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { pWeapon = m_rgpPlayerItems[ i ]; while ( pWeapon ) { if ( pszItemName ) { // try to match by name. if ( FStrEq( pszItemName, STRING( pWeapon->pev->netname ) ) ) { // match! break; } } else { // trying to drop active item if ( pWeapon == m_pActiveItem ) { // active item! break; } } pWeapon = pWeapon->m_pNext; } // if we land here with a valid pWeapon pointer, that's because we found the // item we want to drop and hit a BREAK; pWeapon is the item. if ( pWeapon && pWeapon->AllowToDrop( )) { if( pszItemName != NULL ) g_pGameRules->GetNextBestWeapon( this, pWeapon ); Vector dropAngle = pev->angles; dropAngle.x = dropAngle.z = 0; UTIL_MakeVectors ( dropAngle ); pev->weapons &= ~(1<m_iId);// take item off hud CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin + gpGlobals->v_forward * 10, pev->angles, edict() ); SET_MODEL( ENT(pWeaponBox->pev), STRING( pWeapon->iWorldModel() )); pWeaponBox->pev->angles.x = 0; pWeaponBox->pev->angles.z = 0; if( pWeaponBox->GetSequenceCount() > 1 ) pWeaponBox->pev->sequence = 1; pWeaponBox->PackWeapon( pWeapon ); pWeaponBox->pev->velocity = gpGlobals->v_forward * 300 + gpGlobals->v_forward * 100; // drop half of the ammo for this weapon. int iAmmoIndex; iAmmoIndex = GetAmmoIndex ( pWeapon->pszAmmo1() ); // ??? if ( iAmmoIndex != -1 ) { // this weapon weapon uses ammo, so pack an appropriate amount. if ( pWeapon->iFlags() & ITEM_FLAG_EXHAUSTIBLE ) { // pack up all the ammo, this weapon is its own ammo type pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] ); m_rgAmmo[ iAmmoIndex ] = 0; } else { // pack half of the ammo pWeaponBox->PackAmmo( MAKE_STRING(pWeapon->pszAmmo1()), m_rgAmmo[ iAmmoIndex ] / 2 ); m_rgAmmo[ iAmmoIndex ] /= 2; } } return;// we're done, so stop searching with the FOR loop. } } } //========================================================= // HasPlayerItem Does the player already have this item? //========================================================= BOOL CBasePlayer::HasPlayerItem( CBasePlayerItem *pCheckItem ) { CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; while (pItem) { if (FStrEq( STRING( pItem->pev->netname ), STRING( pCheckItem->pev->netname ) )) { return TRUE; } pItem = pItem->m_pNext; } return FALSE; } //========================================================= // HasNamedPlayerItem Does the player already have this item? //========================================================= BOOL CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) { CBasePlayerItem *pItem; int i; for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { pItem = m_rgpPlayerItems[ i ]; while (pItem) { if ( !Q_stricmp( pszItemName, STRING( pItem->pev->netname ) ) ) { return TRUE; } pItem = pItem->m_pNext; } } return FALSE; } //========================================================= // //========================================================= BOOL CBasePlayer :: SwitchWeapon( CBasePlayerItem *pWeapon ) { if ( !pWeapon->CanDeploy() ) { return FALSE; } ResetAutoaim( ); if ( m_pActiveItem ) m_pActiveItem->Holster( ); QueueItem( pWeapon ); if ( m_pActiveItem ) m_pActiveItem->Deploy( ); return TRUE; } //========================================================= // Dead HEV suit prop // LRC- i.e. the dead blokes you see in Xen. //========================================================= class CDeadHEV : public CBaseMonster { public: void Spawn( void ); int Classify ( void ) { return CLASS_HUMAN_MILITARY; } void KeyValue( KeyValueData *pkvd ); float MaxYawSpeed( void ) { return 8.0f; } int m_iPose;// which sequence to display -- temporary, don't need to save static char *m_szPoses[4]; }; char *CDeadHEV::m_szPoses[] = { "deadback", "deadsitting", "deadstomach", "deadtable" }; void CDeadHEV::KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "pose")) { m_iPose = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else CBaseMonster::KeyValue( pkvd ); } LINK_ENTITY_TO_CLASS( monster_hevsuit_dead, CDeadHEV ); //========================================================= // ********** DeadHEV SPAWN ********** //========================================================= void CDeadHEV :: Spawn( void ) { PRECACHE_MODEL("models/player.mdl"); SET_MODEL(ENT(pev), "models/player.mdl"); pev->sequence = 0; pev->body = 1; m_bloodColor = BLOOD_COLOR_RED; pev->sequence = LookupSequence( m_szPoses[m_iPose] ); if (pev->sequence == -1) { ALERT ( at_debug, "Dead hevsuit with bad pose\n" ); pev->sequence = 0; pev->effects |= EF_BRIGHTFIELD; } // Corpses have less health pev->health = 8; MonsterInitDead(); } class CStripWeapons : public CPointEntity { public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void KeyValue( KeyValueData *pkvd ); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; private: int m_iRemoveGasMask; int m_iRemoveShield; }; LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons ); TYPEDESCRIPTION CStripWeapons::m_SaveData[] = { DEFINE_FIELD( CStripWeapons, m_iRemoveGasMask, FIELD_INTEGER ), DEFINE_FIELD( CStripWeapons, m_iRemoveShield, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CStripWeapons, CPointEntity ); void CStripWeapons :: KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "gasmask")) { m_iRemoveGasMask = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "shield")) { m_iRemoveShield = atoi(pkvd->szValue); pkvd->fHandled = TRUE; } else CBaseEntity::KeyValue( pkvd ); } void CStripWeapons :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { CBasePlayer *pPlayer = NULL; if ( pActivator && pActivator->IsPlayer() ) { pPlayer = (CBasePlayer *)pActivator; } else if ( !g_pGameRules->IsDeathmatch() ) { pPlayer = (CBasePlayer *)CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); } if ( pPlayer ) { // buz if (m_iRemoveGasMask) { if (pPlayer->m_iGasMaskOn) pPlayer->ToggleGasMask(); ClearBits( pPlayer->m_iHideHUD, ITEM_GASMASK ); } if (m_iRemoveShield) { if (pPlayer->m_iHeadShieldOn) pPlayer->ToggleHeadShield(); ClearBits( pPlayer->m_iHideHUD, ITEM_HEADSHIELD ); } pPlayer->RemoveAllItems( FBitSet( pev->spawnflags, 1 )); } } class CRevertSaved : public CPointEntity { public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void EXPORT MessageThink( void ); void EXPORT LoadThink( void ); void KeyValue( KeyValueData *pkvd ); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; inline float Duration( void ) { return pev->dmg_take; } inline float HoldTime( void ) { return pev->dmg_save; } inline float MessageTime( void ) { return m_messageTime; } inline float LoadTime( void ) { return m_loadTime; } inline void SetDuration( float duration ) { pev->dmg_take = duration; } inline void SetHoldTime( float hold ) { pev->dmg_save = hold; } inline void SetMessageTime( float time ) { m_messageTime = time; } inline void SetLoadTime( float time ) { m_loadTime = time; } private: float m_messageTime; float m_loadTime; }; LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved ); TYPEDESCRIPTION CRevertSaved::m_SaveData[] = { DEFINE_FIELD( CRevertSaved, m_messageTime, FIELD_FLOAT ), // These are not actual times, but durations, so save as floats DEFINE_FIELD( CRevertSaved, m_loadTime, FIELD_FLOAT ), }; IMPLEMENT_SAVERESTORE( CRevertSaved, CPointEntity ); void CRevertSaved :: KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "duration")) { SetDuration( atof(pkvd->szValue) ); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "holdtime")) { SetHoldTime( atof(pkvd->szValue) ); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "messagetime")) { SetMessageTime( atof(pkvd->szValue) ); pkvd->fHandled = TRUE; } else if (FStrEq(pkvd->szKeyName, "loadtime")) { SetLoadTime( atof(pkvd->szValue) ); pkvd->fHandled = TRUE; } else CPointEntity::KeyValue( pkvd ); } void CRevertSaved :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, FFADE_OUT ); SetNextThink( MessageTime() ); SetThink(&CRevertSaved :: MessageThink ); } void CRevertSaved :: MessageThink( void ) { UTIL_ShowMessageAll( STRING(pev->message) ); float nextThink = LoadTime() - MessageTime(); if ( nextThink > 0 ) { SetNextThink( nextThink ); SetThink(&CRevertSaved :: LoadThink ); } else LoadThink(); } void CRevertSaved :: LoadThink( void ) { if ( !gpGlobals->deathmatch ) { SERVER_COMMAND("reload\n"); } } //========================================================= // Trigger to disable a player //========================================================= #define SF_FREEZE_LOCUS 1 class CPlayerFreeze:public CBaseDelay { void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); void Think( void ); STATE GetState( void ) { return m_hActivator == NULL? STATE_OFF: STATE_ON; } }; void CPlayerFreeze::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if (!(pev->spawnflags & SF_FREEZE_LOCUS)) { pActivator = UTIL_FindEntityByClassname(NULL, "player"); } if (pActivator && pActivator->pev->flags & FL_CLIENT) { if (!ShouldToggle(useType, pActivator->pev->flags & FL_FROZEN)) return; if (pActivator->pev->flags & FL_FROZEN) { // unfreeze him ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(TRUE); m_hActivator = NULL; DontThink(); } else { // freeze him ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(FALSE); if (m_flDelay) { m_hActivator = pActivator; SetNextThink(m_flDelay); } } } } void CPlayerFreeze::Think ( void ) { Use(m_hActivator, this, USE_ON, 0); } LINK_ENTITY_TO_CLASS( player_freeze, CPlayerFreeze ); // underwater bubbles effect class CPlayerBubbles : public CPointEntity { void Spawn( void ); void Think( void ); virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } }; LINK_ENTITY_TO_CLASS( player_bubbles, CPlayerBubbles ); void CPlayerBubbles :: Spawn( void ) { // when spawned in-game if( GET_SERVER_STATE() == SERVER_ACTIVE ) { UTIL_SetOrigin( this, pev->origin ); SET_MODEL( edict(), "sprites/null.spr" ); pev->body = 1; pev->movetype = MOVETYPE_COMPOUND; pev->aiment = pev->owner; MESSAGE_BEGIN( MSG_BROADCAST, gmsgParticle ); WRITE_ENTITY( entindex() ); WRITE_STRING( "particles/p_underwater.aur" ); WRITE_BYTE( 0 ); // attachment MESSAGE_END(); } SetNextThink( 1 ); // time to auto remove } void CPlayerBubbles :: Think ( void ) { // kill the aurora too UTIL_Remove( this ); } //========================================================= // Multiplayer intermission spots. //========================================================= class CInfoIntermission:public CPointEntity { void Spawn( void ); void Think( void ); }; void CInfoIntermission::Spawn( void ) { UTIL_SetOrigin( this, pev->origin ); pev->solid = SOLID_NOT; pev->effects |= EF_NODRAW; pev->v_angle = g_vecZero; SetNextThink( 2 );// let targets spawn! } void CInfoIntermission::Think ( void ) { CBaseEntity *pTarget; // find my target pTarget = UTIL_FindEntityByTargetname( NULL, STRING(pev->target) ); if ( pTarget ) { pev->v_angle = UTIL_VecToAngles( (pTarget->pev->origin - pev->origin).Normalize() ); pev->v_angle.x = -pev->v_angle.x; } } LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission ); // buz: set jump height function void CBasePlayer::SetJumpHeight(int value) { m_iJumpHeight = value; char buf[16]; Q_snprintf(buf, sizeof(buf), "%d", m_iJumpHeight); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "jh", buf ); // ALERT(at_aiconsole, "SETTING JUMP: %s\n", buf); } //============================================================== // Hud sprite displayer //============================================================== #define SF_HUDSPR_ACTIVE 1 class CHudSprite:public CBaseEntity { void Spawn( void ); void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); STATE GetState( void ) { return pev->spawnflags & SF_HUDSPR_ACTIVE? STATE_ON:STATE_OFF; } void Think( void ); }; void CHudSprite::Spawn( void ) { if (FStringNull(pev->targetname)) { pev->spawnflags |= SF_HUDSPR_ACTIVE; } if (pev->spawnflags & SF_HUDSPR_ACTIVE) { SetNextThink(2); } } void CHudSprite::Think( void ) { Use(this, this, USE_ON, 0); } void CHudSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if ( !pActivator || !pActivator->IsPlayer() ) { pActivator = CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex( 1 )); } if (ShouldToggle(useType)) { if (pev->spawnflags & SF_HUDSPR_ACTIVE) pev->spawnflags &= ~SF_HUDSPR_ACTIVE; else pev->spawnflags |= SF_HUDSPR_ACTIVE; } // byte : TRUE = ENABLE icon, FALSE = DISABLE icon // string : the sprite name to display // byte : red // byte : green // byte : blue MESSAGE_BEGIN( MSG_ONE, gmsgStatusIcon, NULL, pActivator->pev ); WRITE_BYTE(pev->spawnflags & SF_HUDSPR_ACTIVE); WRITE_STRING(STRING(pev->model)); WRITE_BYTE(pev->rendercolor.x); WRITE_BYTE(pev->rendercolor.y); WRITE_BYTE(pev->rendercolor.z); MESSAGE_END(); } LINK_ENTITY_TO_CLASS( hud_sprite, CHudSprite ); // buz void CBasePlayer::ViewPunch( float p, float y, float r ) { // fuser2-4 is punch speed pev->fuser2 -= p * 20; pev->fuser3 += y * 20; pev->fuser4 += r * 20; }