From 988c6757ddbcb337d08293365c64f2da1fded705 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 3 Apr 2024 07:02:57 +0300 Subject: [PATCH] Rebase step 2: add changes automatically patched by diffing HLSDK 2.3 and Decay trees --- cl_dll/ammo.cpp | 2 +- cl_dll/cdll_int.cpp | 17 + cl_dll/ev_hldm.cpp | 281 +++++++ cl_dll/health.cpp | 3 +- cl_dll/hl/hl_baseentity.cpp | 1 + cl_dll/hl/hl_weapons.cpp | 2 + cl_dll/hud.cpp | 94 +++ cl_dll/hud.h | 71 ++ cl_dll/hud_msg.cpp | 5 + cl_dll/hud_redraw.cpp | 211 +++++ cl_dll/tf_defs.h | 3 +- cl_dll/tri.cpp | 5 +- cl_dll/vgui_SchemeManager.cpp | 4 +- cl_dll/vgui_TeamFortressViewport.cpp | 123 +++ cl_dll/vgui_TeamFortressViewport.h | 50 ++ cl_dll/view.cpp | 22 + dlls/barney.cpp | 9 +- dlls/buttons.cpp | 31 + dlls/cbase.h | 9 +- dlls/client.cpp | 52 +- dlls/combat.cpp | 3 + dlls/effects.cpp | 1081 ++++++++++++++++++++++++++ dlls/func_break.cpp | 1 + dlls/gamerules.cpp | 1 + dlls/gamerules.h | 14 + dlls/h_battery.cpp | 472 +++++++++++ dlls/healthkit.cpp | 506 ++++++++++++ dlls/hgrunt.cpp | 17 +- dlls/items.cpp | 9 + dlls/monstermaker.cpp | 52 +- dlls/player.cpp | 124 ++- dlls/player.h | 4 + dlls/scientist.cpp | 239 ++++++ dlls/subs.cpp | 1 + dlls/talkmonster.cpp | 2 + dlls/talkmonster.h | 2 +- dlls/teamplay_gamerules.cpp | 1 + dlls/triggers.cpp | 667 ++++++++++++++++ dlls/util.cpp | 87 +++ dlls/util.h | 1 + dlls/weapons.cpp | 12 + dlls/weapons.h | 50 ++ dlls/world.cpp | 43 + dlls/xen.cpp | 1 - pm_shared/pm_shared.c | 9 + 45 files changed, 4364 insertions(+), 30 deletions(-) diff --git a/cl_dll/ammo.cpp b/cl_dll/ammo.cpp index 0e3d0a07..500be69f 100644 --- a/cl_dll/ammo.cpp +++ b/cl_dll/ammo.cpp @@ -898,7 +898,7 @@ int CHudAmmo::Draw( float flTime ) x += AmmoWidth / 2; - UnpackRGB( r,g,b, RGB_YELLOWISH ); + UnpackRGB( r,g,b, gHUD.uColor ); // draw the | bar FillRGBA( x, y, iBarWidth, gHUD.m_iFontHeight, r, g, b, a ); diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 55516b84..dd0a0907 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -79,6 +79,15 @@ void DLLEXPORT HUD_VoiceStatus(int entindex, qboolean bTalking); void DLLEXPORT HUD_DirectorMessage( int iSize, void *pbuf ); void DLLEXPORT HUD_MobilityInterface( mobile_engfuncs_t *gpMobileEngfuncs ); } +/* +// SCREEN GLOW +extern bool InitScreenGlow(); +extern void RenderScreenGlow(); +*/ +// CAMERA EFFECT +extern bool InitCameraEffect(); +extern void RenderCameraEffect(); +extern void GrabCameraTexture(); /* ================================ @@ -302,6 +311,14 @@ redraw the HUD. int DLLEXPORT HUD_Redraw( float time, int intermission ) { + // check if we are in OpenGl mode + if (IEngineStudio.IsHardware() == 1) + { + //RenderScreenGlow(); // SCREEN GLOW + if (gHUD.m_iCamMode == CAM_ON) + RenderCameraEffect(); + } + gHUD.Redraw( time, intermission ); return 1; diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 2e6b4932..2488f9ab 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -1696,6 +1696,287 @@ void EV_SnarkFire( event_args_t *args ) // SQUEAK END //====================== +//====================== +// VORTI START +//====================== + +enum vorti_e { + SLAVE_IDLE1 = 0, + SLAVE_IDLE2, + SLAVE_ATTACK1HIT, + SLAVE_ATTACK1MISS, + SLAVE_ATTACK2MISS, + SLAVE_ATTACK2HIT, + SLAVE_ATTACK3MISS, + SLAVE_ATTACK3HIT, + + SLAVE_CHARGE, + SLAVE_CHARGE_LOOP, + SLAVE_ZAP, + SLAVE_RETURN +}; + +int g_iVortSwing; + +//Only predict the miss sounds, hit sounds are still played +//server side, so players don't get the wrong idea. +void EV_Vorti( event_args_t *args ) +{ + int idx; + vec3_t origin; + vec3_t angles; + vec3_t velocity; + + idx = args->entindex; + VectorCopy( args->origin, origin ); + + //Play Swing sound + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "zombie/claw_miss1.wav", 1, ATTN_NORM, 0, PITCH_NORM); + + if ( EV_IsLocal( idx ) ) + { + gEngfuncs.pEventAPI->EV_WeaponAnimation( SLAVE_ATTACK1MISS, 1 ); + + switch( (g_iVortSwing++) % 3 ) + { + case 0: + gEngfuncs.pEventAPI->EV_WeaponAnimation ( SLAVE_ATTACK1MISS, 1 ); break; + case 1: + gEngfuncs.pEventAPI->EV_WeaponAnimation ( SLAVE_ATTACK2MISS, 1 ); break; + case 2: + gEngfuncs.pEventAPI->EV_WeaponAnimation ( SLAVE_ATTACK3MISS, 1 ); break; + } + } +} + +/* + float r = 255.0f; + float g = 180.0f; + float b = 96.0f; + + R_BeamEntPoint: + int startEnt, + float * end, + int modelIndex, + float life, + float width, + float amplitude, + float brightness, + float speed, + int startFrame, + float framerate, + float r, float g, float b + + pVortiBeam = gEngfuncs.pEfxAPI->R_BeamEntPoint ( idx | 0x1000, tr.endpos, iBeamModelIndex, + 99999, ///life + 2.0, //width + 0.4, // ampl + 64 / 255, //bright + 55, //speed + 0, 0, + r, g, b ); +*/ + +//#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch + +void EV_SpinVorti( event_args_t *args ) +{ + int idx; + vec3_t origin; + vec3_t angles; + vec3_t velocity; + int iSoundState = 0; + + int pitch; + + idx = args->entindex; + VectorCopy( args->origin, origin ); + VectorCopy( args->angles, angles ); + VectorCopy( args->velocity, velocity ); + + pitch = args->iparam1; + + //iSoundState = args->bparam1 ? SND_CHANGE_PITCH : 0; + + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "debris/zap1.wav", 1.0, ATTN_NORM, iSoundState, pitch ); +} + +/* +============================== +EV_StopPreviousGauss + +============================== +*/ +void EV_StopPreviousVorti( int idx ) +{ + // Make sure we don't have a gauss spin event in the queue for this guy + gEngfuncs.pEventAPI->EV_KillEvents( idx, "events/vortispin.sc" ); + gEngfuncs.pEventAPI->EV_StopSound( idx, CHAN_WEAPON, "debris/zap1.wav" ); +} + +void EV_FireVorti( event_args_t *args ) +{ + int idx; + vec3_t origin; + vec3_t angles; + vec3_t velocity; + float flDamage = args->fparam1; + int primaryfire = args->bparam1; + + int m_fPrimaryFire = args->bparam1; + int m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME; + vec3_t vecSrc; + vec3_t vecDest; + edict_t *pentIgnore; + pmtrace_t tr, beam_tr; + float flMaxFrac = 1.0; + int nTotal = 0; + int fHasPunched = 0; + int fFirstBeam = 1; + int nMaxHits = 1; + physent_t *pEntity; + int m_iBeam, m_iGlow, m_iBalls; + vec3_t up, right, forward; + + idx = args->entindex; + VectorCopy( args->origin, origin ); + VectorCopy( args->angles, angles ); + VectorCopy( args->velocity, velocity ); + + if ( args->bparam2 ) + { + EV_StopPreviousVorti( idx ); + return; + } + + //Con_Printf( "Firing vorti with %f\n", flDamage ); + EV_GetGunPosition( args, vecSrc, origin ); + + m_iBeam = gEngfuncs.pEventAPI->EV_FindModelIndex( "sprites/lgtning.spr" ); + m_iBalls = m_iGlow = gEngfuncs.pEventAPI->EV_FindModelIndex( "sprites/hotglow.spr" ); + + AngleVectors( angles, forward, right, up ); + + VectorMA( vecSrc, 8192, forward, vecDest ); + + if ( EV_IsLocal( idx ) ) + { + V_PunchAxis( 0, -2.0 ); + gEngfuncs.pEventAPI->EV_WeaponAnimation( SLAVE_ZAP, 2 ); + + if ( m_fPrimaryFire == false ) + g_flApplyVel = flDamage; + + } + + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/electro4.wav", 0.5 + flDamage * (1.0 / 400.0), ATTN_NORM, 0, 85 + gEngfuncs.pfnRandomLong( 0, 0x1f ) ); + + while (flDamage > 10 && nMaxHits > 0) + { + nMaxHits--; + + gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true ); + + // Store off the old count + gEngfuncs.pEventAPI->EV_PushPMStates(); + + // Now add in all of the players. + gEngfuncs.pEventAPI->EV_SetSolidPlayers ( idx - 1 ); + + gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); + gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecDest, PM_STUDIO_BOX, -1, &tr ); + + gEngfuncs.pEventAPI->EV_PopPMStates(); + + if ( tr.allsolid ) + break; + + if ( EV_IsLocal( idx ) ) + { + // Add muzzle flash to current weapon model + EV_MuzzleFlash(); + } + fFirstBeam = 0; + + gEngfuncs.pEfxAPI->R_BeamEntPoint( + idx | 0x1000, + tr.endpos, + m_iBeam, + 0.3, + 3.0, + 0.5, // noise + 1.0, + 55, + 0, + 0, + 0.5, 1, 0//180, 255, 96 + ); + + gEngfuncs.pEfxAPI->R_BeamEntPoint( + idx | 0x2000, + tr.endpos, + m_iBeam, + 0.3, + 3.0, + 0.5, // noise + 1.0, + 55, + 0, + 0, + 0.5, 1, 0//180, 255, 96 + ); + + pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent ); + if ( pEntity == NULL ) + break; + + if ( pEntity->solid == SOLID_BSP ) + { + float n; + + pentIgnore = NULL; + + n = -DotProduct( tr.plane.normal, forward ); + + // tunnel + EV_HLDM_DecalGunshot( &tr, BULLET_MONSTER_12MM ); + + //gEngfuncs.pEfxAPI->R_TempSprite( tr.endpos, vec3_origin, 1.0, m_iGlow, kRenderGlow, kRenderFxNoDissipation, flDamage / 255.0, 6.0, FTENT_FADEOUT ); + + //gEngfuncs.pEfxAPI->R_FunnelSprite( tr.endpos, m_iGlow, false ); + gEngfuncs.pEfxAPI->R_SparkShower( tr.endpos ); + + // limit it to one hole punch + if (fHasPunched) + { + break; + } + fHasPunched = 1; + + { + // slug doesn't punch through ever with primary + // fire, so leave a little glowy bit and make some balls + //gEngfuncs.pEfxAPI->R_TempSprite( tr.endpos, vec3_origin, 0.2, m_iGlow, kRenderGlow, kRenderFxNoDissipation, 200.0 / 255.0, 0.3, FTENT_FADEOUT ); + + vec3_t fwd; + VectorAdd( tr.endpos, tr.plane.normal, fwd ); + //gEngfuncs.pEfxAPI->R_Sprite_Trail( TE_SPRITETRAIL, tr.endpos, fwd, m_iBalls, 8, 0.6, gEngfuncs.pfnRandomFloat( 10, 20 ) / 100.0, 100, + // 255, 200 ); + } + + flDamage = 0; + } + else + { + VectorAdd( tr.endpos, forward, vecSrc ); + } + } +} + +//====================== +// VORTI END +//====================== + void EV_TrainPitchAdjust( event_args_t *args ) { int idx; diff --git a/cl_dll/health.cpp b/cl_dll/health.cpp index e14182b8..183686b0 100644 --- a/cl_dll/health.cpp +++ b/cl_dll/health.cpp @@ -88,8 +88,9 @@ int CHudHealth::VidInit( void ) { m_hSprite = 0; - m_HUD_dmg_bio = gHUD.GetSpriteIndex( "dmg_bio" ) + 1; + m_HUD_dmg_bio = gHUD.GetSpriteIndex( "dmg_bio" );// + 1; m_HUD_cross = gHUD.GetSpriteIndex( "cross" ); + m_HUD_alien = gHUD.GetSpriteIndex( "islave_health" ); giDmgHeight = gHUD.GetSpriteRect( m_HUD_dmg_bio ).right - gHUD.GetSpriteRect( m_HUD_dmg_bio ).left; giDmgWidth = gHUD.GetSpriteRect( m_HUD_dmg_bio ).bottom - gHUD.GetSpriteRect( m_HUD_dmg_bio ).top; diff --git a/cl_dll/hl/hl_baseentity.cpp b/cl_dll/hl/hl_baseentity.cpp index a47078e5..1c0836fa 100644 --- a/cl_dll/hl/hl_baseentity.cpp +++ b/cl_dll/hl/hl_baseentity.cpp @@ -330,6 +330,7 @@ void CBasePlayerItem::Drop( void ) { } void CBasePlayerItem::Kill( void ) { } void CBasePlayerItem::Holster( int skiplocal ) { } void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer ) { } +void CBasePlayerItem::KeyValue( KeyValueData *pkvd ) { } int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) { return 0; } int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) { return FALSE; } int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) { return 0; } diff --git a/cl_dll/hl/hl_weapons.cpp b/cl_dll/hl/hl_weapons.cpp index 4f119f02..9c0eb91c 100644 --- a/cl_dll/hl/hl_weapons.cpp +++ b/cl_dll/hl/hl_weapons.cpp @@ -55,6 +55,7 @@ vec3_t previousorigin; // HLDM Weapon placeholder entities. CGlock g_Glock; CCrowbar g_Crowbar; +CVortiHands g_VortiHands; CPython g_Python; CMP5 g_Mp5; CCrossbow g_Crossbow; @@ -725,6 +726,7 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm case WEAPON_SNARK: pWeapon = &g_Snark; break; + } // Store pointer to our destination entity_state_t so we can get our origin, etc. from it diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index 86f0f6cd..e0d30788 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -139,6 +139,33 @@ int __MsgFunc_GameMode( const char *pszName, int iSize, void *pbuf ) return gHUD.MsgFunc_GameMode( pszName, iSize, pbuf ); } +int __MsgFunc_DecayName(const char *pszName, int iSize, void *pbuf ) +{ + return gHUD.MsgFunc_DecayName( pszName, iSize, pbuf ); +} + +int __MsgFunc_LensFlare(const char *pszName, int iSize, void *pbuf) +{ + return gHUD.MsgFunc_LensFlare(pszName, iSize, pbuf ); +} + +int __MsgFunc_AimFrame(const char *pszName, int iSize, void *pbuf) +{ + return gHUD.MsgFunc_AimFrame(pszName, iSize, pbuf ); +} + +int __MsgFunc_ChangePlr(const char *pszName, int iSize, void *pbuf ) +{ + gHUD.MsgFunc_ChangePlayer(pszName, iSize, pbuf ); + return 1; +} + +int __MsgFunc_Camera(const char *pszName, int iSize, void *pbuf) +{ + gHUD.MsgFunc_Camera( pszName, iSize, pbuf ); + return 1; +} + // TFFree Command Menu void __CmdFunc_OpenCommandMenu( void ) { @@ -331,6 +358,8 @@ void CHud::Init( void ) HOOK_MESSAGE( ViewMode ); HOOK_MESSAGE( SetFOV ); HOOK_MESSAGE( Concuss ); + HOOK_MESSAGE( Camera ); + HOOK_MESSAGE( DecayName ); // TFFree CommandMenu HOOK_COMMAND( "+commandmenu", OpenCommandMenu ); @@ -364,8 +393,14 @@ void CHud::Init( void ) HOOK_MESSAGE( ResetFade ); #endif + HOOK_MESSAGE( LensFlare ); + HOOK_MESSAGE( AimFrame ); + HOOK_MESSAGE( ChangePlr ); + // VGUI Menus HOOK_MESSAGE( VGUIMenu ); + HOOK_MESSAGE( Notepad ); + HOOK_MESSAGE( SparePlayer ); CVAR_CREATE( "hud_classautokill", "1", FCVAR_ARCHIVE | FCVAR_USERINFO ); // controls whether or not to suicide immediately on TF class switch CVAR_CREATE( "hud_takesshots", "0", FCVAR_ARCHIVE ); // controls whether or not to automatically take screenshots at the end of a round @@ -373,6 +408,8 @@ void CHud::Init( void ) m_iLogo = 0; m_iFOV = 0; + m_iLensIndex = 0; + m_iFrameIndex = 0; CVAR_CREATE( "zoom_sensitivity_ratio", "1.2", FCVAR_ARCHIVE ); CVAR_CREATE( "cl_autowepswitch", "1", FCVAR_ARCHIVE | FCVAR_USERINFO ); @@ -475,6 +512,9 @@ void CHud::VidInit( void ) m_scrinfo.iSize = sizeof(m_scrinfo); GetScreenInfo( &m_scrinfo ); + // Set HUD color + uColor = RGB_YELLOWISH; + // ---------- // Load Sprites // --------- @@ -590,6 +630,20 @@ void CHud::VidInit( void ) m_iFontHeight = m_rgrcRects[m_HUD_number_0].bottom - m_rgrcRects[m_HUD_number_0].top; + //load sprites for lens flare effect and for selection frame + for (int i=0;i<9;i++) + { + m_hsprLens[i] = NULL; + + char szName[32]; + sprintf(szName, "sprites/lens_%d.spr",i); + if (m_hsprLens[i] == 0) + m_hsprLens[i] = LoadSprite(szName); + } + m_hsprFrame = LoadSprite("sprites/bracket.spr"); + m_iFrameSize = SPR_Width(m_hsprFrame,0); + pFrameTexture = gEngfuncs.GetSpritePointer( m_hsprFrame ); + m_Ammo.VidInit(); m_Health.VidInit(); m_Spectator.VidInit(); @@ -626,6 +680,46 @@ int CHud::MsgFunc_Logo( const char *pszName, int iSize, void *pbuf ) return 1; } +int CHud::MsgFunc_LensFlare(const char *pszName, int iSize, void *pbuf) +{ + BEGIN_READ( pbuf, iSize ); + + m_iLensIndex = READ_BYTE(); + + return 1; +} + +int CHud::MsgFunc_AimFrame(const char *pszName, int iSize, void *pbuf) +{ + BEGIN_READ( pbuf, iSize ); + + m_iFrameIndex = READ_BYTE(); + m_iFrameKind = READ_BYTE(); + + m_vAimFrameCoords.x = READ_COORD(); + m_vAimFrameCoords.y = READ_COORD(); + m_vAimFrameCoords.z = READ_COORD(); + + m_vAimFrameMaxs.x = READ_COORD(); + m_vAimFrameMaxs.y = READ_COORD(); + m_vAimFrameMaxs.z = READ_COORD(); + + return 1; +} + +int CHud::MsgFunc_ChangePlayer(const char *pszName, int iSize, void *pbuf ) +{ + BEGIN_READ( pbuf, iSize ); + int m_iDecayId = READ_BYTE(); + + if (m_iDecayId == 1) + uColor = RGB_SILVERISH; + else + uColor = RGB_YELLOWISH; + + return 1; +} + float g_lastFOV = 0.0; /* diff --git a/cl_dll/hud.h b/cl_dll/hud.h index 69c6646f..e210faa5 100644 --- a/cl_dll/hud.h +++ b/cl_dll/hud.h @@ -25,6 +25,7 @@ #define RGB_YELLOWISH 0x00FFA000 //255,160,0 #define RGB_REDISH 0x00FF1010 //255,160,0 #define RGB_GREENISH 0x0000A000 //0,160,0 +#define RGB_SILVERISH 0x00D4D4D4 // #include "wrect.h" #include "cl_dll.h" @@ -58,6 +59,7 @@ typedef struct cvar_s cvar_t; #define HUD_ACTIVE 1 #define HUD_INTERMISSION 2 +#define HUD_ALIEN 4 #define MAX_PLAYER_NAME_LENGTH 32 @@ -421,6 +423,54 @@ private: int m_iWidth; // width of the battery innards }; +// +//----------------------------------------------------- +// + +class CHudModeIcon: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + void Reset( void ); + int MsgFunc_ChangeMode(const char *pszName, int iSize, void *pbuf ); + +private: + HSPRITE m_hSpriteStand; + HSPRITE m_hSpriteRun; + HSPRITE m_hSpriteCrouch; + HSPRITE m_hSpriteJump; + HSPRITE m_hActiveSprite; + + wrect_t *m_prcStand; + wrect_t *m_prcRun; + wrect_t *m_prcCrouch; + wrect_t *m_prcJump; + wrect_t *m_prcActiveRect; + + int m_fMode; +}; + +class CHudAlienCrosshair: public CHudBase +{ +public: + int Init( void ); + int VidInit( void ); + int Draw(float flTime); + void Reset( void ); + int MsgFunc_AlienState(const char *pszName, int iSize, void *pbuf ); + +private: + HSPRITE m_hCrosshair[4]; + wrect_t *m_prcCrosshair[4]; + + HSPRITE m_hActiveSprite; + wrect_t *m_prcActiveRect; + + int m_iState; +}; + // //----------------------------------------------------- // @@ -542,7 +592,16 @@ private: int m_iSpriteCountAllRes; float m_flMouseSensitivity; int m_iConcussionEffect; + HSPRITE m_hsprLens[9];//lens flare sprite data + HSPRITE m_hsprFrame; + int m_iLensIndex; + int m_iFrameIndex;//index of entity to draw frame around + int m_iFrameKind; //frame hint index in names array + int m_iFrameSize; + const model_s *pFrameTexture;//frame texture for tri_api + //vec3_t m_vAimFrameCoords; //for selection frame + //vec3_t m_vAimFrameMaxs; //for selection frame public: HSPRITE m_hsprCursor; float m_flTime; // the current client time @@ -557,6 +616,17 @@ public: int m_iRes; cvar_t *m_pCvarStealMouse; cvar_t *m_pCvarDraw; + Vector m_vecCamPos; // CAM + int m_iCamMode; // CAM + unsigned long uColor; + + // TEXTURES + unsigned int g_uiScreenTex; + unsigned int g_uiGlowTex; + unsigned int g_uiCameraTex; + + vec3_t m_vAimFrameCoords; //for selection frame + vec3_t m_vAimFrameMaxs; //for selection frame int m_iFontHeight; int DrawHudNumber( int x, int y, int iFlags, int iNumber, int r, int g, int b ); @@ -635,6 +705,7 @@ public: int m_iWeaponBits; int m_fPlayerDead; int m_iIntermission; + bool m_bAlienMode; // sprite indexes int m_HUD_number_0; diff --git a/cl_dll/hud_msg.cpp b/cl_dll/hud_msg.cpp index 3c3b6f52..f4bca3fd 100644 --- a/cl_dll/hud_msg.cpp +++ b/cl_dll/hud_msg.cpp @@ -12,6 +12,11 @@ * without written permission from Valve LLC. * ****/ +/*** +* +* (C) 2008 Vyacheslav Dzhura +* +****/ // // hud_msg.cpp // diff --git a/cl_dll/hud_redraw.cpp b/cl_dll/hud_redraw.cpp index 59366208..f47821bf 100644 --- a/cl_dll/hud_redraw.cpp +++ b/cl_dll/hud_redraw.cpp @@ -94,6 +94,80 @@ void CHud::Think( void ) } } +void ReturnFrameHint(char* szHint,int Id) +{ + switch(Id){ + case 0: + sprintf(szHint,"Button"); + break; + case 1: + sprintf(szHint,"Object"); + break; + case 2: + sprintf(szHint,"Item"); + break; + case 3: + sprintf(szHint,"File"); + break; + case 4: + sprintf(szHint,"Documents"); + break; + case 5: + sprintf(szHint,"Book"); + break; + case 6: + sprintf(szHint,"Monitor"); + break; + case 7: + sprintf(szHint,"Computer"); + break; + case 8: + sprintf(szHint,"Compact Disc"); + break; + case 9: + sprintf(szHint,"Extinguisher"); + break; + case 10: + sprintf(szHint,"Security Card"); + break; + case 11: + sprintf(szHint,"Key"); + break; + case 12: + sprintf(szHint,"Syringe"); + break; + case 13: + sprintf(szHint,"Cleansuit"); + break; + case 14: + sprintf(szHint,"Wrench"); + break; + case 15: + sprintf(szHint,"Retinal Scanner"); + break; + case 16: + sprintf(szHint,"Health Charger"); + break; + case 17: + sprintf(szHint,"HEV Charger"); + break; + default: + sprintf(szHint,"Unknown"); + } +} + +void DrawFrameCorner(int x,int y,int u1,int u2,int u3,int u4,int v1,int v2,int v3,int v4) +{ + gEngfuncs.pTriAPI->TexCoord2f( u1, v1 ); + gEngfuncs.pTriAPI->Vertex3f( x, y, 0.0 ); + gEngfuncs.pTriAPI->TexCoord2f( u2, v2 ); + gEngfuncs.pTriAPI->Vertex3f( x, y+g_iFrameSize, 0.0 ); + gEngfuncs.pTriAPI->TexCoord2f( u3, v3 ); + gEngfuncs.pTriAPI->Vertex3f( x+g_iFrameSize, y+g_iFrameSize, 0.0 ); + gEngfuncs.pTriAPI->TexCoord2f( u4, v4 ); + gEngfuncs.pTriAPI->Vertex3f( x+g_iFrameSize, y, 0.0 ); +} + // Redraw // step through the local data, placing the appropriate graphics & text as appropriate // returns 1 if they've changed, 0 otherwise @@ -170,6 +244,12 @@ int CHud::Redraw( float flTime, int intermission ) if( pList->p->m_iFlags & HUD_INTERMISSION ) pList->p->Draw( flTime ); } + } else + { // alien mode!!! + if ( pList->p->m_iFlags & HUD_ALIEN ) + if ( (pList->p->m_iFlags & HUD_ACTIVE) && !(m_iHideHUDDisplay & HIDEHUD_ALL) ) + pList->p->Draw( flTime ); + } pList = pList->pNext; } @@ -196,6 +276,137 @@ int CHud::Redraw( float flTime, int intermission ) SPR_DrawAdditive( i, x, y, NULL ); } + // draw selection frame around entity + if (m_iFrameIndex !=0) + { + vec3_t v_center, v_maxs; + + gEngfuncs.pTriAPI->WorldToScreen(m_vAimFrameCoords,v_center); + gEngfuncs.pTriAPI->WorldToScreen(m_vAimFrameMaxs,v_maxs); + + v_center[0] = XPROJECT(v_center[0]); + v_center[1] = YPROJECT(v_center[1]); + v_center[2] = 0.0f; + v_maxs[0] = XPROJECT(v_maxs[0]); + v_maxs[1] = YPROJECT(v_maxs[1]); + v_maxs[2] = 0.0f; + + g_iFrameSize = m_iFrameSize; + + gEngfuncs.pTriAPI->SpriteTexture( (struct model_s *)pFrameTexture, 0 ); + gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd ); + gEngfuncs.pTriAPI->Begin( TRI_QUADS ); + + if (v_center[0]>v_maxs[0]) + { + //right bottom + DrawFrameCorner(v_center[0],v_center[1],1,1,0,0,1,0,0,1); + //left bottom + DrawFrameCorner(v_center[0]-(v_center[0]-v_maxs[0]),v_center[1],1,0,0,1,0,0,1,1); + //left up + DrawFrameCorner(v_maxs[0], v_maxs[1],0,0,1,1,0,1,1,0); + //left right + DrawFrameCorner(v_maxs[0]+(v_center[0]-v_maxs[0]),v_maxs[1],0,1,1,0,1,1,0,0); + } + else + { + //right bottom + DrawFrameCorner(v_center[0],v_center[1],1,0,0,1,0,0,1,1); + //left bottom + DrawFrameCorner(v_center[0]-(v_center[0]-v_maxs[0]),v_center[1],1,1,0,0,1,0,0,1); + //left up + DrawFrameCorner(v_maxs[0],v_maxs[1],0,1,1,0,1,1,0,0); + //left right + DrawFrameCorner(v_maxs[0]+(v_center[0]-v_maxs[0]),v_maxs[1],0,0,1,1,0,1,1,0); + } + gEngfuncs.pTriAPI->End(); + gEngfuncs.pTriAPI->RenderMode( kRenderNormal ); + + //to make that working, modify in decay.dll player.cpp blank message value of frame kind to -1 + //if (!m_iFrameKind==-1) + if (m_iFrameKind != -1) + { + char szMes[25]; + ReturnFrameHint(szMes,m_iFrameKind); + gHUD.DrawHudString( v_maxs[0]+5, v_maxs[1]+4, ScreenWidth, szMes, 255, 180, 0 ); + } + } + + /* + char szMes[255]; + if ( m_bAlienMode ) + sprintf( szMes, "Alien slave (vortigaunt) mode on" ); + else + sprintf( szMes, "Normal mode" ); + */ + + //ReturnFrameHint(szMes,m_iFrameKind); +// gHUD.DrawHudString( 10, 10, 512, szMes, 255, 180, 0 ); + + //draw sun + if (m_iLensIndex !=0) + { + vec3_t screen,ors; + float tN[9]; + tN[0]=1.0; + tN[1]=0.8; + tN[2]=0.7; + tN[3]=0.5; + tN[4]=0.4; + tN[5]=0.25; + tN[6]=0.1; + tN[7]=-0.1; + tN[8]=-0.2; + + cl_entity_t *ent = gEngfuncs.GetEntityByIndex(m_iLensIndex); + if (ent) + { + vec3_t forward, right, up; + AngleVectors ( ent->angles, forward, right, up );//get f/r/u vectors + + pmtrace_t tr; + gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); + gEngfuncs.pEventAPI->EV_PlayerTrace( (float *)&v_origin, v_origin + (forward+up)/2 * 8192, PM_GLASS_IGNORE, -1, &tr ); + //pmtrace_t tr = *(gEngfuncs.PM_TraceLine( (float *)&v_origin, v_origin + (forward+up)/2 * 8192, 0, 2, -1 )); // PM_GLASS_IGNORE + if (gEngfuncs.PM_PointContents( tr.endpos, NULL )!=CONTENTS_SKY) + { + return 0; + } + + if (!gEngfuncs.pTriAPI->WorldToScreen(tr.endpos,ors)) + { + int LWidth, LHeight; + LWidth = NULL; + LHeight = NULL; + + ors[0] = XPROJECT(ors[0]); + ors[1] = YPROJECT(ors[1]); + ors[2] = 0.0f; + + if (ors[0]<0 || ors[0]>ScreenWidth) return 0; + if (ors[1]<0 || ors[1]>ScreenHeight) return 0; + + for (int i=0;i<9;i++) + { + if (i!=0) + SPR_Set(m_hsprLens[i], 50, 50, 50); //was 250 + else + SPR_Set(m_hsprLens[i], 100, 100, 100); //make sun brighter then other lens + LWidth = SPR_Width(m_hsprLens[i],0); + LHeight = SPR_Height(m_hsprLens[i],0); + + screen=ors; + screen[0] = ScreenWidth/2+(screen[0]-ScreenWidth/2)*tN[i]; + screen[1] = ScreenHeight/2+(screen[1]-ScreenHeight/2)*tN[i]; + screen[0] = screen[0]-(LWidth/2); + screen[1] = screen[1]-(LHeight/2); + + SPR_DrawAdditive(0, screen[0],screen[1], NULL); + } + } + } + } + /* if( g_iVisibleMouse ) { diff --git a/cl_dll/tf_defs.h b/cl_dll/tf_defs.h index e445f2fc..e6a6e9a9 100644 --- a/cl_dll/tf_defs.h +++ b/cl_dll/tf_defs.h @@ -1106,8 +1106,9 @@ float already_chosen_map; #define MENU_CLASSHELP 6 #define MENU_CLASSHELP2 7 #define MENU_REPEATHELP 8 - #define MENU_SPECHELP 9 +#define MENU_NOTEPAD 10 +#define MENU_SPAREPLAYERWINDOW 11 #define MENU_SPY 12 diff --git a/cl_dll/tri.cpp b/cl_dll/tri.cpp index 0088daf3..a1da6b37 100644 --- a/cl_dll/tri.cpp +++ b/cl_dll/tri.cpp @@ -5,13 +5,14 @@ // $NoKeywords: $ //============================================================================= -// Triangle rendering, if any - +#include +//#include #include "hud.h" #include "cl_util.h" // Triangle rendering apis are in gEngfuncs.pTriAPI +//#include "com_model.h" #include "const.h" #include "entity_state.h" #include "cl_entity.h" diff --git a/cl_dll/vgui_SchemeManager.cpp b/cl_dll/vgui_SchemeManager.cpp index e50e5b73..13bfd215 100644 --- a/cl_dll/vgui_SchemeManager.cpp +++ b/cl_dll/vgui_SchemeManager.cpp @@ -269,7 +269,7 @@ CSchemeManager::CSchemeManager( int xRes, int yRes ) } if ( !pScheme->fontName[0] ) { - strcpy( pScheme->fontName, "Arial" ); + strcpy( pScheme->fontName, "Trebuchet MS" ); // Vyacheslav Dzhura: was Arial } } @@ -353,7 +353,7 @@ buildDefaultFont: { currentScheme = 0; strcpy( tmpSchemes[0].schemeName, "Default Scheme" ); - strcpy( tmpSchemes[0].fontName, "Arial" ); + strcpy( tmpSchemes[0].fontName, "Trebuchet MS" ); //Hoaxer: was Arial tmpSchemes[0].fontSize = 0; tmpSchemes[0].fgColor[0] = tmpSchemes[0].fgColor[1] = tmpSchemes[0].fgColor[2] = tmpSchemes[0].fgColor[3] = 255; tmpSchemes[0].armedFgColor[0] = tmpSchemes[0].armedFgColor[1] = tmpSchemes[0].armedFgColor[2] = tmpSchemes[0].armedFgColor[3] = 255; diff --git a/cl_dll/vgui_TeamFortressViewport.cpp b/cl_dll/vgui_TeamFortressViewport.cpp index b19ae2a1..1198e682 100644 --- a/cl_dll/vgui_TeamFortressViewport.cpp +++ b/cl_dll/vgui_TeamFortressViewport.cpp @@ -513,6 +513,8 @@ TeamFortressViewport::TeamFortressViewport( int x, int y, int wide, int tall ) : m_pSpectatorPanel = NULL; m_pCurrentMenu = NULL; m_pCurrentCommandMenu = NULL; + m_pNotepad = NULL; + m_pSparePlayerWindow = NULL; Initialize(); addInputSignal( new CViewPortInputHandler ); @@ -567,6 +569,8 @@ TeamFortressViewport::TeamFortressViewport( int x, int y, int wide, int tall ) : App::getInstance()->setScheme( pScheme ); // VGUI MENUS + CreateNotepad(); + CreateSparePlayerWindow(); CreateTeamMenu(); CreateClassMenu(); CreateSpectatorMenu(); @@ -618,6 +622,14 @@ void TeamFortressViewport::Initialize( void ) // Spectator menu doesn't need initializing m_pSpectatorPanel->setVisible( false ); } + if (m_pNotepad) + { + m_pNotepad->Initialize(); + } + if (m_pSparePlayerWindow) + { + m_pSparePlayerWindow->Initialize(); + } // Make sure all menus are hidden HideVGUIMenu(); @@ -1461,6 +1473,29 @@ CMenuPanel *TeamFortressViewport::CreateTextWindow( int iTextToShow ) //================================================================ // VGUI Menus +void TeamFortressViewport::DisplayNotepad( char* szText, int iTitle ) +{ + // Don't open window in demo playback + if ( gEngfuncs.pDemoAPI->IsPlayingback() ) + return; + + if (m_pCurrentMenu) + { + CMenuPanel *pMenu = m_pCurrentMenu; + while (pMenu != NULL) + { + if (pMenu->GetMenuID() == MENU_NOTEPAD ) + return; + pMenu = pMenu->GetNextMenu(); + } + } + + strcpy(m_pNotepad->szText, szText); + sprintf(m_pNotepad->szTitle,"#NotepadHeader%i",iTitle); + + ShowNotepad(); +} + void TeamFortressViewport::ShowVGUIMenu( int iMenu ) { CMenuPanel *pNewMenu = NULL; @@ -1513,6 +1548,10 @@ void TeamFortressViewport::ShowVGUIMenu( int iMenu ) case MENU_CLASS: pNewMenu = ShowClassMenu(); break; + //case MENU_NOTEPAD: + // m_pNotepad->Reset(); + // pNewMenu = m_pNotepad; + // break; default: break; @@ -1613,6 +1652,63 @@ void TeamFortressViewport::CreateTeamMenu() m_pTeamMenu->setVisible( false ); } +//====================================================================================== +// NOTEPAD +//====================================================================================== +// Bring up the Notepad +void TeamFortressViewport::ShowNotepad() +{ + if (m_pNotepad) + { + m_pNotepad->SetMenuID( MENU_NOTEPAD ); + m_pNotepad->SetActive( true ); + m_pNotepad->setParent( this ); + + m_pCurrentMenu = m_pNotepad; + m_pCurrentMenu->Reset(); + m_pCurrentMenu->Open(); + } + UpdateCursorState(); +} + +void TeamFortressViewport::ShowSparePlayerWindow() +{ + if (m_pSparePlayerWindow) + { + m_pSparePlayerWindow->SetMenuID( MENU_SPAREPLAYERWINDOW ); + m_pSparePlayerWindow->SetActive( true ); + m_pSparePlayerWindow->setParent( this ); + + m_pCurrentMenu = m_pSparePlayerWindow; + m_pCurrentMenu->Reset(); + m_pCurrentMenu->Open(); + } + UpdateCursorState(); +} + +bool TeamFortressViewport::IsNotepadVisible( void ) +{ + if (m_pNotepad) + return m_pNotepad->isVisible(); + + return false; +} + +void TeamFortressViewport::CreateNotepad() +{ + // Create the panel + m_pNotepad = new CNotepad(100, false, 0, 0, ScreenWidth, ScreenHeight); + m_pNotepad->setParent( this ); + m_pNotepad->setVisible( false ); +} + +void TeamFortressViewport::CreateSparePlayerWindow( void ) +{ + m_pSparePlayerWindow = new CSparePlayerWindow(100, false, 0, 0, ScreenWidth, ScreenHeight); + m_pSparePlayerWindow->setParent( this ); + m_pSparePlayerWindow->setVisible( false ); +} + //====================================================================================== // CLASS MENU //====================================================================================== @@ -1990,6 +2086,33 @@ int TeamFortressViewport::MsgFunc_VGUIMenu( const char *pszName, int iSize, void return 1; } +int TeamFortressViewport::MsgFunc_Notepad( const char *pszName, int iSize, void *pbuf ) +{ + BEGIN_READ( pbuf, iSize ); + + char* szText; + int iTitle; + + szText = READ_STRING(); + iTitle = READ_BYTE(); + + DisplayNotepad( szText, iTitle ); + + return 1; +} + +int TeamFortressViewport::MsgFunc_SparePlayer( const char *pszName, int iSize, void *pbuf ) +{ + BEGIN_READ( pbuf, iSize ); + + int iPlayerId; + + iPlayerId = READ_BYTE(); + ShowSparePlayerWindow(); + + return 1; +} + int TeamFortressViewport::MsgFunc_MOTD( const char *pszName, int iSize, void *pbuf ) { if( m_iGotAllMOTD ) diff --git a/cl_dll/vgui_TeamFortressViewport.h b/cl_dll/vgui_TeamFortressViewport.h index 023d716d..15c27357 100644 --- a/cl_dll/vgui_TeamFortressViewport.h +++ b/cl_dll/vgui_TeamFortressViewport.h @@ -575,6 +575,12 @@ public: void HideScoreBoard( void ); bool IsScoreBoardVisible( void ); + void ShowNotepad( void ); + bool IsNotepadVisible( void ); + void DisplayNotepad( char* szText, int iTitle ); + + void ShowSparePlayerWindow( void ); + bool AllowedToPrintText( void ); void ShowVGUIMenu( int iMenu ); @@ -634,6 +640,8 @@ public: CClassMenuPanel *m_pClassMenu; ScorePanel *m_pScoreBoard; SpectatorPanel *m_pSpectatorPanel; + CNotepad *m_pNotepad; + CSparePlayerWindow *m_pSparePlayerWindow; char m_szServerName[ MAX_SERVERNAME_LENGTH ]; }; @@ -1661,6 +1669,48 @@ public: } }; +class CSparePlayerWindow : public CMenuPanel +{ +public: + CTransparentPanel *m_pWindow; + TextPanel *m_pMemo; + Label *m_pTitle; + CImageLabel *m_pImage; + + char szText[256]; + char szTitle[256]; +public: + CSparePlayerWindow(int iTrans, int iRemoveMe, int x,int y,int wide,int tall); + + virtual bool SlotInput( int iSlot ); + virtual void Open( void ); + virtual void Update( void ); + + virtual void Initialize( void ); +}; + +class CNotepad : public CMenuPanel +{ +public: + ScrollPanel *m_pScrollPanel; + CTransparentPanel *m_pNotepadWindow; + TextPanel *m_pBriefing; + CommandButton *m_pCancelButton; + Label *m_pTitle; + CImageLabel *m_pImage; + + char szText[256]; + char szTitle[256]; +public: + CNotepad(int iTrans, int iRemoveMe, int x,int y,int wide,int tall); + + virtual bool SlotInput( int iSlot ); + virtual void Open( void ); + virtual void Update( void ); + + virtual void Initialize( void ); +}; + //========================================================= // Specific Menus to handle old HUD sections class CHealthPanel : public DragNDropPanel diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index 41389296..b5d3d4f3 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -63,6 +63,7 @@ void VectorAngles( const float *forward, float *angles ); #include "com_model.h" extern engine_studio_api_t IEngineStudio; +extern void GrabCameraTexture(); /* The view is allowed to move slightly from it's true position for bobbing, @@ -435,6 +436,15 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams ) // view is the weapon model (only visible from inside body) view = gEngfuncs.GetViewModel(); + //SKY START + //LRC - don't show weapon models when we're drawing the sky. + if (gHUD.m_iCamMode == CAM_ON) + { + savedviewmodel = view->model; + view->model = NULL; + } + //SKY END + // transform the view offset by the model's matrix to get the offset from // model origin for the view bob = V_CalcBob( pparams ); @@ -780,6 +790,18 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams ) lasttime = pparams->time; v_origin = pparams->vieworg; + + // SKY START + // LRC - override the view position if we're drawing a sky, rather than the player's view + if (gHUD.m_iCamMode == CAM_ON && pparams->nextView == 0) + { + pparams->vieworg[0] = gHUD.m_vecCamPos.x; + pparams->vieworg[1] = gHUD.m_vecCamPos.y; + pparams->vieworg[2] = gHUD.m_vecCamPos.z; + //pparams->viewangles[0] + pparams->nextView = 1; + } + // SKY END } void V_SmoothInterpolateAngles( float * startAngle, float * endAngle, float * finalAngle, float degreesPerSec ) diff --git a/dlls/barney.cpp b/dlls/barney.cpp index dd91de44..2795a544 100644 --- a/dlls/barney.cpp +++ b/dlls/barney.cpp @@ -36,9 +36,11 @@ #define BARNEY_AE_SHOOT ( 3 ) #define BARNEY_AE_HOLSTER ( 4 ) +#define GUN_GROUP 1 + #define BARNEY_BODY_GUNHOLSTERED 0 -#define BARNEY_BODY_GUNDRAWN 1 -#define BARNEY_BODY_GUNGONE 2 +#define BARNEY_BODY_GUNDRAWN 1 * 3 // * 3 for Decay +#define BARNEY_BODY_GUNGONE 2 * 3 // because of LODS class CBarney : public CTalkMonster { @@ -406,7 +408,8 @@ void CBarney::Spawn() m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello m_MonsterState = MONSTERSTATE_NONE; - pev->body = 0; // gun in holster + //pev->body = 0; // gun in holster + SetBodygroup( GUN_GROUP, 0 ); m_fGunDrawn = FALSE; m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; diff --git a/dlls/buttons.cpp b/dlls/buttons.cpp index 9c7d5d39..afeaa534 100644 --- a/dlls/buttons.cpp +++ b/dlls/buttons.cpp @@ -156,6 +156,7 @@ void CMultiSource::KeyValue( KeyValueData *pkvd ) } #define SF_MULTI_INIT 1 +#define SF_MULTI_NO_TOGGLE 2 void CMultiSource::Spawn() { @@ -964,6 +965,7 @@ public: vec3_t m_start; vec3_t m_end; int m_sounds; + string_t m_iszEndLockTarget; }; TYPEDESCRIPTION CMomentaryRotButton::m_SaveData[] = @@ -974,6 +976,7 @@ TYPEDESCRIPTION CMomentaryRotButton::m_SaveData[] = DEFINE_FIELD( CMomentaryRotButton, m_start, FIELD_VECTOR ), DEFINE_FIELD( CMomentaryRotButton, m_end, FIELD_VECTOR ), DEFINE_FIELD( CMomentaryRotButton, m_sounds, FIELD_INTEGER ), + DEFINE_FIELD( CMomentaryRotButton, m_iszEndLockTarget, FIELD_STRING ), }; IMPLEMENT_SAVERESTORE( CMomentaryRotButton, CBaseToggle ) @@ -1028,6 +1031,11 @@ void CMomentaryRotButton::KeyValue( KeyValueData *pkvd ) m_sounds = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } + else if (FStrEq(pkvd->szKeyName, "endlocktarget")) + { + m_iszEndLockTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else CBaseToggle::KeyValue( pkvd ); } @@ -1042,6 +1050,14 @@ void CMomentaryRotButton::PlaySound( void ) // current, not future position. void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { + // Vyacheslav Dzhura: in Decay there are momentary buttons with MASTER field + if (!UTIL_IsMasterTriggered(m_sMaster, pActivator)) + { + // play button locked sound + // PlayLockSounds(pev, &m_ls, TRUE, TRUE); + return; + } + pev->ideal_yaw = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles, m_start ) / m_flMoveDistance; UpdateAllButtons( pev->ideal_yaw, 1 ); @@ -1092,6 +1108,19 @@ void CMomentaryRotButton::UpdateSelf( float value ) { pev->avelocity = g_vecZero; pev->angles = m_end; + + // Vyacheslav Dzhura: off the button if we have locked target specified +/* if (!FStringNull( m_iszEndLockTarget )) + { + //CBaseEntity *m_pGoalEnt; + //m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_iszEndLockTarget ) ); + //if (m_pGoalEnt) + //{ + FireTargets(STRING( m_iszEndLockTarget ), this, this, USE_TOGGLE, 0); + SetThink( Off ); + //} + }*/ + return; } else if( m_direction < 0 && value <= 0.0f ) @@ -1145,6 +1174,8 @@ void CMomentaryRotButton::Off( void ) } else SetThink( NULL ); + +// if (m_flMoveDistance } void CMomentaryRotButton::Return( void ) diff --git a/dlls/cbase.h b/dlls/cbase.h index 1d2b2f7a..20c91b2e 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -93,6 +93,8 @@ typedef void(CBaseEntity::*BASEPTR)( void ); typedef void(CBaseEntity::*ENTITYFUNCPTR)( CBaseEntity *pOther ); typedef void(CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +extern BOOL g_startSuit; + // For CLASSIFY #define CLASS_NONE 0 #define CLASS_MACHINE 1 @@ -118,6 +120,7 @@ class CBasePlayerItem; class CSquadMonster; #define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. +#define SF_SPECIFICPLAYER 256 // Decay's flag which indicates that trigger should check player activator's index // // EHANDLE. Safe way to point to CBaseEntities who may die between frames @@ -414,7 +417,7 @@ void PlayLockSounds( entvars_t *pev, locksound_t *pls, int flocked, int fbutton // MultiSouce // -#define MAX_MULTI_TARGETS 16 // maximum number of targets a single multi_manager entity may be assigned. +#define MAX_MULTI_TARGETS 32 // was 16 - maximum number of targets a single multi_manager entity may be assigned. #define MS_MAX_TARGETS 32 class CMultiSource : public CPointEntity @@ -523,6 +526,8 @@ public: Vector m_vecFinalDest; Vector m_vecFinalAngle; + int m_iPlayerIndex; // Decay's player index + int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does virtual int Save( CSave &save ); @@ -797,5 +802,7 @@ public: void Spawn( void ); void Precache( void ); void KeyValue( KeyValueData *pkvd ); + + bool m_bSlaveCoop; }; #endif diff --git a/dlls/client.cpp b/dlls/client.cpp index 4e269eae..551fc111 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -53,6 +53,8 @@ extern cvar_t allow_spectators; extern cvar_t multibyte_only; extern int g_teamplay; +BOOL botadded = false; +extern bool bSlaveCoop; void LinkUserMessages( void ); @@ -82,6 +84,43 @@ called when a player connects to a server */ BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] ) { + int i; + int count = 0; + + // check if this is NOT a bot joining the server... + if (strcmp(pszAddress, "127.0.0.1") != 0) + { + // don't try to add bots for 30 seconds, give client time to get added + bot_check_time = gpGlobals->time + 30.0; + + for (i=0; i < 32; i++) + { + if (bot_respawn[i].is_used) // count the number of bots in use + count++; + } + + // if there are currently more than the minimum number of bots running + // then kick one of the bots off the server... + if ((min_bots != 0) && (count > min_bots)) + { + for (i=0; i < 32; i++) + { + if (bot_respawn[i].is_used) // is this slot used? + { + char cmd[40]; + + sprintf(cmd, "kick \"%s\"\n", bot_respawn[i].name); + + bot_respawn[i].state = BOT_IDLE; + + SERVER_COMMAND(cmd); // kick the bot using (kick "name") + + break; + } + } + } + } + return g_pGameRules->ClientConnected( pEntity, pszName, pszAddress, szRejectReason ); // a client connecting during an intermission can cause problems @@ -690,11 +729,14 @@ void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer ) } else { - UTIL_LogPrintf( "\"%s<%i><%s><%i>\" changed name to \"%s\"\n", + // TODO: crashes here when changing name during gameplay + UTIL_LogPrintf( "\"%s\" changed name to \"%s\"\n", + //UTIL_LogPrintf( "\"%s<%i><%s><%s>\" changed name to \"%s\"\n", STRING( pEntity->v.netname ), - GETPLAYERUSERID( pEntity ), - GETPLAYERAUTHID( pEntity ), - GETPLAYERUSERID( pEntity ), + // GETPLAYERUSERID( pEntity ), + // GETPLAYERAUTHID( pEntity ), + //GETPLAYERUSERID( pEntity ), + // g_engfuncs.pfnInfoKeyValue( infobuffer, "model" ), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) ); } } @@ -947,7 +989,7 @@ const char *GetGameDescription() if( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized return g_pGameRules->GetGameDescription(); else - return "Half-Life"; + return "Decay"; } /* diff --git a/dlls/combat.cpp b/dlls/combat.cpp index b0912c31..609ab858 100644 --- a/dlls/combat.cpp +++ b/dlls/combat.cpp @@ -579,6 +579,9 @@ void CBaseMonster::Killed( entvars_t *pevAttacker, int iGib ) // Make sure this condition is fired too (TakeDamage breaks out before this happens on death) SetConditions( bits_COND_LIGHT_DAMAGE ); + // DECAY STATS: tell game rules about killed monster + g_pGameRules->MonsterKilled( pevAttacker, &this->edict()->v ); + // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. CBaseEntity *pOwner = CBaseEntity::Instance( pev->owner ); if( pOwner ) diff --git a/dlls/effects.cpp b/dlls/effects.cpp index f9afd987..7019aaa5 100644 --- a/dlls/effects.cpp +++ b/dlls/effects.cpp @@ -2226,3 +2226,1084 @@ void CItemSoda::CanTouch( CBaseEntity *pOther ) SetThink( &CBaseEntity::SUB_Remove ); pev->nextthink = gpGlobals->time; } + +//========================================================= +// Entities added for Decay are below +//========================================================= + +//========================================================= +// object_model +//========================================================= + +/* +model file name + body index - object + +tech_crategibs.mdl + 7 - pc wax + 8 - screwdriver (yellow) + 9 - cd + 10 - chip1 + 11 - floppy (red) + 12 - floppy (grass green) + 13 - hard drive + 14 - screwdriver (blue) + 15 - ampermeter + 16 - wrench (low poly) + 17 - wrench (high poly) + 18 - erm...chip with wires I think ;-) + +med_crategibs.mdl + 7 - syringe + 8 - med pack + 9 - medic erm...stamp + 10 - same as 9, but twice bigger + 11 - same as 10, but twice bigger + +garbagegibs.mdl + 5 - glass + 6 - hamburger + 7 - apple + 8 - banana + 9 - news paper + 10 - envelope package + 12 - sprite can + +bookgibs.mdl + 1-4 books + 5 - opened book (small) + 6 - --//-- (big) + 15 - upside down book + +office_gibs.mdl + 1-7 papers + 8 - journal (blue) + 9 - --//-- (orange) + 10 - --//-- green, opened +*/ + +LINK_ENTITY_TO_CLASS( env_model, CObjModel ); + +void CObjModel::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "skin")) + { + m_iSkin = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "scale")) + { + m_iScale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "body")) + { + m_iBody = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "bodygroup")) // THIS IS THE ONLY PARAMETER, WHICH NEEDS TO BE READ + { + m_iBodyGroup = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +void CObjModel::ObjModelInit( const char *pModelName, int skin, int scale, int body, int bodygroup ) +{ + /*m_iSkin = skin; + m_iScale = scale; + m_iBody = body;*/ + m_iBodyGroup = bodygroup; + //m_iSequence = sequence; + + pev->model = MAKE_STRING( pModelName ); + Spawn(); +} + +CObjModel *CObjModel::ObjModelCreate( const char *pModelName, int skin, int scale, int body, int bodygroup ) +{ + CObjModel *pObjModel = GetClassPtr( (CObjModel *)NULL ); + pObjModel->Spawn(); + //pObjModel->ObjModelInit( pModelName, skin, scale, body, bodygroup ); + return pObjModel; +} + +float CObjModel::SetBoneController(int iController, float flValue ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return SetController( pmodel, pev, iController, flValue ); +} + +void CObjModel::Precache( void ) +{ + PRECACHE_MODEL( (char *)STRING(pev->model) ); +} + +void CObjModel::Spawn( void ) +{ + Precache(); + SET_MODEL(ENT(pev), STRING(pev->model)); + + //pev->skin = m_iSkin; + //pev->scale = m_iScale; + SetBodygroup(GET_MODEL_PTR( ENT(pev) ), pev, m_iBodyGroup, m_iBody);//-1 +} + +//========================================================= +// Dynamic light entity +//========================================================= + +#define SF_DLIGHT_ONLYONCE 1 +#define SF_DLIGHT_STARTON 2 + +class CEnvDLight : public CPointEntity +{ +public: + void Spawn( void ); + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Think( void ); + void DesiredAction( void ); + virtual void MakeLight( int iTime ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + bool GetState( void ) + { + if (pev->health == 0 && pev->nextthink > 0) // if we're thinking, and in switchable mode, then we're on + return true; //on + else + return false;//off + } + + Vector m_vecPos; +}; + +LINK_ENTITY_TO_CLASS( env_dlight, CEnvDLight ); + +TYPEDESCRIPTION CEnvDLight::m_SaveData[] = +{ + DEFINE_FIELD( CEnvDLight, m_vecPos, FIELD_VECTOR ), +}; + +IMPLEMENT_SAVERESTORE( CEnvDLight, CPointEntity ); + +void CEnvDLight::Spawn( void ) +{ + if (FStringNull(pev->targetname) || pev->spawnflags & SF_DLIGHT_STARTON) + { + DesiredAction(); + } +} + +void CEnvDLight::DesiredAction( void ) +{ + Use(this, this, USE_ON, 0); +} + +void CEnvDLight::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (!ShouldToggle(useType, GetState())) + { + return; + } + if (GetState() == true) + { + // turn off + //DontThink(); + return; + } + + int iTime; + m_vecPos = pev->origin; + + if (pev->health == 0) + { + iTime = 10; // 1 second + pev->nextthink = 1.0; + } + else if (pev->health > 25) + { + iTime = 250; + pev->takedamage = 25; + pev->nextthink = 25.0; + } + else + { + iTime = pev->health*10; + } + + MakeLight(iTime); + + if (pev->spawnflags & SF_DLIGHT_ONLYONCE) + { + SetThink( SUB_Remove ); + pev->nextthink = 0; + } +} + +void CEnvDLight::MakeLight( int iTime) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_DLIGHT ); + WRITE_COORD( m_vecPos.x ); // X + WRITE_COORD( m_vecPos.y ); // Y + WRITE_COORD( m_vecPos.z ); // Z + WRITE_BYTE( pev->renderamt ); // radius * 0.1 + WRITE_BYTE( pev->rendercolor.x ); // r + WRITE_BYTE( pev->rendercolor.y ); // g + WRITE_BYTE( pev->rendercolor.z ); // b + WRITE_BYTE( iTime ); // time * 10 + WRITE_BYTE( pev->frags ); // decay * 0.1 + MESSAGE_END( ); +} + +void CEnvDLight::Think( void ) +{ + int iTime; + if (pev->health == 0) + { + iTime = 10; + pev->nextthink = 1.0; + } + else + { + pev->takedamage += 25; + if (pev->health > pev->takedamage) + { + iTime = 25; + pev->nextthink = 25; + } + else + { + // finished, just do the leftover bit + iTime = (pev->health - pev->takedamage)*10; + pev->takedamage = 0; + } + } + + MakeLight( iTime ); +} + +//================================================================== +class CEnvELight : public CEnvDLight +{ +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void MakeLight(int iTime); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + EHANDLE m_hAttach; +}; + +LINK_ENTITY_TO_CLASS( env_elight, CEnvELight ); + +TYPEDESCRIPTION CEnvELight::m_SaveData[] = +{ + DEFINE_FIELD( CEnvELight, m_hAttach, FIELD_EHANDLE ), +}; + +IMPLEMENT_SAVERESTORE( CEnvELight, CEnvDLight ); + +void CEnvELight::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (pev->target) + { + m_hAttach = UTIL_FindEntityByTargetname( NULL, STRING(pev->target)); + if (m_hAttach == NULL) + { + ALERT(at_console, "env_elight \"%s\" can't find target %s\n", STRING(pev->targetname), STRING(pev->target)); + return; // error? + } + } + else + { + m_hAttach = this; + } + + CEnvDLight::Use(pActivator, pCaller, useType, value); +} + +void CEnvELight::MakeLight(int iTime) +{ + if (m_hAttach == NULL) + { + //DontThink(); + pev->takedamage = 0; + return; + } + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( m_hAttach->entindex( ) + 0x1000 * pev->impulse ); // entity, attachment + WRITE_COORD( m_vecPos.x ); // X + WRITE_COORD( m_vecPos.y ); // Y + WRITE_COORD( m_vecPos.z ); // Z + WRITE_COORD( pev->renderamt ); // radius * 0.1 + WRITE_BYTE( pev->rendercolor.x ); // r + WRITE_BYTE( pev->rendercolor.y ); // g + WRITE_BYTE( pev->rendercolor.z ); // b + WRITE_BYTE( iTime ); // time * 10 + WRITE_COORD( pev->frags ); // decay * 0.1 + MESSAGE_END( ); +} + +//========================================================= +// Lens flare spawn point entity +//========================================================= + +class CEnvLensFlare : public CBaseEntity +{ +public: + virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DIRECTIONAL_USE; } + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Precache( void ); + void Spawn( void ); +}; +LINK_ENTITY_TO_CLASS( env_lensflare, CEnvLensFlare); + +void CEnvLensFlare::Precache(void) +{ + PRECACHE_MODEL( "models/uplant1.mdl"); +} + +void CEnvLensFlare::Spawn(void) +{ + Precache(); + SET_MODEL(ENT(pev), "models/uplant1.mdl"); + + pev->scale = 1.0; + + pev->rendermode = kRenderTransAdd; + pev->renderamt = 1; //make model invisible +} + +void CEnvLensFlare::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // char szMes[256]; + // sprintf(szMes,"Server: ",pev->origin.x,pev->origin.y,pev->origin.z, entindex()); + // ALERT( at_console, szMes ); + + MESSAGE_BEGIN( MSG_ALL, gmsgLensFlare); + WRITE_BYTE( entindex() ); // send this entity number as part of the message (for callback) + MESSAGE_END(); +} + +//================================================================== +// Xen monsters' warp-in effect +//================================================================== + +LINK_ENTITY_TO_CLASS( effect_warpball, CEnvWarpBall ); + +void CEnvWarpBall :: KeyValue( KeyValueData *pkvd ) +{ +/* + if ( FStrEq(pkvd->szKeyName, "monstercount") ) + { + m_cNumMonsters = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "m_imaxlivechildren") ) + { + m_iMaxLiveChildren = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "monstertype") ) + { + m_iszMonsterClassname = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else */ + + if ( FStrEq(pkvd->szKeyName, "warp_target") ) + { + m_iszWarpTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +void CEnvWarpBall::Precache( void ) +{ + PRECACHE_MODEL( "sprites/lgtning.spr" ); + PRECACHE_MODEL( "sprites/Fexplo1.spr" ); + PRECACHE_MODEL( "sprites/XFlare1.spr" ); + PRECACHE_SOUND( "debris/beamstart2.wav" ); + PRECACHE_SOUND( "debris/beamstart7.wav" ); + //if (m_iszMonsterClassname != NULL) + // UTIL_PrecacheOther( STRING(m_iszMonsterClassname) ); +} + +void CEnvWarpBall::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBeam *pBeam[15]; + TraceResult tr; + Vector vecDest, Origin; + + CBaseEntity *m_pGoalEnt; + m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_iszWarpTarget ) ); + if (m_pGoalEnt) + { + Origin = m_pGoalEnt->pev->origin; + ALERT( at_console, "effect_warpball: playing at entity \"%s\" (%f %f %f)\n", STRING(m_iszWarpTarget), Origin.x, Origin.y, Origin.z ); + } else + Origin = pev->origin; + + EMIT_SOUND( edict(), CHAN_BODY, "debris/beamstart7.wav", 1, ATTN_NORM ); + + int i; + for (i=1;i<15;i++) + { + vecDest = 500 * (Vector(RANDOM_FLOAT(-2,2), RANDOM_FLOAT(-2,2), RANDOM_FLOAT(-2,2)).Normalize());//better use -5,5 + UTIL_TraceLine( Origin, Origin + vecDest, ignore_monsters, NULL, &tr); + if (tr.flFraction != 1.0) + { + // we hit something. + pBeam[i] = CBeam::BeamCreate("sprites/lgtning.spr",200); + pBeam[i]->pev->origin = Origin; + pBeam[i]->PointsInit( Origin, tr.vecEndPos ); + pBeam[i]->SetColor( 0, 255, 0 ); //Blue-Shift style + pBeam[i]->SetNoise( 65 ); + pBeam[i]->SetBrightness( 150 ); + pBeam[i]->SetWidth( 18 ); + pBeam[i]->SetScrollRate( 35 ); + pBeam[i]->SetThink( SUB_Remove ); + pBeam[i]->pev->nextthink = gpGlobals->time + 1; //was 0.1 + } + } + EMIT_SOUND( edict(), CHAN_BODY, "debris/beamstart2.wav", 1, ATTN_NORM ); + UTIL_ScreenShake( Origin, 4.0, 3.0, 1.0, 750 ); + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(Origin.x); // X + WRITE_COORD(Origin.y); // Y + WRITE_COORD(Origin.z); // Z + WRITE_BYTE( 8 ); // radius * 0.1 + WRITE_BYTE( 255 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 96 ); // b + WRITE_BYTE( 10 ); // time * 10 + WRITE_BYTE( 0 ); // decay * 0.1 + MESSAGE_END( ); + + CSprite *pSpr = CSprite::SpriteCreate( "sprites/Fexplo1.spr", Origin, TRUE ); + pSpr->AnimateAndDie( 10 ); + pSpr->SetTransparency(kRenderGlow, 77, 210, 130, 255, kRenderFxNoDissipation); + + pSpr = CSprite::SpriteCreate( "sprites/XFlare1.spr", Origin, TRUE ); + pSpr->AnimateAndDie( 10 ); + pSpr->SetTransparency(kRenderGlow, 184, 250, 214, 255, kRenderFxNoDissipation); + /* + if (m_iszMonsterClassname != NULL ) + { + CMonsterMaker *pMonsterMaker = CMonsterMaker::MonsterMakerCreate( STRING(m_iszMonsterClassname), 1, 1); // "monster_headcrab" + pMonsterMaker->pev->origin = pev->origin; + pMonsterMaker->pev->angles = pev->angles; + SetBits( pMonsterMaker->pev->spawnflags, SF_MONSTERMAKER_FIREONCE ); + pMonsterMaker->Use( this, this, USE_ON, 1); + } + */ + // SUB_UseTargets(this,USE_TOGGLE,1); + pev->nextthink = 2; +} + +void CEnvWarpBall::Think( void ) +{ + EMIT_SOUND( edict(), CHAN_ITEM, "debris/beamstart7.wav", 1, ATTN_NORM ); + SUB_UseTargets( this, USE_TOGGLE, 0); + + if ( pev->spawnflags & SF_AUTO_FIREONCE ) + UTIL_Remove( this ); +} + +CEnvWarpBall *CEnvWarpBall::WarpBallCreate() +{ + CEnvWarpBall *pWarpBall = GetClassPtr( (CEnvWarpBall *)NULL ); + pWarpBall->Spawn(); + return pWarpBall; +} + +//================================================================== +// Selection Frame bounding box object +//================================================================== +LINK_ENTITY_TO_CLASS( func_frame, CFuncFrame ); + +void CFuncFrame :: Spawn( void ) +{ + pev->angles = g_vecZero; + pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + pev->solid = SOLID_NOT; + SET_MODEL( ENT(pev), STRING(pev->model) ); + + // If it can't move/go away, it's really part of the world + pev->flags |= FL_WORLDBRUSH; + pev->effects |= EF_NODRAW; + + SET_MODEL( ENT(pev), STRING(pev->model) ); + pev->nextthink = 0.1; +} + +void CFuncFrame::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "kind")) + { + m_iKind = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +//================================================================== +//LRC- Shockwave effect, like when a Houndeye attacks. +//================================================================== +#define SF_SHOCKWAVE_CENTERED 1 +#define SF_SHOCKWAVE_REPEATABLE 2 + +class CEnvShockwave : public CBaseEntity +{ +public: + void Precache( void ); + void Spawn( void ) { Precache(); } + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + void KeyValue( KeyValueData *pkvd ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + int m_iTime; + int m_iRadius; + int m_iHeight; + int m_iScrollRate; + int m_iNoise; + int m_iFrameRate; + int m_iStartFrame; + int m_iSpriteTexture; +}; + +LINK_ENTITY_TO_CLASS( env_shockwave, CEnvShockwave ); + +TYPEDESCRIPTION CEnvShockwave::m_SaveData[] = +{ + DEFINE_FIELD( CEnvShockwave, m_iHeight, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iTime, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iRadius, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iScrollRate, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iNoise, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iFrameRate, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iStartFrame, FIELD_INTEGER ), + DEFINE_FIELD( CEnvShockwave, m_iSpriteTexture, FIELD_INTEGER ) +}; + +IMPLEMENT_SAVERESTORE( CEnvShockwave, CBaseEntity ); + +void CEnvShockwave::Precache( void ) +{ + m_iSpriteTexture = PRECACHE_MODEL( (char *)STRING(pev->netname) ); +} + +void CEnvShockwave::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "m_iTime")) + { + m_iTime = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iRadius")) + { + m_iRadius = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iHeight")) + { + m_iHeight = atoi(pkvd->szValue)/2; //LRC- the actual height is doubled when drawn + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iScrollRate")) + { + m_iScrollRate = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iNoise")) + { + m_iNoise = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iFrameRate")) + { + m_iFrameRate = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "m_iStartFrame")) + { + m_iStartFrame = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +void CEnvShockwave::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int posz; + if (pev->spawnflags & SF_SHOCKWAVE_CENTERED) + posz = pev->origin.z; + else + posz = pev->origin.z + m_iHeight; + // blast circle + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BEAMCYLINDER ); + WRITE_COORD( pev->origin.x );// coord coord coord (center position) + WRITE_COORD( pev->origin.y ); + WRITE_COORD( posz ); + WRITE_COORD( pev->origin.x );// coord coord coord (axis and radius) + WRITE_COORD( pev->origin.y ); + WRITE_COORD( posz + m_iRadius ); + WRITE_SHORT( m_iSpriteTexture ); // short (sprite index) + WRITE_BYTE( m_iStartFrame ); // byte (starting frame) + WRITE_BYTE( m_iFrameRate ); // byte (frame rate in 0.1's) + WRITE_BYTE( m_iTime ); // byte (life in 0.1's) + WRITE_BYTE( m_iHeight ); // byte (line width in 0.1's) + WRITE_BYTE( m_iNoise ); // byte (noise amplitude in 0.01's) + WRITE_BYTE( pev->rendercolor.x ); // byte,byte,byte (color) + WRITE_BYTE( pev->rendercolor.y ); + WRITE_BYTE( pev->rendercolor.z ); + WRITE_BYTE( pev->renderamt ); // byte (brightness) + WRITE_BYTE( m_iScrollRate ); // byte (scroll speed in 0.1's) + MESSAGE_END(); + + if (!(pev->spawnflags & SF_SHOCKWAVE_REPEATABLE)) + { + SetThink( SUB_Remove ); + pev->nextthink = 0; + } +} + +//========================================================= +// env_rtcamera - real-timer camera effect +//========================================================= +class CEnvRTCamera : public CBaseEntity +{ +public: + bool bActive; + //void Activate( void ); + void ThinkOn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +}; +/* +void CEnvRTCamera :: Activate ( void ) +{ + pev->effects |= EF_NODRAW; + pev->nextthink = gpGlobals->time + 1.0; +} +*/ +extern int gmsgCamera; + +void CEnvRTCamera :: ThinkOn () +{ + ALERT( at_console, "env_rtcamera called ThinkOn\n"); + MESSAGE_BEGIN(MSG_BROADCAST, gmsgCamera, NULL); + WRITE_BYTE(1); // mode + WRITE_COORD(pev->origin.x); // view position + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z); + MESSAGE_END(); + pev->nextthink = gpGlobals->time + 1.0; +} + +void CEnvRTCamera :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (useType == USE_TOGGLE ) + { + bActive = !bActive; + if (bActive) + { + SetThink( ThinkOn ); + pev->nextthink = gpGlobals->time + 1.0; + } else + { + MESSAGE_BEGIN(MSG_BROADCAST, gmsgCamera, NULL); + WRITE_BYTE(0); + WRITE_COORD(0); + WRITE_COORD(0); + WRITE_COORD(0); + MESSAGE_END(); + SetThink( NULL ); + } + } + ALERT( at_console, "env_rtcamera called Use\n"); +} + + +LINK_ENTITY_TO_CLASS( env_rtcamera, CEnvRTCamera ); + +//========================================================= +// env_mirroredlaser +//========================================================= + +TYPEDESCRIPTION CEnvMirroredLaser::m_SaveData[] = +{ + DEFINE_FIELD( CEnvMirroredLaser, m_iSearchDistance, FIELD_INTEGER ), + DEFINE_FIELD( CEnvMirroredLaser, m_iPrimarySearchDistance, FIELD_INTEGER ), + DEFINE_FIELD( CEnvMirroredLaser, iMaxStep, FIELD_INTEGER ), + DEFINE_FIELD( CEnvMirroredLaser, iBeamCount, FIELD_INTEGER ), + DEFINE_FIELD( CEnvMirroredLaser, iMirrorCount, FIELD_INTEGER ), + // all beams? + // all mirrors? +}; +IMPLEMENT_SAVERESTORE( CEnvMirroredLaser, CBaseEntity ); + +void CEnvMirroredLaser :: KeyValue( KeyValueData *pkvd ) +{ + if ( FStrEq(pkvd->szKeyName, "searchdistance") ) + { + m_iSearchDistance = atoi( pkvd->szValue ); + m_iPrimarySearchDistance = m_iSearchDistance; + pkvd->fHandled = TRUE; + } else + if ( FStrEq(pkvd->szKeyName, "maxstep") ) + { + iMaxStep = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + CBaseEntity::KeyValue( pkvd ); +} + +void CEnvMirroredLaser :: Spawn() +{ + iBeamCount = -1; + iMirrorCount = -1; + + if ((!m_iSearchDistance) || (m_iSearchDistance == 0)) + m_iSearchDistance = 1024; + + if ((!iMaxStep) || (iMaxStep)) + iMaxStep = 12; +} + +void CEnvMirroredLaser :: ReColorLasers() +{ + if (iBeamCount != -1) + { + // recolor beams + for (int i = 0; i <= iBeamCount; i++) + { + pBeam[i]->SetColor( pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z ); + pBeam[i]->SetBrightness( pev->renderamt ); + } + } +} + +void CEnvMirroredLaser :: RebuildPath() +{ + Vector origStartAngle, StartAngle, StartOrigin, NewOrigin/*, Collector*/; + CBaseEntity *LastHit = this; + + StartAngle = pev->angles; + StartOrigin = pev->origin; + m_iSearchDistance = m_iPrimarySearchDistance; + + if (iBeamCount != -1) + { + // remove previous beams + for (int i = 0; i <= iBeamCount; i++) + { + UTIL_Remove( pBeam[i] ); + //pBeam[i]->SetThink( SUB_Remove ); + //pBeam[i]->pev->nextthink = gpGlobals->time; + } + // send off signal to mirrors used previously + // DONE: iBeamCount doesn't mean there is the same amount of mirrors as beams!!! + for (int i = 0; i <= iMirrorCount; i++) + pMirrors[i]->CheckTargets(false); + } + iBeamCount = -1; + iMirrorCount = -1; + + //CBaseEntity *m_pGoalEnt; + //m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, "collector" ); + //Collector = m_pGoalEnt->pev->origin; + + // TODO: on rebuild go through all pMirrors and call USE_OFF + + for (int i = 0; i < iMaxStep; i++) + { + TraceResult tr; + tr.pHit = NULL; + tr.vecEndPos = Vector(0, 0, 0); + tr.vecPlaneNormal = Vector(0, 0, 0); + tr.flFraction = 0; + + origStartAngle = StartAngle; + UTIL_MakeVectors( StartAngle ); // converts angle into vector and puts into gpGlobals->v_forward + StartAngle = gpGlobals->v_forward; + //m_vecDir = StartAngle; + m_vecEnd = StartOrigin + StartAngle * m_iSearchDistance; // 1024 + + gpGlobals->trace_flags = FTRACE_SIMPLEBOX; + UTIL_TraceLine( StartOrigin, m_vecEnd, missile, ENT(LastHit->pev) /*ENT( pev )*/, &tr ); + float m_flBeamLength = tr.flFraction; + Vector vecTmpEnd = StartOrigin + StartAngle * m_iSearchDistance * m_flBeamLength; // 1024 + + iBeamCount++; + + //ALERT( at_console, "env_mirroredlaser: spawning a laser\n" ); + pBeam[iBeamCount] = CBeam::BeamCreate("sprites/laserbeam.spr", 32); //200 + pBeam[iBeamCount]->pev->origin = StartOrigin; + //pBeam[iBeamCount]->PointsInit( StartOrigin, vecTmpEnd ); + pBeam[iBeamCount]->SetColor( pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z ); //0, 255, 0 Blue-Shift style + pBeam[iBeamCount]->SetBrightness( pev->renderamt ); // 150 + pBeam[iBeamCount]->SetScrollRate( 35 ); // 35 + pBeam[iBeamCount]->SetNoise( 1 ); + + if (tr.pHit) + { + CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); + ALERT( at_console, "env_mirroredlaser hit: %s (%s)\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->targetname) ); + if ( FClassnameIs( pEntity->pev, "env_lasermirror" ) /*&& STRING(pEntity->pev->targetname) != ""*/) + { + NewOrigin = pEntity->pev->origin; + pBeam[iBeamCount]->PointsInit( StartOrigin, NewOrigin/*vecTmpEnd*/ ); + StartOrigin = NewOrigin; + StartAngle = pEntity->pev->angles; + LastHit = pEntity; + //pEntity->pev->origin = Collector; //Vector(0,0,0); + //UTIL_Remove( pEntity ); + tr.pHit = NULL; + + CEnvLaserMirror* pMirror = (CEnvLaserMirror*)CBaseEntity::Instance(LastHit->edict()); + pMirror->CheckTargets(true); + iMirrorCount++; + pMirrors[iMirrorCount]=pMirror; + + if ( pMirror->pev->spawnflags & SF_MIRROR_FINAL ) + return; + + m_iSearchDistance = pMirror->m_iSearchDistance; + + //pEntity->SetThink( SUB_Remove ); + //pEntity->pev->nextthink = gpGlobals->time + 0.01; + } else + { + pBeam[iBeamCount]->PointsInit( StartOrigin, vecTmpEnd ); + + // ******* NEW STUFF ************** + StartOrigin = vecTmpEnd; + StartAngle = origStartAngle; + LastHit = pEntity; + tr.pHit = NULL; + // ******* END OF NEW STUFF ******* + + //return; + } + }else + { + pBeam[iBeamCount]->PointsInit( StartOrigin, vecTmpEnd ); + return; + } + // FORMULA: Vect2 = Vect1 - 2 * WallN * (WallN DOT Vect1) + /*Vector Mirrored, WallN; + WallN = tr.vecPlaneNormal; + Mirrored = (StartOrigin+StartAngle) - 2 * WallN * (DotProduct(WallN, (StartOrigin+StartAngle))); + + StartAngle = Mirrored; + StartOrigin = vecTmpEnd; + ALERT( at_console, "normal: %f %f %f\n", tr.vecPlaneNormal.x, tr.vecPlaneNormal.y, tr.vecPlaneNormal.z );*/ + } +} + +void CEnvMirroredLaser :: ThinkOn () +{ + ALERT( at_console, "env_mirroredlaser called ThinkOn version 6\n"); + // do something + SetThink( NULL ); + pev->nextthink = gpGlobals->time + 1.0; +} + +void CEnvMirroredLaser :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if (useType == USE_TOGGLE ) + { + /*bActive = !bActive; + if (bActive) + { + RebuildPath(); + SetThink( ThinkOn ); + pev->nextthink = gpGlobals->time + 1.0; + } else + { + // do something - off + SetThink( NULL ); + }*/ + bActive = true; + useType = USE_ON; + } + if (useType == USE_ON) + { + RebuildPath(); + bActive = true; + } + if (useType == USE_OFF) + { + if (iBeamCount != -1) + { + // remove previous beams + for (int i = 0; i <= iBeamCount; i++) + UTIL_Remove( pBeam[i] ); + // send off signal to mirrors used previously + for (int i = 0; i <= iMirrorCount; i++) + pMirrors[i]->CheckTargets(false); + } + iBeamCount = -1; + iMirrorCount = -1; + bActive = false; + } + ALERT( at_console, "env_mirroredlaser called Use\n"); +} + +LINK_ENTITY_TO_CLASS( env_mirroredlaser, CEnvMirroredLaser ); + + +//========================================================= +// Mirrored laser mirror lens object +//========================================================= + +LINK_ENTITY_TO_CLASS( env_lasermirror, CEnvLaserMirror); + +TYPEDESCRIPTION CEnvLaserMirror::m_SaveData[] = +{ + DEFINE_FIELD( CEnvLaserMirror, m_iszTargetUnlocked, FIELD_STRING ), + DEFINE_FIELD( CEnvLaserMirror, m_iszTargetLocked, FIELD_STRING ), + DEFINE_FIELD( CEnvLaserMirror, bStateOn, FIELD_BOOLEAN ), + DEFINE_FIELD( CEnvLaserMirror, m_globalstate, FIELD_STRING ), + DEFINE_FIELD( CEnvLaserMirror, m_iUseStateMode, FIELD_INTEGER ), + DEFINE_FIELD( CEnvLaserMirror, m_iSearchDistance, FIELD_INTEGER ), + DEFINE_FIELD( CEnvLaserMirror, bUsedCount, FIELD_CHARACTER ), +}; +IMPLEMENT_SAVERESTORE( CEnvLaserMirror, CBaseEntity ); + +void CEnvLaserMirror::Precache(void) +{ + PRECACHE_MODEL( "models/uplant1.mdl"); +} + +void CEnvLaserMirror::Spawn(void) +{ + Precache(); + SET_MODEL(ENT(pev), "models/uplant1.mdl"); + + pev->scale = 1.0; + UTIL_SetOrigin ( pev, pev->origin ); + + pev->solid = SOLID_BBOX; + pev->movetype = MOVETYPE_FLY; //MOVETYPE_FLY; + pev->flags |= FL_MONSTER; + pev->takedamage = DAMAGE_NO; + pev->nextthink += 1.0; + + // UTIL_SetSize( pev, Vector(-32, -32, -8), Vector(8, 32, 32) ); + UTIL_SetSize( pev, Vector(-24, -24, -8), Vector(8, 24, 24) ); + + pev->rendermode = kRenderTransAdd; + pev->renderamt = 1; //make model invisible + bStateOn = true; + bUsedCount = 0; +} + +void CEnvLaserMirror::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "activated_target")) + { + m_iszTargetUnlocked = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "snoozed_target")) + { + m_iszTargetLocked = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "globalstate") ) + { + m_globalstate = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "searchdistance") ) + { + m_iSearchDistance = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "useglobalstatewhen") ) + { + m_iUseStateMode = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +void CEnvLaserMirror::CheckTargets(bool DoActivate) +{ + //typedef enum { GLOBAL_OFF = 0, GLOBAL_ON = 1, GLOBAL_DEAD = 2 } GLOBALESTATE; + + // TODO: + // add ability to turn off - e.g. turn on targetlocked after globalstate has been + // turned off on lasers resync and doesn't hit our final mirror + // Or turn off when global state is on but laser does not hit mirror anymore + + if ((m_iUseStateMode != -1) && (m_globalstate)) + { + if ( gGlobalState.EntityGetState( m_globalstate ) != m_iUseStateMode ) + { + if (DoActivate == false) + { + FireTargets( STRING(m_iszTargetLocked), this, this, USE_TOGGLE, 0 ); + return; + } + } else + { + if ((pev->spawnflags & SF_MIRROR_FIREONCE) && (bUsedCount >= 1)) + return; + + FireTargets( STRING(m_iszTargetUnlocked), this, this, USE_TOGGLE, 0 ); + bUsedCount++; + } + } else + if (DoActivate == true) + { + FireTargets( STRING(m_iszTargetUnlocked), this, this, USE_TOGGLE, 0 ); + } else + { + FireTargets( STRING(m_iszTargetLocked), this, this, USE_TOGGLE, 0 ); + } +} + +void CEnvLaserMirror::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + //change state + //state just just toggle solidness of object + + if (bStateOn) + { // if ON then turn OFF + pev->solid = SOLID_NOT; + UTIL_SetSize( pev, Vector(-1, -1, -1), Vector(1, 1, 1) ); + bStateOn = false; + } else + { + pev->solid = SOLID_BBOX; + // UTIL_SetSize( pev, Vector(-32, -32, -8), Vector(8, 32, 32) ); // -32 -32 -8 ___ 8 32 32 + UTIL_SetSize( pev, Vector(-24, -24, -8), Vector(8, 24, 24) ); + bStateOn = true; + } + pev->nextthink = gpGlobals->time + 0.01; + + // char szMes[256]; + // sprintf(szMes,"Server: ",pev->origin.x,pev->origin.y,pev->origin.z, entindex()); + // ALERT( at_console, szMes ); +} \ No newline at end of file diff --git a/dlls/func_break.cpp b/dlls/func_break.cpp index f1829d6c..4a70ef84 100644 --- a/dlls/func_break.cpp +++ b/dlls/func_break.cpp @@ -59,6 +59,7 @@ const char *CBreakable::pSpawnObjects[] = "weapon_satchel", // 19 "weapon_snark", // 20 "weapon_hornetgun", // 21 + "weapon_displacer", // 22 }; void CBreakable::KeyValue( KeyValueData* pkvd ) diff --git a/dlls/gamerules.cpp b/dlls/gamerules.cpp index 2499a67d..eedf56f6 100644 --- a/dlls/gamerules.cpp +++ b/dlls/gamerules.cpp @@ -32,6 +32,7 @@ DLL_GLOBAL CGameRules *g_pGameRules = NULL; extern DLL_GLOBAL BOOL g_fGameOver; extern int gmsgDeathMsg; // client dll messages extern int gmsgMOTD; +extern bool bDecay; int g_teamplay = 0; diff --git a/dlls/gamerules.h b/dlls/gamerules.h index adbbaa31..8c01f34e 100644 --- a/dlls/gamerules.h +++ b/dlls/gamerules.h @@ -252,6 +252,13 @@ public: // Teamplay stuff virtual const char *GetTeamID( CBaseEntity *pEntity ) {return "";}; virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ); + +// Decay stats + virtual void MonsterKilled( entvars_t *pKiller, entvars_t *pVictim ) {}; + virtual void PlayerDamaged( CBasePlayer *pPlayer, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) {}; + virtual void BulletsFired( entvars_t *pevAttacker, ULONG cShots, int iBulletType, int iShotId ) {}; + virtual void BulletHit( CBaseEntity *pEntity, entvars_t *pevAttacker, int iShotId ) {}; + virtual void savePlayerStats( int playerId, int finalGrade, int damageGrade, int killsGrade, int accuracyGrade ) {}; }; //========================================================= @@ -355,6 +362,13 @@ public: // Immediately end a multiplayer game virtual void EndMultiplayerGame( void ) { GoToIntermission(); } +// Decay stats + virtual void MonsterKilled( entvars_t *pKiller, entvars_t *pVictim ) {}; + virtual void PlayerDamaged( CBasePlayer *pPlayer, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) {}; + virtual void BulletsFired( entvars_t *pevAttacker, ULONG cShots, int iBulletType, int iShotId ) {}; + virtual void BulletHit( CBaseEntity *pEntity, entvars_t *pevAttacker, int iShotId ) {}; + virtual void savePlayerStats( int playerId, int finalGrade, int damageGrade, int killsGrade, int accuracyGrade ) {}; + protected: virtual void ChangeLevel( void ); virtual void GoToIntermission( void ); diff --git a/dlls/h_battery.cpp b/dlls/h_battery.cpp index 77559cc5..0df105e9 100644 --- a/dlls/h_battery.cpp +++ b/dlls/h_battery.cpp @@ -89,6 +89,7 @@ void CRecharge::Spawn() pev->solid = SOLID_BSP; pev->movetype = MOVETYPE_PUSH; + pev->skin = CHARGER_ACTIVE; UTIL_SetOrigin( pev, pev->origin ); // set size and link into world UTIL_SetSize( pev, pev->mins, pev->maxs ); @@ -192,3 +193,474 @@ void CRecharge::Off( void ) else SetThink( &CBaseEntity::SUB_DoNothing ); } + +// +// NEW MODEL WALL HEV CHARGER AS SEEN IN PLAYSTATION(R)2 VERSION OF HALF-LIFE +// + +//------------------------------------------------------------- +// Wall mounted HEV charger +//------------------------------------------------------------- + +#define seqCharge_Still 0 +#define seqCharge_Deploy 1 +#define seqCharge_RetractArm 2 +#define seqCharge_GiveShot 3 +#define seqCharge_RetractShot 4 +#define seqCharge_PrepShot 5 +#define seqCharge_ShotIdle 6 +#define seqCharge_Inactive 0 + +#define CHARGER_AWAKE_DISTANCE 64 + +class CMdlChargerGlass : public CActAnimating +{ +public: + void Spawn( ); + void Precache( void ); + static CMdlChargerGlass *CreateChargerGlass( edict_t *pOwner, const Vector &position ); +}; +LINK_ENTITY_TO_CLASS( item_rechargeglass, CMdlChargerGlass ); + +CMdlChargerGlass *CMdlChargerGlass::CreateChargerGlass( edict_t *pOwner, const Vector &position ) +{ + CMdlChargerGlass *pGlass = GetClassPtr( (CMdlChargerGlass *)NULL ); + pGlass->pev->classname = MAKE_STRING( "item_rechargeglass" ); + pGlass->pev->origin = position; + pGlass->pev->owner = pOwner; + pGlass->Spawn(); + + return pGlass; +} + +void CMdlChargerGlass::Precache() +{ + PRECACHE_MODEL("models/hev_glass.mdl" ); +} + +void CMdlChargerGlass::Spawn() +{ + Precache( ); + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), "models/hev_glass.mdl" ); + + pev->rendermode = kRenderTransTexture; + pev->renderamt = 192; +} + +class CMdlCharger : public CActAnimating +{ +public: + void Spawn( ); + void Precache( void ); + void EXPORT Off(void); + void EXPORT Recharge(void); + void EXPORT ChargerThink( void ); + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual int ObjectCaps( void ) { return (CActAnimating :: ObjectCaps() | FCAP_CONTINUOUS_USE) & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + void UpdateArm(); + void UpdateFluidTank(); + void TurnOff(); + + static TYPEDESCRIPTION m_SaveData[]; + + float m_flNextCharge, m_flStopCharge; + int m_iReactivate ; // DeathMatch Delay until reactvated + int m_iJuice; + int m_iOn; // 0 = off, 1 = startup, 2 = going + float m_flSoundTime; + float m_flDegree; + bool m_bStartUsing; + int m_iRotValue; + + CBaseEntity *pEntity; + CMdlChargerGlass *pGlass; + CBeam *m_pBeam; +}; + +TYPEDESCRIPTION CMdlCharger::m_SaveData[] = +{ + DEFINE_FIELD( CMdlCharger, m_flNextCharge, FIELD_TIME), + DEFINE_FIELD( CMdlCharger, m_flStopCharge, FIELD_TIME), + DEFINE_FIELD( CMdlCharger, m_iReactivate, FIELD_INTEGER), + DEFINE_FIELD( CMdlCharger, m_iJuice, FIELD_INTEGER), + DEFINE_FIELD( CMdlCharger, m_iOn, FIELD_INTEGER), + DEFINE_FIELD( CMdlCharger, m_iRotValue, FIELD_INTEGER ), + DEFINE_FIELD( CMdlCharger, m_flSoundTime, FIELD_TIME), + DEFINE_FIELD( CMdlCharger, m_flDegree, FIELD_TIME), + DEFINE_FIELD( CMdlCharger, m_bStartUsing, FIELD_BOOLEAN), + DEFINE_FIELD( CMdlCharger, pGlass, FIELD_CLASSPTR ), + DEFINE_FIELD( CMdlCharger, pEntity, FIELD_CLASSPTR ), +}; +//FIXED: caused bugs after load because of incorrectly specified derivied class +IMPLEMENT_SAVERESTORE( CMdlCharger, CActAnimating ); + +LINK_ENTITY_TO_CLASS(item_recharge, CMdlCharger); + +void CMdlCharger::KeyValue( KeyValueData *pkvd ) +{ + if ( FStrEq(pkvd->szKeyName, "style") || + FStrEq(pkvd->szKeyName, "height") || + FStrEq(pkvd->szKeyName, "value1") || + FStrEq(pkvd->szKeyName, "value2") || + FStrEq(pkvd->szKeyName, "value3")) + { + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "dmdelay")) + { + m_iReactivate = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CActAnimating::KeyValue( pkvd ); +} + +void CMdlCharger::Spawn() +{ + Precache( ); + + pev->solid = SOLID_BBOX; //BBOX; + pev->movetype = MOVETYPE_NONE; //NONE; //PUSH; + // UTIL_SetSize( pev, Vector(-7,-6,-27), Vector(7,6,27) ); //size(-20 -6 -27,20 6 27) + + UTIL_SetOrigin(pev, pev->origin); // set size and link into world + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), "models/hev.mdl" ); + m_iJuice = gSkillData.suitchargerCapacity; + pev->frame = 0; + InitBoneControllers(); + + m_pBeam = CBeam::BeamCreate("sprites/lgtning.spr", 200); + m_pBeam->EntsInit( entindex( ), entindex( ) ); + m_pBeam->SetStartAttachment( 3 ); // 2 + m_pBeam->SetEndAttachment( 4 ); // 3 + m_pBeam->SetColor( 0, 255, 0 ); + m_pBeam->SetNoise( 10 ); + m_pBeam->SetBrightness( 150 ); + m_pBeam->SetWidth( 5 ); + m_pBeam->SetScrollRate( 35 ); + + pGlass = CMdlChargerGlass::CreateChargerGlass(edict(), pev->origin); + pGlass->pev->angles = pev->angles; + + int YVal = pev->angles.y; + switch (YVal) + { + case 180: + m_iRotValue = 0; + break; + case 270: + m_iRotValue = -90; + break; + case 0: + m_iRotValue = -180; + break; + case 90: + m_iRotValue = -270; + break; + } + + // controller is -90...90 + + // -90...90 - ang 180 use 0 + // 0..180 - ang 270 use -90 + // 90..270 - ang 0 use -180 + // 180..360 - ang 90 use -270 + + SetSequence( seqCharge_Inactive ); + + SetThink(ChargerThink); + + if (g_pGameRules->IsCoOp() ) + { + CDecayRules *g_pDecayRules; + g_pDecayRules = (CDecayRules*)g_pGameRules; + + if ( g_pDecayRules->m_bAlienMode == true ) + { + SetThink( NULL ); + SetUse( NULL ); + } + } + + pev->nextthink = gpGlobals->time + 0.1; +} + +void CMdlCharger::Precache() +{ + PRECACHE_SOUND("items/suitcharge1.wav"); + PRECACHE_SOUND("items/suitchargeno1.wav"); + PRECACHE_SOUND("items/suitchargeok1.wav"); + PRECACHE_MODEL("models/hev.mdl"); + UTIL_PrecacheOther("item_rechargeglass"); +} + +void CMdlCharger::UpdateFluidTank( void ) +{ + // controllers 1 and 2 - 0..360 + m_flDegree += 7; + if (m_flDegree > 360 ) + m_flDegree = m_flDegree - 360; + + SetBoneController( 1, m_flDegree ); + SetBoneController( 2, -m_flDegree ); +} + +void CMdlCharger::ChargerThink( void ) +{ + //ALERT( at_console, "seq %d, on %d, juice %d\n", GetSequence(), m_iOn, m_iJuice ); + + StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.1; + + float flDist; + + if (m_bStartUsing == true) + if (m_flStopCharge >= gpGlobals->time) + { + SetSequence( seqCharge_RetractShot ); + //pFluidTank->SetSequence( seqCharge_ToRest ); + m_flStopCharge = 0; + } + + switch( GetSequence() ) + { + case seqCharge_Inactive: // inactive + { + if (pev->skin == CHARGER_EMPTY) // we're in "off" state + { + SetThink( NULL ); + return; + } + + // iterate on all entities in the vicinity. + CBaseEntity *pTmpEntity = NULL; + while ((pTmpEntity = UTIL_FindEntityInSphere( pTmpEntity, pev->origin, CHARGER_AWAKE_DISTANCE )) != NULL) + { + if (pTmpEntity->IsPlayer()) + { + pEntity = pTmpEntity; + if (( pev->origin - pEntity->pev->origin).Length() <= CHARGER_AWAKE_DISTANCE ) + { + SetSequence( seqCharge_Deploy ); + break; + } + } + } + } + break; + + case seqCharge_Deploy: // arm awakens + UpdateArm(); + if ( m_fSequenceFinished ) + SetSequence( seqCharge_PrepShot ); + break; + + case seqCharge_PrepShot: // we are waiting for player to use charger + flDist = ( pev->origin - pEntity->pev->origin).Length(); + //ALERT( at_console, "dist = %f\n", flDist ); + + if ( flDist > CHARGER_AWAKE_DISTANCE ) + { + SetSequence( seqCharge_RetractArm ); + break; + } + UpdateArm(); + // can be interruped from USE function which activates seqCharge_GiveShot + + break; + + case seqCharge_RetractArm: + if ( m_fSequenceFinished ) + { + SetSequence( seqCharge_Inactive ); + pEntity = NULL; + } else + UpdateArm(); // possible fix??? + break; + + case seqCharge_RetractShot: // stopped using + m_bStartUsing = false; + if ( m_fSequenceFinished ) + SetSequence( seqCharge_PrepShot ); + break; + + case seqCharge_GiveShot: // started using + m_bStartUsing = false; + if ( m_fSequenceFinished ) + { + m_bStartUsing = true; + SetSequence( seqCharge_ShotIdle ); + } + break; + + case seqCharge_ShotIdle: // in use + // give health here while USE is pressed + break; + + default: + break; + } + +} + +void CMdlCharger::UpdateArm() +{ + if ( FNullEnt( pEntity->pev ) ) + return; + + Vector vecTarget = (pev->origin - pEntity->pev->origin).Normalize( ); // TODO: CRASH HERE, because pEntity is NULL (0x00) + Vector angles = UTIL_VecToAngles (vecTarget); + //ALERT( at_console, "angles = %f, %f, %f - %f\n", angles.x, angles.y, angles.z, angles.y / 180 ); + if (angles.y > 180) + angles.y = angles.y - 360; + if (angles.y < -180) + angles.y = angles.y + 360; + + // 180 - 100 + // 90 - X (50) + // 50 = 90 * 100 / 180 + // percent = desired degree * 100 / 180 + // percent = desired degree / 180 + + // controller is -90...90 + // -90...90 - 180 use 0 + // 0..180 - 270 use -90 + // 90..270 - 0 use -180 + // 180..360(0) - 90 use + + // pos = (end - start) * scalar + start + // pos = 180 * scalar - 90 + SetBoneController(3, angles.y + m_iRotValue); //- 90); // +25 to the right +} + +void CMdlCharger::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // Make sure that we have a caller + if (!pActivator) + return; + + // if it's not a player, ignore + if ( !pActivator->IsPlayer() ) + return; + + if (pev->skin == CHARGER_EMPTY) // already off + return; + + // if there is no juice left, turn it off + if (m_iJuice <= 0) + { + TurnOff(); + return; + } + + // if the player doesn't have the suit, or there is no juice left, make the deny noise + if ((m_iJuice <= 0) || (!(pActivator->pev->weapons & (1<time) + { + m_flSoundTime = gpGlobals->time + 0.62; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeno1.wav", 0.85, ATTN_NORM ); + } + return; + } + + //pFluidTank->StartUse(); + pev->nextthink = gpGlobals->time + 0.25; // pev->ltime + SetThink(Off); + + // start the give shot sequence and do not use until it's finished + if (GetSequence() != seqCharge_GiveShot) + if ( m_bStartUsing == false ) + { + //if ( GetSequence() != seqCharge_PrepShot ) + // return; // can't start using until arm fully deployed from charger + + SetSequence( seqCharge_GiveShot ); + return; + } + + // Time to recharge yet? + if (m_flNextCharge >= gpGlobals->time) + return; + + // Play the on sound or the looping charging sound + if (!m_iOn) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeno1.wav", 0.85, ATTN_NORM ); + m_flSoundTime = 0.56 + gpGlobals->time; + } + if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->time)) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav", 0.85, ATTN_NORM ); + } + + // charge the player + if (pActivator->pev->armorvalue < 100) + { + m_iJuice--; + pActivator->pev->armorvalue += 1; + //pFluidTank->StartUse(); + + if (pActivator->pev->armorvalue > 100) + pActivator->pev->armorvalue = 100; + } + + UpdateFluidTank(); + + // govern the rate of charge + m_flNextCharge = gpGlobals->time + 0.1; +} + +void CMdlCharger::Recharge(void) +{ + //if (pev->skin == CHARGER_EMPTY) + // return; + + //EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM ); + m_iJuice = gSkillData.suitchargerCapacity; + pev->skin = CHARGER_ACTIVE; // set the active skin + SetThink( ChargerThink ); +} + +void CMdlCharger::Off(void) +{ + m_flStopCharge = gpGlobals->time + 0.25; + SetThink( ChargerThink ); + pev->nextthink = gpGlobals->time + 0.01; + + // Stop looping sound. + if (m_iOn > 1) + STOP_SOUND( ENT(pev), CHAN_STATIC, "items/suitcharge1.wav" ); + + m_iOn = 0; + + if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() ) > 0) ) + { + pev->nextthink = pev->ltime + m_iReactivate; + SetThink( Recharge ); + } + //else + // SetThink( SUB_DoNothing ); + + SetSequence( seqCharge_RetractShot ); +} + +void CMdlCharger::TurnOff(void) +{ + pev->skin = CHARGER_EMPTY; + UTIL_Remove( m_pBeam ); + m_pBeam = NULL; + SetUse( NULL ); + SetSequence( seqCharge_RetractArm ); +} \ No newline at end of file diff --git a/dlls/healthkit.cpp b/dlls/healthkit.cpp index 37e4624a..52449d89 100644 --- a/dlls/healthkit.cpp +++ b/dlls/healthkit.cpp @@ -252,3 +252,509 @@ void CWallHealth::Off( void ) else SetThink( &CBaseEntity::SUB_DoNothing ); } + +// +// NEW MODEL WALL HEALTH AS SEEN IN PLAYSTATION(R)2 VERSION OF HALF-LIFE +// + +//------------------------------------------------------------- +// Wall mounted health kit +//------------------------------------------------------------- + +#define seq_Still 0 +#define seq_Deploy 1 +#define seq_RetractArm 2 +#define seq_GiveShot 3 +#define seq_RetractShot 4 +#define seq_PrepShot 5 +#define seq_ShotIdle 6 +#define seq_Inactive 7 + +#define seq_Slosh 1 +#define seq_ToRest 2 + +#define CHARGER_AWAKE_DISTANCE 64 + +class CMdlWallHealthTank : public CActAnimating +{ +public: + void Spawn( ); + void Precache( void ); + void Update( int m_iJuice ); + void EXPORT TankThink( void ); + void StartUse( void ); + int m_iJuice; + static CMdlWallHealthTank *CreateFluidTank( edict_t *pOwner, const Vector &position ); +}; +LINK_ENTITY_TO_CLASS( item_healthchargertank, CMdlWallHealthTank ); + +CMdlWallHealthTank *CMdlWallHealthTank::CreateFluidTank( edict_t *pOwner, const Vector &position ) +{ + CMdlWallHealthTank *pHealthTank = GetClassPtr( (CMdlWallHealthTank *)NULL ); + pHealthTank->pev->classname = MAKE_STRING( "item_healthchargertank" ); + pHealthTank->pev->origin = position; + pHealthTank->pev->owner = pOwner; + pHealthTank->Spawn(); + + return pHealthTank; +} + +void CMdlWallHealthTank::Precache() +{ + PRECACHE_MODEL("models/health_charger_both.mdl" ); +} + +void CMdlWallHealthTank::Spawn() +{ + Precache( ); + + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), "models/health_charger_both.mdl" ); + + pev->rendermode = kRenderTransTexture; + pev->renderamt = 192; + SetBoneController( 0, 0 ); // -11 to 0 (empty to full) + + SetSequence( seq_Still ); + SetThink( TankThink ); + + if (g_pGameRules->IsCoOp() ) + { + CDecayRules *g_pDecayRules; + g_pDecayRules = (CDecayRules*)g_pGameRules; + + if ( g_pDecayRules->m_bAlienMode == true ) + { + SetThink( NULL ); + SetUse( NULL ); + } + } + + pev->nextthink = gpGlobals->time + 0.1; +} + +void CMdlWallHealthTank::Update(int m_iJuice) +{ + SetBoneController( 0, -(10 - (m_iJuice / gSkillData.healthchargerCapacity * 10)) ); // -11 to 0 (empty to full) +} + +void CMdlWallHealthTank::StartUse() +{ + if (GetSequence() != seq_Slosh ) + SetSequence( seq_Slosh ); +} + +void CMdlWallHealthTank::TankThink() +{ + StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.1; + + switch( GetSequence() ) + { + case seq_Still: // 0 - still + break; + case seq_Slosh: // 1 - slosh + if ( m_fSequenceFinished ) + SetSequence( seq_Slosh ); + break; + case seq_ToRest: // 2 - to rest + if ( m_fSequenceFinished ) + SetSequence( seq_Still ); + break; + default: + break; + } +} + +#define HEALTHSTATION_FULL 0 +#define HEALTHSTATION_EMPTY 1 + +class CMdlWallHealth : public CActAnimating +{ +public: + void Spawn( ); + void Precache( void ); + void EXPORT Off(void); + void EXPORT Recharge(void); + void EXPORT ChargerThink( void ); + void KeyValue( KeyValueData *pkvd ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual int ObjectCaps( void ) { return (CActAnimating :: ObjectCaps() | FCAP_CONTINUOUS_USE) & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + void UpdateArm(); + void UpdateFluidTank(); + void TurnOff(); + + static TYPEDESCRIPTION m_SaveData[]; + + float m_flNextCharge, m_flStopCharge; + int m_iReactivate ; // DeathMatch Delay until reactvated + int m_iJuice; + int m_iOn; // 0 = off, 1 = startup, 2 = going + float m_flSoundTime; + bool m_bStartUsing; + int m_iRotValue; + + CBaseEntity *pEntity; + CMdlWallHealthTank *pFluidTank; +}; + +TYPEDESCRIPTION CMdlWallHealth::m_SaveData[] = +{ + DEFINE_FIELD( CMdlWallHealth, m_flNextCharge, FIELD_TIME), + DEFINE_FIELD( CMdlWallHealth, m_flStopCharge, FIELD_TIME), + DEFINE_FIELD( CMdlWallHealth, m_iReactivate, FIELD_INTEGER), + DEFINE_FIELD( CMdlWallHealth, m_iJuice, FIELD_INTEGER), + DEFINE_FIELD( CMdlWallHealth, m_iOn, FIELD_INTEGER), + DEFINE_FIELD( CMdlWallHealth, m_iRotValue, FIELD_INTEGER ), + DEFINE_FIELD( CMdlWallHealth, m_flSoundTime, FIELD_TIME), + DEFINE_FIELD( CMdlWallHealth, m_bStartUsing, FIELD_BOOLEAN), + DEFINE_FIELD( CMdlWallHealth, pFluidTank, FIELD_CLASSPTR ), + DEFINE_FIELD( CMdlWallHealth, pEntity, FIELD_CLASSPTR ), +}; +//FIXED: caused bugs after load because of incorrectly specified derivied class +IMPLEMENT_SAVERESTORE( CMdlWallHealth, CActAnimating ); + +LINK_ENTITY_TO_CLASS(item_healthcharger, CMdlWallHealth); + +void CMdlWallHealth::KeyValue( KeyValueData *pkvd ) +{ + if ( FStrEq(pkvd->szKeyName, "style") || + FStrEq(pkvd->szKeyName, "height") || + FStrEq(pkvd->szKeyName, "value1") || + FStrEq(pkvd->szKeyName, "value2") || + FStrEq(pkvd->szKeyName, "value3")) + { + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "dmdelay")) + { + m_iReactivate = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CActAnimating::KeyValue( pkvd ); +} + +void CMdlWallHealth::Spawn() +{ + Precache( ); + + pev->solid = SOLID_BBOX; + pev->movetype = MOVETYPE_NONE; //PUSH; + // UTIL_SetSize( pev, Vector(-7,-6,-27), Vector(7,6,27) ); //size(-20 -6 -27,20 6 27) + + UTIL_SetOrigin(pev, pev->origin); // set size and link into world + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), "models/health_charger_body.mdl" ); + m_iJuice = gSkillData.healthchargerCapacity; + pev->frame = 0; + pev->skin = HEALTHSTATION_FULL; + InitBoneControllers(); + + pFluidTank = CMdlWallHealthTank::CreateFluidTank( edict(), pev->origin); + //pFluidTank->pev->origin = pev->origin; + pFluidTank->pev->angles = pev->angles; + + int YVal = pev->angles.y; + switch (YVal) + { + case 180: + m_iRotValue = 0; + break; + case 270: + m_iRotValue = -90; + break; + case 0: + m_iRotValue = -180; + break; + case 90: + m_iRotValue = -270; + break; + } + + // controller is -90...90 + + // -90...90 - ang 180 use 0 + // 0..180 - ang 270 use -90 + // 90..270 - ang 0 use -180 + // 180..360 - ang 90 use -270 + + SetSequence( seq_Inactive ); + SetThink(ChargerThink); + pev->nextthink = gpGlobals->time + 0.1; +} + +void CMdlWallHealth::Precache() +{ + PRECACHE_SOUND("items/medshot4.wav"); + PRECACHE_SOUND("items/medshotno1.wav"); + PRECACHE_SOUND("items/medcharge4.wav"); + PRECACHE_MODEL("models/health_charger_body.mdl" ); + UTIL_PrecacheOther("item_healthchargertank"); +} + +void CMdlWallHealth::UpdateFluidTank( void ) +{ + pFluidTank->Update( m_iJuice ); +/* + -11 - 0 + 0 - 100 + + m_iJuice - X + gSkillData.healthchargerCapacity - 100 + + 25 - 50 (?) + 50 - 100 + 25:50*100 + + m_iJuice div gSkillData.healthchargerCapacity * 100 + -(10 - (m_iJuice div gSkillData.healthchargerCapacity * 10)) +*/ +} + +void CMdlWallHealth::ChargerThink( void ) +{ + //ALERT( at_console, "WallHealth thinks!\n" ); + //ALERT( at_console, "next = %f prev = %f\n", m_flNextCharge, m_flStopCharge ); + //ALERT( at_console, "seq %d, on %d, juice %d\n", GetSequence(), m_iOn, m_iJuice ); + + StudioFrameAdvance(); + pev->nextthink = gpGlobals->time + 0.1; + + Vector posGun, angGun; + Vector vecTarget, vecOut, angles; + float flDist; + + if (m_bStartUsing == true) + if (m_flStopCharge >= gpGlobals->time) + { + SetSequence( seq_RetractShot ); + pFluidTank->SetSequence( seq_ToRest ); + m_flStopCharge = 0; + } + + switch( GetSequence() ) + { + case seq_Inactive: // inactive + { + if (pev->skin == HEALTHSTATION_EMPTY) // we're in "off" state + { + SetThink( NULL ); + return; + } + + // iterate on all entities in the vicinity. + CBaseEntity *pTmpEntity = NULL; + while ((pTmpEntity = UTIL_FindEntityInSphere( pTmpEntity, pev->origin, CHARGER_AWAKE_DISTANCE )) != NULL) + { + if (pTmpEntity->IsPlayer()) + { + pEntity = pTmpEntity; + if (( pev->origin - pEntity->pev->origin).Length() <= CHARGER_AWAKE_DISTANCE ) + { + SetSequence( seq_Deploy ); + break; + } + } + } + } + break; + + case seq_Deploy: // arm awakens + UpdateArm(); + if ( m_fSequenceFinished ) + SetSequence( seq_PrepShot ); + break; + + case seq_PrepShot: // we are waiting for player to use charger + flDist = ( pev->origin - pEntity->pev->origin).Length(); // CRASH IS HERE!!!!!! + //ALERT( at_console, "dist = %f\n", flDist ); + + if ( flDist > CHARGER_AWAKE_DISTANCE ) + { + SetSequence( seq_RetractArm ); + break; + } + UpdateArm(); + // can be interruped from USE function which activates seq_GiveShot + + break; + + case seq_RetractArm: + if ( m_fSequenceFinished ) + { + SetSequence( seq_Inactive ); + pEntity = NULL; + } else + UpdateArm(); // possible fix??? + break; + + case seq_RetractShot: // stopped using + m_bStartUsing = false; + if ( m_fSequenceFinished ) + SetSequence( seq_PrepShot ); + break; + + case seq_GiveShot: // started using + m_bStartUsing = false; + if ( m_fSequenceFinished ) + { + m_bStartUsing = true; + SetSequence( seq_ShotIdle ); + } + break; + + case seq_ShotIdle: // in use + // give health here while USE is pressed + break; + + default: + break; + } + +} + +void CMdlWallHealth::UpdateArm() +{ + Vector vecTarget = (pev->origin - pEntity->pev->origin).Normalize( ); + Vector angles = UTIL_VecToAngles (vecTarget); + //ALERT( at_console, "angles = %f, %f, %f - %f\n", angles.x, angles.y, angles.z, angles.y / 180 ); + if (angles.y > 180) + angles.y = angles.y - 360; + if (angles.y < -180) + angles.y = angles.y + 360; + + // 180 - 100 + // 90 - X (50) + // 50 = 90 * 100 / 180 + // percent = desired degree * 100 / 180 + // percent = desired degree / 180 + + // controller is -90...90 + // -90...90 - 180 use 0 + // 0..180 - 270 use -90 + // 90..270 - 0 use -180 + // 180..360(0) - 90 use + + // pos = (end - start) * scalar + start + // pos = 180 * scalar - 90 + SetBoneController(0, angles.y + m_iRotValue); //- 90); // +25 to the right +} + +void CMdlWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // Make sure that we have a caller + if (!pActivator) + return; + + // if it's not a player, ignore + if ( !pActivator->IsPlayer() ) + return; + + if (pev->skin == HEALTHSTATION_EMPTY) // already off + return; + + // if there is no juice left, turn it off + if (m_iJuice <= 0) + { + TurnOff(); // do we need it there or in Off() ? + return; + } + + // if the player doesn't have the suit, or there is no juice left, make the deny noise + if ((m_iJuice <= 0) || (!(pActivator->pev->weapons & (1<time) + { + m_flSoundTime = gpGlobals->time + 0.62; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshotno1.wav", 1.0, ATTN_NORM ); + } + return; + } + + pFluidTank->StartUse(); + pev->nextthink = gpGlobals->time + 0.25; // pev->ltime + SetThink(Off); + + // start the give shot sequence and do not use until it's finished + if (GetSequence() != seq_GiveShot) + if ( m_bStartUsing == false ) + { + SetSequence( seq_GiveShot ); + return; + } + + // Time to recharge yet? + if (m_flNextCharge >= gpGlobals->time) + return; + + // Play the on sound or the looping charging sound + if (!m_iOn) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM ); + m_flSoundTime = 0.56 + gpGlobals->time; + } + if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->time)) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/medcharge4.wav", 1.0, ATTN_NORM ); + } + + // charge the player + if ( pActivator->TakeHealth( 1, DMG_GENERIC ) ) + { + m_iJuice--; + //pFluidTank->StartUse(); + } + + UpdateFluidTank(); + + // govern the rate of charge + m_flNextCharge = gpGlobals->time + 0.1; +} + +void CMdlWallHealth::Recharge(void) +{ + //if (pev->skin == HEALTHSTATION_EMPTY) + // return; + + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM ); + m_iJuice = gSkillData.healthchargerCapacity; + pev->skin = HEALTHSTATION_FULL; // set the active skin + SetThink( ChargerThink ); +} + +void CMdlWallHealth::Off(void) +{ + m_flStopCharge = gpGlobals->time + 0.25; + SetThink( ChargerThink ); + pev->nextthink = gpGlobals->time + 0.01; + + // Stop looping sound. + if (m_iOn > 1) + STOP_SOUND( ENT(pev), CHAN_STATIC, "items/medcharge4.wav" ); + + m_iOn = 0; + + if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) ) + { + pev->nextthink = pev->ltime + m_iReactivate; + SetThink(Recharge); + } + //else + // SetThink( SUB_DoNothing ); +} + +void CMdlWallHealth::TurnOff( void ) +{ + pev->skin = HEALTHSTATION_EMPTY; + pFluidTank->SetSequence( seq_ToRest ); + SetUse( NULL ); + SetSequence( seq_RetractArm ); +} \ No newline at end of file diff --git a/dlls/hgrunt.cpp b/dlls/hgrunt.cpp index d8d198db..cbfd78c4 100644 --- a/dlls/hgrunt.cpp +++ b/dlls/hgrunt.cpp @@ -53,7 +53,7 @@ extern DLL_GLOBAL int g_iSkillLevel; #define GRUNT_ATTN ATTN_NORM // attenutation of grunt sentences #define HGRUNT_LIMP_HEALTH 20 #define HGRUNT_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. -#define HGRUNT_NUM_HEADS 2 // how many grunt heads are there? +#define HGRUNT_NUM_HEADS 4 // how many grunt heads are there? #define HGRUNT_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill #define HGRUNT_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences @@ -62,15 +62,18 @@ extern DLL_GLOBAL int g_iSkillLevel; #define HGRUNT_GRENADELAUNCHER ( 1 << 2) #define HGRUNT_SHOTGUN ( 1 << 3) +#define LODS_GROUP 0 #define HEAD_GROUP 1 -#define HEAD_GRUNT 0 -#define HEAD_COMMANDER 1 -#define HEAD_SHOTGUN 2 -#define HEAD_M203 3 #define GUN_GROUP 2 + +#define HEAD_GRUNT 0 +#define HEAD_COMMANDER 1// * 3 +#define HEAD_SHOTGUN 2// * 3 +#define HEAD_M203 3// * 3 + #define GUN_MP5 0 -#define GUN_SHOTGUN 1 -#define GUN_NONE 2 +#define GUN_SHOTGUN 1// * 3 +#define GUN_NONE 2// * 3 //========================================================= // Monster's Anim Events Go Here diff --git a/dlls/items.cpp b/dlls/items.cpp index 08a13711..6d2e530e 100644 --- a/dlls/items.cpp +++ b/dlls/items.cpp @@ -28,6 +28,15 @@ #include "skill.h" #include "items.h" #include "gamerules.h" +#include "decals.h" +#include "actanimating.h" + +#define seqEmitterClosed 0 +#define seqEmitterDeploy 1 +#define seqEmitterIdleOpen 2 +#define seqEmitterBroken1 3 +#define seqEmitterBroken2 4 +#define seqEmitterDeath 5 extern int gmsgItemPickup; diff --git a/dlls/monstermaker.cpp b/dlls/monstermaker.cpp index 38173d41..e15a36b5 100644 --- a/dlls/monstermaker.cpp +++ b/dlls/monstermaker.cpp @@ -72,6 +72,8 @@ TYPEDESCRIPTION CMonsterMaker::m_SaveData[] = DEFINE_FIELD( CMonsterMaker, m_iMaxLiveChildren, FIELD_INTEGER ), DEFINE_FIELD( CMonsterMaker, m_fActive, FIELD_BOOLEAN ), DEFINE_FIELD( CMonsterMaker, m_fFadeChildren, FIELD_BOOLEAN ), + DEFINE_FIELD( CMonsterMaker, m_fIsWarpBall, FIELD_BOOLEAN ), + DEFINE_FIELD( CMonsterMaker, m_cTotalMonstersCount, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CMonsterMaker, CBaseMonster ) @@ -93,14 +95,33 @@ void CMonsterMaker::KeyValue( KeyValueData *pkvd ) m_iszMonsterClassname = ALLOC_STRING( pkvd->szValue ); pkvd->fHandled = TRUE; } + else if ( FStrEq(pkvd->szKeyName, "warptarget") || FStrEq(pkvd->szKeyName, "makertarget") || FStrEq(pkvd->szKeyName, "warp_target") ) + { + m_iszWarpTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "monsterspawnflags") ) // monsterspawnflags + { + m_iChildrenSpawnflags = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + // radius + // damage_delay else CBaseMonster::KeyValue( pkvd ); } void CMonsterMaker::Spawn() { + //ALERT( at_console, "CMonsterMaker::Spawn\n"); + m_fIsWarpBall = !strcmp(STRING(pev->classname), "env_warpball"); + pev->solid = SOLID_NOT; + // for WarpBall to function correctly - it's spawnflag is another then same in monstermaker + if ( m_fIsWarpBall && FBitSet ( pev->spawnflags, SF_WARPBALL_ONCE )) // flag bit 1 + SetBits ( pev->spawnflags, SF_MONSTERMAKER_FIREONCE ); // flag bit 16 + m_cLiveChildren = 0; Precache(); if( !FStringNull( pev->targetname ) ) @@ -149,9 +170,20 @@ void CMonsterMaker::Spawn() void CMonsterMaker::Precache( void ) { + //ALERT( at_console, "%s::Precache\n", STRING(pev->classname)); + CBaseMonster::Precache(); + if (m_fIsWarpBall) + { + m_flDelay = 5; + UTIL_PrecacheOther( "effect_warpball" ); + } + + if (FStringNull( m_iszMonsterClassname ) != true) UTIL_PrecacheOther( STRING( m_iszMonsterClassname ) ); + else + ALERT( at_console, "CMonsterMaker without a children name!\n"); } //========================================================= @@ -206,8 +238,9 @@ void CMonsterMaker::MakeMonster( void ) } pevCreate = VARS( pent ); - pevCreate->origin = pev->origin; - pevCreate->angles = pev->angles; + pevCreate->origin = DesiredOrigin; + pevCreate->angles = DesiredAngles; + pevCreate->spawnflags = m_iChildrenSpawnflags; SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND ); // Children hit monsterclip brushes @@ -228,10 +261,21 @@ void CMonsterMaker::MakeMonster( void ) if( m_cNumMonsters == 0 ) { - // Disable this forever. Don't kill it because it still gets death notices + // FIXME: do we need this comment? "Disable this forever. Don't kill it because it still gets death notices" + //m_cNumMonsters = m_cTotalMonstersCount; + //m_fActive = FALSE; SetThink( NULL ); SetUse( NULL ); } + + if ( !FBitSet ( pev->spawnflags, SF_MONSTERMAKER_CYCLIC ) ) + { + if ( FBitSet ( pev->spawnflags, SF_MONSTERMAKER_FIREONCE ) ) + { + //ALERT( at_console, "Removing MakeMonster\n"); + UTIL_Remove( this ); + } + } } //========================================================= @@ -270,6 +314,8 @@ void CMonsterMaker::ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, US //========================================================= void CMonsterMaker::MakerThink( void ) { + //ALERT( at_console, "CMonsterMaker::MakerThink\n"); + pev->nextthink = gpGlobals->time + m_flDelay; MakeMonster(); diff --git a/dlls/player.cpp b/dlls/player.cpp index 27f9705e..5f8a6c73 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -67,6 +67,11 @@ extern CGraph WorldGraph; #define FLASH_DRAIN_TIME 1.2f //100 units/3 minutes #define FLASH_CHARGE_TIME 0.2f // 100 units/20 seconds (seconds per unit) +#define MODE_STAND 0 +#define MODE_RUN 1 +#define MODE_CROUCH 2 +#define MODE_JUMP 3 + // Global Savedata for player TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = { @@ -116,6 +121,7 @@ TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = DEFINE_FIELD( CBasePlayer, m_pTank, FIELD_EHANDLE ), DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_iDecayId, 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 @@ -3440,7 +3446,15 @@ void CBasePlayer::ForceClientDllUpdate( void ) m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message memset( m_rgAmmoLast, 0, sizeof( m_rgAmmoLast )); // a1ba: Force update AmmoX - +int gmsgLensFlare = 0; +int gmsgAimFrame = 0; +int gmsgNotepad = 0; +int gmsgChangeMode = 0; +int gmsgCamera = 0; +int gmsgChangePlayer = 0; +int gmsgSparePlayer = 0; +int gmsgAlienState = 0; +int gmsgUpdateDecayPlayerName = 0; // Now force all the necessary messages // to be sent. @@ -4363,6 +4377,7 @@ Vector CBasePlayer::GetAutoaimVector( float flDelta ) // ALERT( at_console, "%f %f\n", angles.x, angles.y ); UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); + //AutoaimFrame(vecSrc, flDist, flDelta); //draw selection frame around objects return gpGlobals->v_forward; } @@ -4495,6 +4510,94 @@ void CBasePlayer::ResetAutoaim() m_fOnTarget = FALSE; } +void CBasePlayer :: AutoaimFrame( Vector &vecSrc, float flDist, float flDelta ) +{ + edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + CBaseEntity *pEntity; + float bestdot; + Vector bestdir; + TraceResult tr; + + MESSAGE_BEGIN( MSG_ALL, gmsgAimFrame); + WRITE_BYTE( 0 ); + WRITE_BYTE( 0.0 ); + WRITE_COORD( 0.0 ); + WRITE_COORD( 0.0 ); + WRITE_COORD( 0.0 ); + WRITE_COORD( 0.0 ); + WRITE_COORD( 0.0 ); + WRITE_COORD( 0.0 ); + MESSAGE_END(); + + UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); + + // try all possible entities + bestdot = flDelta; // +- 10 degrees + + UTIL_TraceLine( vecSrc, vecSrc + bestdir * flDist, dont_ignore_monsters, edict(), &tr ); + for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) + { + Vector center; + Vector dir; + float dot; + + if ( pEdict->free ) // Not in use + continue; + + if (pEdict == edict()) + continue; + + if ( !g_pGameRules->ShouldAutoAim( this, pEdict ) ) + continue; + + pEntity = Instance( pEdict ); + if (pEntity == NULL) + 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) + { + continue; + } + + if (!FClassnameIs(pEntity->pev,"func_frame")) continue; + + CFuncFrame* pFrame = (CFuncFrame*)CBaseEntity::Instance(pEdict); + //ALERT( at_console, "Kind is %d\n", pFrame->m_iKind); + + if ((vecSrc-center).Length() < 200) //was 50 + { + MESSAGE_BEGIN( MSG_ALL, gmsgAimFrame); + WRITE_BYTE( pEntity->entindex() ); + WRITE_BYTE( pFrame->m_iKind ); + WRITE_COORD( pEntity->pev->mins.x ); + WRITE_COORD( pEntity->pev->mins.y ); + WRITE_COORD( pEntity->pev->mins.z ); + WRITE_COORD( pEntity->pev->maxs.x ); + WRITE_COORD( pEntity->pev->maxs.y ); + WRITE_COORD( pEntity->pev->maxs.z ); + MESSAGE_END(); + } + } +} + /* ============= SetCustomDecalFrames @@ -4713,9 +4816,25 @@ BOOL CBasePlayer::SwitchWeapon( CBasePlayerItem *pWeapon ) return TRUE; } +//========================================================= +// Set Decay's player index +//========================================================= +void CBasePlayer::SetDecayPlayerIndex( int Id ) +{ + m_iDecayId = Id; +} + //========================================================= // Dead HEV suit prop //========================================================= + +#define PLAYER_LODS 4 + +#define HEAD_GROUP 1 +#define HEAD_GORDON 0 +#define HEAD_HELMET 1 + + class CDeadHEV : public CBaseMonster { public: @@ -4763,7 +4882,8 @@ void CDeadHEV::Spawn( void ) pev->effects = 0; pev->yaw_speed = 8; pev->sequence = 0; - pev->body = 1; + //pev->body = 1; + SetBodygroup( HEAD_GROUP, HEAD_HELMET * PLAYER_LODS ); m_bloodColor = BLOOD_COLOR_RED; pev->sequence = LookupSequence( m_szPoses[m_iPose] ); diff --git a/dlls/player.h b/dlls/player.h index 90dd62e1..5fa009dc 100644 --- a/dlls/player.h +++ b/dlls/player.h @@ -226,7 +226,9 @@ public: virtual int Restore( CRestore &restore ); void RenewItems(void); void PackDeadPlayerItems( void ); + void PackAllItems( void ); void RemoveAllItems( BOOL removeSuit ); + void SetDecayPlayerIndex( int Id ); BOOL SwitchWeapon( CBasePlayerItem *pWeapon ); // JOHN: sends custom messages if player HUD data has changed (eg health, ammo) @@ -298,6 +300,7 @@ public: void ResetAutoaim( void ); Vector GetAutoaimVector( float flDelta ); Vector AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta ); + void AutoaimFrame( Vector &vecSrc, float flDist, float flDelta ); // Draw selection frame around objects void ForceClientDllUpdate( void ); // Forces all client .dll specific data to be resent to client. @@ -325,6 +328,7 @@ public: void SetPrefsFromUserinfo( char *infobuffer ); float m_flNextChatTime; + int m_iDecayId; // Decay player index int m_iAutoWepSwitch; diff --git a/dlls/scientist.cpp b/dlls/scientist.cpp index 7fb7bab5..380e9e98 100644 --- a/dlls/scientist.cpp +++ b/dlls/scientist.cpp @@ -1466,3 +1466,242 @@ int CSittingScientist::FIdleSpeak( void ) CTalkMonster::g_talkWaitTime = 0; return FALSE; } + +//========================================================= +// Doctor Keller +// monster_wheelchair +//========================================================= + +class CWheelChairScientist : public CScientist // kdb: changed from public CBaseMonster so he can speak +{ +public: + void Spawn( void ); + void Precache( void ); + int FIdleSpeak( void ); + void PainSound( void ); + void TalkInit( void ); + BOOL CanHeal( void ); +}; + +LINK_ENTITY_TO_CLASS( monster_wheelchair, CWheelChairScientist ); + +void CWheelChairScientist :: Spawn( ) +{ + Precache(); + SET_MODEL(ENT(pev), "models/wheelchair_sci.mdl"); + + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = gSkillData.barneyHealth; + pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello + m_MonsterState = MONSTERSTATE_NONE; + + pev->body = 0; // gun in holster + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + MonsterInit(); + SetUse( FollowerUse ); +} + +void CWheelChairScientist :: Precache( void ) +{ + PRECACHE_MODEL("models/wheelchair_sci.mdl"); + + PRECACHE_SOUND("keller/dk_pain1.wav"); + PRECACHE_SOUND("keller/dk_pain2.wav"); + PRECACHE_SOUND("keller/dk_pain3.wav"); + PRECACHE_SOUND("keller/dk_pain4.wav"); + PRECACHE_SOUND("keller/dk_pain5.wav"); + PRECACHE_SOUND("keller/dk_pain6.wav"); + PRECACHE_SOUND("keller/dk_pain7.wav"); + + PRECACHE_SOUND("wheelchair/wheelchair_jog.wav"); + PRECACHE_SOUND("wheelchair/wheelchair_run.wav"); + PRECACHE_SOUND("wheelchair/wheelchair_walk.wav"); + + TalkInit(); + CTalkMonster::Precache(); +} + +int CWheelChairScientist :: FIdleSpeak( void ) +{ + return -1; +} + +BOOL CWheelChairScientist :: CanHeal( void ) +{ + return FALSE; +} + +void CWheelChairScientist :: PainSound( void ) +{ + if (gpGlobals->time < m_painTime ) + return; + + m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75); + + switch (RANDOM_LONG(0,6)) + { + case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 3: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain4.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 4: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain5.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 5: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain6.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 6: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "keller/dk_pain7.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + } +} + +void CWheelChairScientist :: TalkInit() +{ + CTalkMonster::TalkInit(); + + // Keller's speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = NULL; + m_szGrp[TLK_QUESTION] = NULL; + m_szGrp[TLK_IDLE] = "DK_IDLE"; + m_szGrp[TLK_STARE] = "DK_STARE"; + m_szGrp[TLK_HELLO] = "DK_HELLO"; + + m_szGrp[TLK_FEAR] = "DK_FEAR"; + m_szGrp[TLK_PLFEAR] = "DK_PLFEAR"; + m_szGrp[TLK_HEAL] = NULL; + + m_szGrp[TLK_USE] = "DK_OK"; + m_szGrp[TLK_UNUSE] = "DK_WAIT"; + m_szGrp[TLK_STOP] = "DK_STOP"; + m_szGrp[TLK_NOSHOOT] = "DK_SCARED"; + + m_szGrp[TLK_PLHURT1] = NULL; + m_szGrp[TLK_PLHURT2] = NULL; + m_szGrp[TLK_PLHURT3] = NULL; + + m_szGrp[TLK_WOUND] = NULL; + m_szGrp[TLK_MORTAL] = NULL; + + // get voice for head + m_voicePitch = 100; +} + +//========================================================= +// Doctor Rosenberg +// monster_rosenberg +//========================================================= + +class CRosenberg : public CScientist // kdb: changed from public CBaseMonster so he can speak +{ +public: + void Spawn( void ); + void Precache( void ); + void PainSound( void ); + void TalkInit( void ); + int FIdleSpeak( void ); +}; + +LINK_ENTITY_TO_CLASS( monster_rosenberg, CRosenberg ); + +void CRosenberg :: Spawn( ) +{ + Precache(); + SET_MODEL(ENT(pev), "models/scientist_rosenberg.mdl"); + + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = gSkillData.barneyHealth+10; + pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello + m_MonsterState = MONSTERSTATE_NONE; + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + SetBodygroup( BODY_GROUP, BODY_LOD0 ); + SetBodygroup( HEAD_GROUP, 0 ); + + MonsterInit(); + SetUse( FollowerUse ); +} + +void CRosenberg :: Precache( void ) +{ + PRECACHE_MODEL("models/scientist_rosenberg.mdl"); + + PRECACHE_SOUND("rosenberg/ro_pain0.wav"); + PRECACHE_SOUND("rosenberg/ro_pain1.wav"); + PRECACHE_SOUND("rosenberg/ro_pain2.wav"); + PRECACHE_SOUND("rosenberg/ro_pain3.wav"); + PRECACHE_SOUND("rosenberg/ro_pain4.wav"); + PRECACHE_SOUND("rosenberg/ro_pain5.wav"); + PRECACHE_SOUND("rosenberg/ro_pain6.wav"); + PRECACHE_SOUND("rosenberg/ro_pain7.wav"); + PRECACHE_SOUND("rosenberg/ro_pain8.wav"); + + TalkInit(); + CTalkMonster::Precache(); +} + +void CRosenberg :: PainSound( void ) +{ + if (gpGlobals->time < m_painTime ) + return; + + m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75); + + switch (RANDOM_LONG(0,8)) + { + case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain0.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 3: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 4: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain4.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 5: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain5.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 6: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain6.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 7: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain7.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 8: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "rosenberg/ro_pain8.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + } +} + +void CRosenberg :: TalkInit() +{ + CTalkMonster::TalkInit(); + + // Rosenberg's speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = NULL; + m_szGrp[TLK_QUESTION] = NULL; + m_szGrp[TLK_IDLE] = NULL; + m_szGrp[TLK_STARE] = NULL; + m_szGrp[TLK_HELLO] = NULL; + + m_szGrp[TLK_FEAR] = "RO_FEAR"; + m_szGrp[TLK_PLFEAR] = "RO_PLFEAR"; + m_szGrp[TLK_HEAL] = "RO_HEAL"; + + m_szGrp[TLK_USE] = "RO_OK"; + m_szGrp[TLK_UNUSE] = "RO_WAIT"; + m_szGrp[TLK_STOP] = "RO_STOP"; + m_szGrp[TLK_NOSHOOT] = "RO_SCARED"; + + m_szGrp[TLK_PLHURT1] = "!RO_CUREA"; + m_szGrp[TLK_PLHURT2] = "!RO_CUREB"; + m_szGrp[TLK_PLHURT3] = "!RO_CUREC"; + + m_szGrp[TLK_WOUND] = "RO_WOUND"; + m_szGrp[TLK_MORTAL] = "RO_MORTAL"; + + // get voice for head + m_voicePitch = 100; +} + +int CRosenberg::FIdleSpeak( void ) +{ + return -1; +} \ No newline at end of file diff --git a/dlls/subs.cpp b/dlls/subs.cpp index 9cdb6010..4ee1fb96 100644 --- a/dlls/subs.cpp +++ b/dlls/subs.cpp @@ -342,6 +342,7 @@ TYPEDESCRIPTION CBaseToggle::m_SaveData[] = DEFINE_FIELD( CBaseToggle, m_vecFinalAngle, FIELD_VECTOR ), DEFINE_FIELD( CBaseToggle, m_sMaster, FIELD_STRING), DEFINE_FIELD( CBaseToggle, m_bitsDamageInflict, FIELD_INTEGER ), // damage type inflicted + DEFINE_FIELD( CBaseToggle, m_iPlayerIndex, FIELD_INTEGER ), // Decay's player index }; IMPLEMENT_SAVERESTORE( CBaseToggle, CBaseAnimating ) diff --git a/dlls/talkmonster.cpp b/dlls/talkmonster.cpp index ed173231..a2d13d62 100644 --- a/dlls/talkmonster.cpp +++ b/dlls/talkmonster.cpp @@ -56,6 +56,8 @@ const char *CTalkMonster::m_szFriends[TLK_CFRIENDS] = "monster_barney", "monster_scientist", "monster_sitting_scientist", + "monster_rosenberg", + "monster_wheelchair" }; //========================================================= diff --git a/dlls/talkmonster.h b/dlls/talkmonster.h index 6a6e31ac..def73180 100644 --- a/dlls/talkmonster.h +++ b/dlls/talkmonster.h @@ -38,7 +38,7 @@ #define bit_saidHeard (1<<6) #define bit_saidSmelled (1<<7) -#define TLK_CFRIENDS 3 +#define TLK_CFRIENDS 5 typedef enum { diff --git a/dlls/teamplay_gamerules.cpp b/dlls/teamplay_gamerules.cpp index 5c9a98d3..55192b13 100644 --- a/dlls/teamplay_gamerules.cpp +++ b/dlls/teamplay_gamerules.cpp @@ -33,6 +33,7 @@ extern DLL_GLOBAL BOOL g_fGameOver; CHalfLifeTeamplay::CHalfLifeTeamplay() { + ALERT( at_console, "Half-Life multiplayer teamplay\n"); m_DisableDeathMessages = FALSE; m_DisableDeathPenalty = FALSE; diff --git a/dlls/triggers.cpp b/dlls/triggers.cpp index 69b3ee3d..fd7472db 100644 --- a/dlls/triggers.cpp +++ b/dlls/triggers.cpp @@ -27,6 +27,10 @@ #include "saverestore.h" #include "trains.h" // trigger_camera has train functionality #include "gamerules.h" +#include "shake.h" // trigger_enddecay fading constants +#include "effects.h" // env_render can use instance of CEnvMirroredLaser +#include "triggers.h" +#include "shellapi.h" #define SF_TRIGGER_PUSH_START_OFF 2//spawnflag that makes trigger_push spawn turned OFF #define SF_TRIGGER_HURT_TARGETONCE 1// Only fire hurt target once @@ -34,8 +38,14 @@ #define SF_TRIGGER_HURT_NO_CLIENTS 8//spawnflag that makes trigger_push spawn turned OFF #define SF_TRIGGER_HURT_CLIENTONLYFIRE 16// trigger hurt will only fire its target if it is hurting a client #define SF_TRIGGER_HURT_CLIENTONLYTOUCH 32// only clients may touch this trigger. +#define SF_MM_KILLTARGETS 2 + +// to help trigger entites define who is standing inside +#define PF_PLAYER1 1 +#define PF_PLAYER2 2 extern DLL_GLOBAL BOOL g_fGameOver; +extern bool bDecay; extern void SetMovedir(entvars_t* pev); extern Vector VecBModelOrigin( entvars_t* pevBModel ); @@ -457,6 +467,7 @@ void CMultiManager::ManagerReport( void ) #define SF_RENDER_MASKMODE ( 1 << 2 ) #define SF_RENDER_MASKCOLOR ( 1 << 3 ) +// TODO: it is rather slow using NETNAME to set renderfx, need to make list of those entities on level load class CRenderFxManager : public CBaseEntity { public: @@ -492,6 +503,38 @@ void CRenderFxManager::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_T if( !FBitSet( pev->spawnflags, SF_RENDER_MASKCOLOR ) ) pevTarget->rendercolor = pev->rendercolor; } + } else + if (!FStringNull(pev->netname)) + { + edict_t* pentTarget = NULL; + while ( 1 ) + { + pentTarget = FIND_ENTITY_BY_NETNAME(pentTarget, STRING(pev->netname)); + if (FNullEnt(pentTarget)) + break; + + // other env_renders also have netname specified, and we must not touch them, so: + // if classname<>env_render then + // do smthng + if (FClassnameIs(pentTarget, "env_render")) + break; + + entvars_t *pevTarget = VARS( pentTarget ); + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKFX ) ) + pevTarget->renderfx = pev->renderfx; + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKAMT ) ) + pevTarget->renderamt = pev->renderamt; + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKMODE ) ) + pevTarget->rendermode = pev->rendermode; + if ( !FBitSet( pev->spawnflags, SF_RENDER_MASKCOLOR ) ) + pevTarget->rendercolor = pev->rendercolor; + + if (FClassnameIs(pentTarget, "env_mirroredlaser")) + { + CEnvMirroredLaser* pLaser = (CEnvMirroredLaser*)CBaseEntity::Instance(pentTarget); + pLaser->ReColorLasers(); + } + } } } @@ -552,6 +595,11 @@ void CBaseTrigger::KeyValue( KeyValueData *pkvd ) m_bitsDamageInflict = atoi( pkvd->szValue ); pkvd->fHandled = TRUE; } + else if (FStrEq(pkvd->szKeyName, "player_index")) + { + m_iPlayerIndex = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } else CBaseToggle::KeyValue( pkvd ); } @@ -1496,6 +1544,17 @@ void CChangeLevel::ChangeLevelNow( CBaseEntity *pActivator ) } //ALERT( at_console, "Level touches %d levels\n", ChangeList( levels, 16 ) ); ALERT( at_console, "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot ); + +/* +Vyacheslav Dzhura: 8/9/2008 +DONE: COMMENTED - EXPERIMENTAL!!!!!!!! +*/ + if ( g_pGameRules->IsCoOp ) + { + char cmd[128]; + sprintf( cmd, "changelevel %s %s\n", st_szNextMap, st_szNextSpot ); + SERVER_COMMAND( cmd ); + } else CHANGE_LEVEL( st_szNextMap, st_szNextSpot ); } @@ -1962,6 +2021,25 @@ LINK_ENTITY_TO_CLASS( trigger_endsection, CTriggerEndSection ) void CTriggerEndSection::EndSectionUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { + char szFilename[MAX_PATH]; + GET_GAME_DIR( szFilename ); + strcat( szFilename, "\\manual\\credits.xml" ); + + char szFilename2[MAX_PATH]; + GET_GAME_DIR( szFilename2 ); + strcat( szFilename2, "\\manual" ); + + int i; + + for( i = 0; i < strlen( szFilename ); i++) + if ( szFilename[i] == '/' ) szFilename[i] = '\\'; + + for( i = 0; i < strlen( szFilename2 ); i++) + if ( szFilename2[i] == '/' ) szFilename2[i] = '\\'; + + if (!IS_DEDICATED_SERVER()) + ShellExecute( GetActiveWindow(), "open", "iexplore", szFilename, szFilename2, SW_MAXIMIZE); + // Only save on clients if( pActivator && !pActivator->IsNetClient() ) return; @@ -2386,3 +2464,592 @@ void CTriggerCamera::Move() float fraction = 2 * gpGlobals->frametime; pev->velocity = ( ( pev->movedir * pev->speed ) * fraction ) + ( pev->velocity * ( 1 - fraction ) ); } + +// +// TRIGGER_ENDDECAY +// + +class CTriggerEndDecay : public CBaseTrigger +{ +//private: +// int m_iPhase; +public: + void Spawn( void ); + void EXPORT EndDecayTouch( CBaseEntity *pOther ); + void KeyValue( KeyValueData *pkvd ); + void EXPORT EndDecayUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void EXPORT EndDecayThink( void ); + + bool bIsFinal; + bool bUsed; + t_playerStats stats[3]; +}; +LINK_ENTITY_TO_CLASS( trigger_enddecay, CTriggerEndDecay ); + +void CTriggerEndDecay::Spawn( void ) +{ + bUsed = false; + + if ( g_pGameRules->IsDeathmatch() ) + { + REMOVE_ENTITY( ENT(pev) ); + return; + } + + InitTrigger(); + + SetThink( SUB_DoNothing ); + SetUse ( EndDecayUse ); + // If it is a "use only" trigger, then don't set the touch function. + if ( ! (pev->spawnflags & SF_ENDSECTION_USEONLY) ) + SetTouch( EndDecayTouch ); +} +/* + +MOVED TO: decay_gamerules.cpp + +char getGradeChar( int grade ) +{ + char gradeChar; + switch ( grade ) + { + case 3: + gradeChar = 'D'; + break; + case 2: + gradeChar = 'C'; + break; + case 1: + gradeChar = 'B'; + break; + case 0: + gradeChar = 'A'; + break; + default: + gradeChar = 'D'; + break; + } + return gradeChar; +} +*/ +void CTriggerEndDecay::EndDecayUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // Only save on clients + //if ( pActivator && !pActivator->IsNetClient() ) + // return; + + SetUse( NULL ); + + /*while (pActivator = UTIL_FindEntityByClassname(pActivator, "player")) + { + ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(FALSE); + }*/ + + CBasePlayer *client; + client = NULL; + while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) + { + if ( !client->pev ) + continue; + if ( !(client->IsNetClient()) ) // Not a client ? (should never be true) + continue; + client->EnableControl(FALSE); + } + + CDecayRules *g_pDecayRules = (CDecayRules*)g_pGameRules; + + // our players should have DecayId of 1 or 2, so "loop" between these two numbers only + if ( this->bIsFinal == true ) + { + int damageGrade; + int killsGrade; + int accuracyGrade; + + char gradeChar[8]; + memset( gradeChar, 0, sizeof( gradeChar ) ); + + damageGrade = killsGrade = accuracyGrade = 3; // by default "D" to all categories + + for (int i = 1; i <= 2; i++ ) + { + t_playerStats *curStats = &g_pDecayRules->pStats[i]; + + // compute accuracy + if ( ( curStats->shots * 100 ) != 0 ) + curStats->accuracy = (float)curStats->hits / (float)curStats->shots * 100; + else + curStats->accuracy = 0; + + // go through grade values for C,B,A + for (int j = 2; j >= 0; j-- ) + { + if ( curStats->damage <= stats[j].damage ) + damageGrade = j; + if ( curStats->kills >= stats[j].kills ) + killsGrade = j; + + if ( curStats->accuracy >= stats[j].accuracy ) + accuracyGrade = j; + } + + if ( curStats->accuracy == 0 ) // not a single shot!!! + accuracyGrade = 3; + + int finalGrade = ( damageGrade + killsGrade + accuracyGrade ) / 3; + g_pDecayRules->savePlayerStats( i, finalGrade, damageGrade, killsGrade, accuracyGrade ); + gradeChar[i] = g_pDecayRules->getGradeChar( finalGrade ); + } + + char buf[256]; + sprintf( buf, "Player 1 grade: %c\nPlayer 2 grade: %c", gradeChar[1], gradeChar[2] ); + UTIL_ShowMessageAll( buf ); + } + + UTIL_ScreenFadeAll( Vector(0, 0, 0), 7, 3, 255, FFADE_OUT ); + UTIL_ShowMessageAll( STRING(pev->message) ); + + pev->nextthink = gpGlobals->time + 7; + SetThink( EndDecayThink ); +} + +void CTriggerEndDecay::EndDecayThink( void ) +{ + +//******************************************************************************** +// Theoretically we should go there after level-end dialog which should be called +// from client.dll from EndDecayUse, then player should choose next mission, read +// it's briefing and continue game on that level, or restart current or exit the +// game. Callback is done via console commands which are intercepted in this dll +// in clien.cpp file which then could call methods of this trigger_enddecay +//******************************************************************************** + + if ( bUsed == true ) + return; +/* + if ( pev->spawnflags & 2 ) + { + bUsed = true; + return; + } +*/ + static char szNextMap[128]; + sprintf( szNextMap, "null" ); + + if (g_pGameRules->IsCoOp()) + { + CDecayRules *g_pDecayRules = (CDecayRules*)g_pGameRules; + sprintf( szNextMap, g_pDecayRules->getDecayNextMap() ); + } + + //if ( pev->message ) + // g_engfuncs.pfnEndSection(STRING(pev->message)); + + bool bHasNextMap = ( strcmp( szNextMap, "null" ) != 0 ); + + + if ( this->bIsFinal ) + { + if ( ( bHasNextMap == true ) && ( pev->spawnflags & 2 ) == false ) + CHANGE_LEVEL( szNextMap, NULL ); + else + { + if ( ( pev->spawnflags & 2 ) == false ) + { + if ( pev->message ) + g_engfuncs.pfnEndSection(STRING(pev->message)); + else + g_engfuncs.pfnEndSection( "Decay" ); + } // else map should be ended somehow via it's entities login + } + } else + { + // do restart of server + char cmd[128]; + sprintf( cmd, "restart\n" ); + SERVER_COMMAND( cmd ); + } + +/* + if ( ( strcmp( szNextMap, "null" ) != 0 ) && ( this->bIsFinal == true ) ) + CHANGE_LEVEL( szNextMap, NULL ); + else { + int mapId = g_pDecayRules->getDecayMapId(); + + if ( ( mapId == 11 ) || ( mapId == 12 ) ) // do not restart server for dy_outro or dy_alien + { + if ( pev->message ) + g_engfuncs.pfnEndSection(STRING(pev->message)); + else + g_engfuncs.pfnEndSection( "Decay" ); + } else + { + // do restart of server + char cmd[128]; + sprintf( cmd, "restart\n" ); + SERVER_COMMAND( cmd ); + } + } +*/ + // TODO: send here message to client.dll to popup level stats dialog + // TODO: do we need this here? + // pev->nextthink = gpGlobals->time + 10.0; + // SetThink( SUB_DoNothing ); + bUsed = true; + //UTIL_Remove( this ); +} + +void CTriggerEndDecay::EndDecayTouch( CBaseEntity *pOther ) +{ + // Only save on clients + if ( !pOther->IsNetClient() ) + return; + + SetTouch( NULL ); + Use( pOther, pOther, USE_ON, 1 ); + // same as use below +} + +void CTriggerEndDecay :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "success")) + { + if (atoi( pkvd->szValue ) == 1) + this->bIsFinal = true; + else + this->bIsFinal = false; + pkvd->fHandled = TRUE; + } else + + // + // WOUNDS (DAMAGE) + // + if (FStrEq(pkvd->szKeyName, "woundsa")) + { + this->stats[0].damage = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + + if (FStrEq(pkvd->szKeyName, "woundsb")) + { + this->stats[1].damage = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + if (FStrEq(pkvd->szKeyName, "woundsc")) + { + this->stats[2].damage = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + + // + // KILLS + // + if (FStrEq(pkvd->szKeyName, "killsa")) + { + this->stats[0].kills = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + if (FStrEq(pkvd->szKeyName, "killsb")) + { + this->stats[1].kills = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + if (FStrEq(pkvd->szKeyName, "killsc")) + { + this->stats[2].kills = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + + // + // ACCURACY + // + if (FStrEq(pkvd->szKeyName, "accuracya")) + { + this->stats[0].accuracy = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + if (FStrEq(pkvd->szKeyName, "accuracyb")) + { + this->stats[1].accuracy = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + if (FStrEq(pkvd->szKeyName, "accuracyc")) + { + this->stats[2].accuracy = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + + if (FStrEq(pkvd->szKeyName, "section")) + { +// m_iszSectionName = ALLOC_STRING( pkvd->szValue ); + // Store this in message so we don't have to write save/restore for this ent + pev->message = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseTrigger::KeyValue( pkvd ); +} + +// +// TRIGGER_PLAYERFREEZE (based on code from Spirit of Half-Life SDK) +// + +class CPlayerFreeze: public CBaseDelay +{ + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Think( void ); + EHANDLE m_hActivator; +}; + +void CPlayerFreeze::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBasePlayer *client; + client = NULL; + while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) ) + { + if ( !client->pev ) + continue; + + // if ( client->edict() == pEntity ) + // continue; + + // if ( !(client->IsNetClient()) ) // Not a client ? (should never be true) + // continue; + + if (client->pev->flags & FL_FROZEN) + client->EnableControl(TRUE); + else + client->EnableControl(FALSE); + } + + /* + pActivator = UTIL_FindEntityByClassname(NULL, "player"); + if (pActivator->pev->flags & FL_FROZEN) + ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(TRUE); + else + ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(FALSE); + */ + + /*if (pActivator && pActivator->pev->flags & FL_CLIENT) + { + if (pActivator->pev->flags & FL_FROZEN) + { + // unfreeze him + ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(TRUE); + m_hActivator = NULL; + SetThink( SUB_DoNothing ); + } + else + { + // freeze him + ((CBasePlayer *)((CBaseEntity *)pActivator))->EnableControl(FALSE); + if (m_flDelay) + { + m_hActivator = pActivator; + pev->nextthink = gpGlobals->time + m_flDelay; + } + } + }*/ +} + +void CPlayerFreeze::Think ( void ) +{ + Use(m_hActivator, this, USE_ON, 0); +} + +LINK_ENTITY_TO_CLASS( trigger_playerfreeze, CPlayerFreeze ); + +// +// trigger_random +// + +class CTriggerRandom : public CBaseDelay +{ +public: + void KeyValue( KeyValueData *pkvd ); + void Spawn( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + + static TYPEDESCRIPTION m_SaveData[]; + +private: + int m_iRandomRange; + float m_flProbability; +}; +LINK_ENTITY_TO_CLASS( trigger_random, CTriggerRandom ); + +TYPEDESCRIPTION CTriggerRandom::m_SaveData[] = +{ + DEFINE_FIELD( CTriggerRandom, m_iRandomRange, FIELD_INTEGER ), + DEFINE_FIELD( CTriggerRandom, m_flProbability, FIELD_FLOAT ), +}; + +IMPLEMENT_SAVERESTORE(CTriggerRandom,CBaseDelay); + +void CTriggerRandom::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "randomrange")) + { + m_iRandomRange = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } else + if (FStrEq(pkvd->szKeyName, "probability")) + { + m_flProbability = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + + +void CTriggerRandom::Spawn( void ) +{ +} + +void CTriggerRandom::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int FireId = RANDOM_LONG(0, m_iRandomRange-1); // was 1, m_iRandomRange + //SUB_UseTargets( this, USE_ON, 0 ); + + char m_iszFireTarget[60]; + sprintf( m_iszFireTarget, "%s%d", STRING( pev->target ), FireId); + + CBaseEntity *pEnt = NULL; + bool bFoundRandomTarget = false; + while ((pEnt = UTIL_FindEntityByTargetname( pEnt, m_iszFireTarget )) != NULL) + { + pEnt->Use( pActivator, pCaller, useType, value ); + bFoundRandomTarget = true; + } + + if ( !bFoundRandomTarget ) + ALERT( at_console, "Randomly found entity `%s` not found!\n", m_iszFireTarget ); + + /* + CBaseEntity *pTarget = UTIL_FindEntityByTargetname( NULL, m_iszFireTarget ); + if ( pTarget ) + pTarget->Use( pActivator, pCaller, useType, value ); + else + ALERT( at_console, "Randomly found entity %s not found!\n", m_iszFireTarget ); + */ + + //if ( pev->spawnflags & SF_RELAY_FIREONCE ) + // UTIL_Remove( this ); +} + +// +// trigger_bit and trigger_bit_counter (used in ht11lasers) +// + +typedef struct +{ + bool Bits[8]; +} TBitArray; + +TBitArray ByteToArray(byte Number) +{ + int i; + byte fl = 1; + TBitArray MyArr; + for (i = 0; i < 8; i++, fl = fl << 1) { + MyArr.Bits[i] = Number & fl; + } + return MyArr; +} + +byte ArrayToByte(TBitArray A) +{ + int i; + byte fl = 1; + byte Result; + for (i = 0; i < 8; i++, fl = fl << 1) { + if (A.Bits[i]) { + Result = Result | fl; + } + } + return Result; +} + +class CTriggerBit : public CBaseDelay +{ + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +}; +LINK_ENTITY_TO_CLASS( trigger_bit, CTriggerBit ); + +void CTriggerBit::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + SUB_UseTargets( this, USE_TOGGLE, 1.0 ); +} + +class CTriggerBitCounter : public CBaseDelay +{ +public: + void KeyValue( KeyValueData *pkvd ); + //void Spawn( void ); + //void Precache( void ); + void Think( void ); + + // use code, which will operates the bits + // use activators skin field as bit which we need to toggle + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + int m_iTriggerMask; + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; +}; +LINK_ENTITY_TO_CLASS( trigger_bit_counter, CTriggerBitCounter ); + +TYPEDESCRIPTION CTriggerBitCounter::m_SaveData[] = +{ + DEFINE_FIELD( CTriggerBitCounter, m_iTriggerMask, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE(CTriggerBitCounter, CBaseDelay); + +void CTriggerBitCounter::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "triggermask")) + { + m_iTriggerMask = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseDelay::KeyValue( pkvd ); +} + +void CTriggerBitCounter::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + //uncommentme + //ALERT( at_console, "trigger_bit_counter used! bit id received %d\n", pActivator->pev->skin ); + + int m_iDesiredBit = pActivator->pev->skin; + ASSERT( m_iDesiredBit<=8 ); + + TBitArray MyArr = ByteToArray( pev->skin ); + MyArr.Bits[m_iDesiredBit-1] = !MyArr.Bits[m_iDesiredBit-1]; + pev->skin = ArrayToByte(MyArr); + if ( pev->skin == m_iTriggerMask ) + { + ALERT( at_console, "trigger_bit_counter used! bit id received %d target %s\n", pActivator->pev->skin, STRING(pev->target) ); + SUB_UseTargets( this, USE_ON, 0 ); + } +} + +void CTriggerBitCounter::Think( void ) +{ +/* if ( !m_globalstate || gGlobalState.EntityGetState( m_globalstate ) == GLOBAL_ON ) + { + SUB_UseTargets( this, triggerType, 0 ); + if ( pev->spawnflags & SF_AUTO_FIREONCE ) + UTIL_Remove( this ); + } +*/ +} diff --git a/dlls/util.cpp b/dlls/util.cpp index 9834aa1f..747706a0 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -75,6 +75,93 @@ void U_Srand( unsigned int seed ) glSeed = seed_table[seed & 0xff]; } +/* +===================== +UTIL_Intersect - moved here from controller.cpp and priest.cpp +===================== +*/ + +Vector UTIL_Intersect( Vector vecSrc, Vector vecDst, Vector vecMove, float flSpeed ) +{ + Vector vecTo = vecDst - vecSrc; + + float a = DotProduct( vecMove, vecMove ) - flSpeed * flSpeed; + float b = 0 * DotProduct(vecTo, vecMove); // why does this work? + float c = DotProduct( vecTo, vecTo ); + + float t; + if (a == 0) + { + t = c / (flSpeed * flSpeed); + } + else + { + t = b * b - 4 * a * c; + t = sqrt( t ) / (2.0 * a); + float t1 = -b +t; + float t2 = -b -t; + + if (t1 < 0 || t2 < t1) + t = t2; + else + t = t1; + } + + // ALERT( at_console, "Intersect %f\n", t ); + + if (t < 0.1) + t = 0.1; + if (t > 10.0) + t = 10.0; + + Vector vecHit = vecTo + vecMove * t; + return vecHit.Normalize( ) * flSpeed; +} + +void FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity ) +{ + int i, j, k; + float distance; + float *minmaxs[2] = {mins, maxs}; + TraceResult tmpTrace; + Vector vecHullEnd = tr.vecEndPos; + Vector vecEnd; + + distance = 1e6f; + + vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2); + UTIL_TraceLine( vecSrc, vecHullEnd, dont_ignore_monsters, pEntity, &tmpTrace ); + if ( tmpTrace.flFraction < 1.0 ) + { + tr = tmpTrace; + return; + } + + for ( i = 0; i < 2; i++ ) + { + for ( j = 0; j < 2; j++ ) + { + for ( k = 0; k < 2; k++ ) + { + vecEnd.x = vecHullEnd.x + minmaxs[i][0]; + vecEnd.y = vecHullEnd.y + minmaxs[j][1]; + vecEnd.z = vecHullEnd.z + minmaxs[k][2]; + + UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, pEntity, &tmpTrace ); + if ( tmpTrace.flFraction < 1.0 ) + { + float thisDistance = (tmpTrace.vecEndPos - vecSrc).Length(); + if ( thisDistance < distance ) + { + tr = tmpTrace; + distance = thisDistance; + } + } + } + } + } +} + /* ===================== UTIL_SharedRandomLong diff --git a/dlls/util.h b/dlls/util.h index 1fe4d418..f890f95d 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -408,6 +408,7 @@ extern DLL_GLOBAL const Vector g_vecZero; #define AMBIENT_SOUND_LARGERADIUS 8 #define AMBIENT_SOUND_START_SILENT 16 #define AMBIENT_SOUND_NOT_LOOPING 32 +#define AMBIENT_SHOWMESSAGE 512 // Decay: strings code #define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements diff --git a/dlls/weapons.cpp b/dlls/weapons.cpp index 3ff1a15f..a054a5e2 100644 --- a/dlls/weapons.cpp +++ b/dlls/weapons.cpp @@ -396,6 +396,7 @@ TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ), // DEFINE_FIELD( CBasePlayerItem, m_iIdPrimary, FIELD_INTEGER ), // DEFINE_FIELD( CBasePlayerItem, m_iIdSecondary, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayerItem, DecayPlayerIndex, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating ) @@ -1228,6 +1229,7 @@ TYPEDESCRIPTION CWeaponBox::m_SaveData[] = DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ), DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ), DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ), + DEFINE_FIELD( CWeaponBox, m_iPlayerIndex, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity ) @@ -1264,6 +1266,9 @@ void CWeaponBox::Spawn( void ) { Precache(); + if (!m_iPlayerIndex) + m_iPlayerIndex = 0; + pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; @@ -1334,6 +1339,13 @@ void CWeaponBox::Touch( CBaseEntity *pOther ) } CBasePlayer *pPlayer = (CBasePlayer *)pOther; + + if (m_iPlayerIndex != 0) + { + if (pPlayer->m_iDecayId != this->m_iPlayerIndex) + return; + } + int i; // dole out ammo diff --git a/dlls/weapons.h b/dlls/weapons.h index 4db8381f..e638af5e 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -78,6 +78,8 @@ public: #define WEAPON_TRIPMINE 13 #define WEAPON_SATCHEL 14 #define WEAPON_SNARK 15 +#define WEAPON_DISPLACER 16 +#define WEAPON_VORTI 17 #define WEAPON_ALLWEAPONS (~(1<SetAlienMode( bSlaveCoop ); + + CBaseEntity *pEntity; + pEntity = CBaseEntity::Create( "trigger_autobot", g_vecZero, g_vecZero, edict() ); + if ( !pEntity ) + ALERT( at_aiconsole, "Autobot entity was not created!\n"); + } + //!!!UNDONE why is there so much Spawn code in the Precache function? I'll just keep it here ///!!!LATER - do we want a sound ent in deathmatch? (sjb) @@ -713,6 +732,30 @@ void CWorld::KeyValue( KeyValueData *pkvd ) } pkvd->fHandled = TRUE; } + else if ( FStrEq(pkvd->szKeyName, "startsuit") ) + { + if ( atoi(pkvd->szValue) ) + { + g_startSuit = atoi(pkvd->szValue); + } + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "decay") ) + { + if ( atoi(pkvd->szValue) ) + { + bDecay = atoi(pkvd->szValue); + } + pkvd->fHandled = TRUE; + } + else if ( FStrEq(pkvd->szKeyName, "slavecoop") ) + { + if ( atoi(pkvd->szValue) ) + { + m_bSlaveCoop = atoi(pkvd->szValue); + } + pkvd->fHandled = TRUE; + } else CBaseEntity::KeyValue( pkvd ); } diff --git a/dlls/xen.cpp b/dlls/xen.cpp index 847f26f7..a02b4847 100644 --- a/dlls/xen.cpp +++ b/dlls/xen.cpp @@ -278,7 +278,6 @@ void CXenTree::Spawn( void ) SET_MODEL( ENT( pev ), "models/tree.mdl" ); pev->movetype = MOVETYPE_NONE; pev->solid = SOLID_BBOX; - pev->takedamage = DAMAGE_YES; UTIL_SetSize( pev, Vector( -30, -30, 0 ), Vector( 30, 30, 188 ) ); diff --git a/pm_shared/pm_shared.c b/pm_shared/pm_shared.c index 922c7ca6..e520238c 100644 --- a/pm_shared/pm_shared.c +++ b/pm_shared/pm_shared.c @@ -1969,6 +1969,15 @@ void PM_UnDuck( void ) newOrigin[i] += ( pmove->player_mins[1][i] - pmove->player_mins[0][i] ); } } + */ + if ( pmove->onground != -1 && pmove->flags & FL_DUCKING && pmove->bInDuck == false) + { + for ( i = 0; i < 3; i++ ) + { + newOrigin[i] += ( pmove->player_mins[1][i] - pmove->player_mins[0][i] ); + } + } + trace = pmove->PM_PlayerTrace( newOrigin, newOrigin, PM_NORMAL, -1 );