From 01a416c5a7e0fd21e20fa09b2005b83c98a55917 Mon Sep 17 00:00:00 2001 From: Night Owl Date: Sat, 11 Nov 2017 02:40:36 +0500 Subject: [PATCH] Add weapon_eagle, weapon_displacer, monster_otis and monster_gonome implementations. --- cl_dll/CMakeLists.txt | 2 + cl_dll/ev_hldm.cpp | 94 +++++ cl_dll/hl/hl_events.cpp | 4 + cl_dll/hl/hl_weapons.cpp | 10 + dlls/CAd/displacer.cpp | 708 ++++++++++++++++++++++++++++++++ dlls/CAd/eagle.cpp | 299 ++++++++++++++ dlls/CAd/gonome.cpp | 863 +++++++++++++++++++++++++++++++++++++++ dlls/CAd/otis.cpp | 479 ++++++++++++++++++++++ dlls/CMakeLists.txt | 4 + dlls/barney.cpp | 49 +-- dlls/barney.h | 71 ++++ dlls/bullsquid.cpp | 56 +-- dlls/bullsquid.h | 82 ++++ dlls/player.cpp | 5 + dlls/player.h | 2 +- dlls/util.cpp | 32 ++ dlls/util.h | 2 + dlls/weapons.cpp | 21 + dlls/weapons.h | 95 +++++ 19 files changed, 2774 insertions(+), 104 deletions(-) create mode 100644 dlls/CAd/displacer.cpp create mode 100644 dlls/CAd/eagle.cpp create mode 100644 dlls/CAd/gonome.cpp create mode 100644 dlls/CAd/otis.cpp create mode 100644 dlls/barney.h create mode 100644 dlls/bullsquid.h diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index 7fffb29b..dffc5142 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -43,6 +43,8 @@ set (CLDLL_SOURCES ../dlls/squeakgrenade.cpp ../dlls/tripmine.cpp ../dlls/glock.cpp + ../dlls/CAd/displacer.cpp + ../dlls/CAd/eagle.cpp ev_hldm.cpp hl/hl_baseentity.cpp hl/hl_events.cpp diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 628b5bfa..4009880d 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -68,6 +68,8 @@ void EV_EgonStop( struct event_args_s *args ); void EV_HornetGunFire( struct event_args_s *args ); void EV_TripmineFire( struct event_args_s *args ); void EV_SnarkFire( struct event_args_s *args ); +void EV_Displacer( struct event_args_s *args ); +void EV_FireEagle( struct event_args_s *args ); void EV_TrainPitchAdjust( struct event_args_s *args ); } @@ -1690,6 +1692,98 @@ void EV_SnarkFire( event_args_t *args ) // SQUEAK END //====================== +//====================== +// DISPLACER START +//====================== +enum displacer_e +{ + DISPLACER_IDLE1 = 0, + DISPLACER_IDLE2, + DISPLACER_SPINUP, + DISPLACER_SPIN, + DISPLACER_FIRE, + DISPLACER_DRAW, + DISPLACER_HOLSTER +}; + +void EV_Displacer( event_args_t *args ) +{ + int idx; + vec3_t origin; + + idx = args->entindex; + VectorCopy( args->origin, origin ); + + if( EV_IsLocal( idx ) ) + { + gEngfuncs.pEventAPI->EV_WeaponAnimation( DISPLACER_FIRE, 0 ); + V_PunchAxis( 0, -2.0 ); + } + + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/displacer_fire.wav", 1, ATTN_NORM, 0, PITCH_NORM ); +} +//====================== +// DISPLACER END +//====================== + +//====================== +// DESERT EAGLE START +//====================== +enum eagle_e +{ + EAGLE_IDLE1 = 0, + EAGLE_IDLE2, + EAGLE_IDLE3, + EAGLE_IDLE4, + EAGLE_IDLE5, + EAGLE_SHOOT, + EAGLE_SHOOT_EMPTY, + EAGLE_RELOAD, + EAGLE_RELOAD_NOT_EMPTY, + EAGLE_DRAW, + EAGLE_HOLSTER +}; + +void EV_FireEagle( event_args_t *args ) +{ + int idx; + vec3_t origin; + vec3_t angles; + vec3_t velocity; + + vec3_t vecSrc, vecAiming; + vec3_t up, right, forward; + float flSpread = 0.01; + + idx = args->entindex; + VectorCopy( args->origin, origin ); + VectorCopy( args->angles, angles ); + VectorCopy( args->velocity, velocity ); + + AngleVectors( angles, forward, right, up ); + + if( EV_IsLocal( idx ) ) + { + // Add muzzle flash to current weapon model + EV_MuzzleFlash(); + gEngfuncs.pEventAPI->EV_WeaponAnimation( EAGLE_SHOOT, 1 ); + + V_PunchAxis( 0, -4.0 ); + } + + // Play fire sound. + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/desert_eagle_fire.wav", gEngfuncs.pfnRandomFloat(0.8, 0.9), ATTN_NORM, 0, PITCH_NORM ); + + EV_GetGunPosition( args, vecSrc, origin ); + + VectorCopy( forward, vecAiming ); + + EV_HLDM_FireBullets( idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_357, 0, 0, args->fparam1, args->fparam2 ); +} +//====================== +// DESERT EAGLE END +//====================== + void EV_TrainPitchAdjust( event_args_t *args ) { int idx; diff --git a/cl_dll/hl/hl_events.cpp b/cl_dll/hl/hl_events.cpp index c79279dd..33ddeca6 100644 --- a/cl_dll/hl/hl_events.cpp +++ b/cl_dll/hl/hl_events.cpp @@ -38,6 +38,8 @@ void EV_EgonStop( struct event_args_s *args ); void EV_HornetGunFire( struct event_args_s *args ); void EV_TripmineFire( struct event_args_s *args ); void EV_SnarkFire( struct event_args_s *args ); +void EV_Displacer( struct event_args_s *args ); +void EV_FireEagle( struct event_args_s *args ); void EV_TrainPitchAdjust( struct event_args_s *args ); } @@ -76,4 +78,6 @@ void Game_HookEvents( void ) gEngfuncs.pfnHookEvent( "events/firehornet.sc", EV_HornetGunFire ); gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire ); gEngfuncs.pfnHookEvent( "events/snarkfire.sc", EV_SnarkFire ); + gEngfuncs.pfnHookEvent( "events/displacer.sc", EV_Displacer ); + gEngfuncs.pfnHookEvent( "events/eagle.sc", EV_FireEagle ); } diff --git a/cl_dll/hl/hl_weapons.cpp b/cl_dll/hl/hl_weapons.cpp index 75161a9e..ac8307c2 100644 --- a/cl_dll/hl/hl_weapons.cpp +++ b/cl_dll/hl/hl_weapons.cpp @@ -67,6 +67,8 @@ CHandGrenade g_HandGren; CSatchel g_Satchel; CTripmine g_Tripmine; CSqueak g_Snark; +CDisplacer g_Displacer; +CEagle g_Eagle; /* ====================== @@ -616,6 +618,8 @@ void HUD_InitClientWeapons( void ) HUD_PrepEntity( &g_Satchel, &player ); HUD_PrepEntity( &g_Tripmine, &player ); HUD_PrepEntity( &g_Snark, &player ); + HUD_PrepEntity( &g_Displacer, &player ); + HUD_PrepEntity( &g_Eagle, &player ); } /* @@ -721,6 +725,12 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm case WEAPON_SNARK: pWeapon = &g_Snark; break; + case WEAPON_DISPLACER: + pWeapon = &g_Displacer; + break; + case WEAPON_EAGLE: + pWeapon = &g_Eagle; + break; } // Store pointer to our destination entity_state_t so we can get our origin, etc. from it diff --git a/dlls/CAd/displacer.cpp b/dlls/CAd/displacer.cpp new file mode 100644 index 00000000..30ab58e4 --- /dev/null +++ b/dlls/CAd/displacer.cpp @@ -0,0 +1,708 @@ +/*** +* +* Copyright (c) 1996-2001, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "weapons.h" +#include "monsters.h" +#include "player.h" +#include "gamerules.h" +#include "shake.h" + +#ifndef CLIENT_DLL + +extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ); + +LINK_ENTITY_TO_CLASS(info_displacer_xen_target, CPointEntity); +LINK_ENTITY_TO_CLASS(info_displacer_earth_target, CPointEntity); + +int iPortalSprite = 0; +int iRingSprite = 0; +//========================================================= +// Displacement field +//========================================================= +class CDisplacerBall : public CBaseEntity +{ +public: + void Spawn( void ); + + static void Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles); + static void SelfCreate(entvars_t *pevOwner, Vector vecStart); + + void Touch(CBaseEntity *pOther); + void EXPORT ExplodeThink( void ); + void EXPORT KillThink( void ); + void Circle( void ); + + virtual int Save(CSave &save); + virtual int Restore(CRestore &restore); + static TYPEDESCRIPTION m_SaveData[]; + + CBeam* m_pBeam[8]; + + void EXPORT FlyThink( void ); + void ClearBeams( void ); + void ArmBeam( int iSide ); + + int m_iBeams; + + CBaseEntity *pRemoveEnt; +}; + +LINK_ENTITY_TO_CLASS(displacer_ball, CDisplacerBall) + +TYPEDESCRIPTION CDisplacerBall::m_SaveData[] = +{ + DEFINE_FIELD(CDisplacerBall, m_iBeams, FIELD_INTEGER), + DEFINE_ARRAY(CDisplacerBall, m_pBeam, FIELD_CLASSPTR, 8), +}; + +IMPLEMENT_SAVERESTORE(CDisplacerBall, CBaseEntity); + +void CDisplacerBall::Spawn(void) +{ + pev->movetype = MOVETYPE_FLY; + pev->classname = MAKE_STRING("portal"); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAdd; + pev->renderamt = 255; + + SET_MODEL(ENT(pev), "sprites/exit1.spr"); + pev->frame = 0; + pev->scale = 0.75; + + SetTouch( &CDisplacerBall::Touch ); + SetThink( &CDisplacerBall::FlyThink ); + pev->nextthink = gpGlobals->time + 0.2; + UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); + + m_iBeams = 0; +} + +void CDisplacerBall::FlyThink() +{ + ArmBeam( -1 ); + ArmBeam( 1 ); + pev->nextthink = gpGlobals->time + 0.05; +} + +void CDisplacerBall::ArmBeam( int iSide ) +{ + //This method is identical to the Alien Slave's ArmBeam, except it treats m_pBeam as a circular buffer. + if( m_iBeams > 7 ) + m_iBeams = 0; + + TraceResult tr; + float flDist = 1.0; + + UTIL_MakeAimVectors( pev->angles ); + Vector vecSrc = gpGlobals->v_forward * 32.0 + iSide * gpGlobals->v_right * 16.0 + gpGlobals->v_up * 36.0 + pev->origin; + Vector vecAim = gpGlobals->v_up * RANDOM_FLOAT( -1.0, 1.0 ); + Vector vecEnd = (iSide * gpGlobals->v_right * RANDOM_FLOAT( 0.0, 1.0 ) + vecAim) * 512.0 + vecSrc; + UTIL_TraceLine( &vecSrc.x, &vecEnd.x, dont_ignore_monsters, ENT( pev ), &tr ); + + if( flDist > tr.flFraction ) + flDist = tr.flFraction; + + // Couldn't find anything close enough + if( flDist == 1.0 ) + return; + + // The beam might already exist if we've created all beams before. + if( !m_pBeam[ m_iBeams ] ) + m_pBeam[ m_iBeams ] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + + if( !m_pBeam[ m_iBeams ] ) + return; + + CBaseEntity* pHit = Instance( tr.pHit ); + + entvars_t *pevOwner; + if ( pev->owner ) + pevOwner = VARS( pev->owner ); + else + pevOwner = NULL; + + if( pHit && pHit->pev->takedamage != DAMAGE_NO ) + { + //Beam hit something, deal radius damage to it + m_pBeam[ m_iBeams ]->EntsInit( pHit->entindex(), entindex() ); + m_pBeam[ m_iBeams ]->SetColor( 255, 255, 255 ); + m_pBeam[ m_iBeams ]->SetBrightness( 255 ); + m_pBeam[ m_iBeams ]->SetNoise( 10 ); + + RadiusDamage( tr.vecEndPos, pev, pevOwner, 25, 15, CLASS_NONE, DMG_ENERGYBEAM ); + } + else + { + m_pBeam[ m_iBeams ]->PointEntInit( tr.vecEndPos, entindex() ); + m_pBeam[ m_iBeams ]->SetColor( 96, 128, 16 ); + m_pBeam[ m_iBeams ]->SetBrightness( 255 ); + m_pBeam[ m_iBeams ]->SetNoise( 80 ); + } + m_iBeams++; +} + +void CDisplacerBall::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles ) +{ + CDisplacerBall *pSpit = GetClassPtr((CDisplacerBall *)NULL); + pSpit->Spawn(); + UTIL_SetOrigin(pSpit->pev, vecStart); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->angles = vecAngles; + pSpit->pev->owner = ENT(pevOwner); +} + +void CDisplacerBall::SelfCreate(entvars_t *pevOwner,Vector vecStart) +{ + CDisplacerBall *pSelf = GetClassPtr((CDisplacerBall *)NULL); + pSelf->Spawn(); + pSelf->ClearBeams(); + UTIL_SetOrigin(pSelf->pev, vecStart); + + pSelf->pev->owner = ENT(pevOwner); + pSelf->Circle(); + pSelf->SetTouch( NULL ); + pSelf->SetThink(&CDisplacerBall::KillThink); + pSelf->pev->nextthink = gpGlobals->time + ( g_pGameRules->IsMultiplayer() ? 0.2f : 0.5f ); +} + +void CDisplacerBall::Touch(CBaseEntity *pOther) +{ + // Do not collide with the owner. + if (ENT(pOther->pev) == pev->owner || (ENT(pOther->pev) == VARS(pev->owner)->owner)) + return; + + TraceResult tr; + Vector vecSpot; + Vector vecSrc; + pev->enemy = pOther->edict(); + CBaseEntity *pTarget = NULL; + + if (!pOther->pev->takedamage) + { + //EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/displacer_impact.wav", 1, ATTN_NORM, 0, 100); + //UTIL_MuzzleLight( pev->origin, 160.0f, 255, 180, 96, 1.0f, 100.0f ); + } + + if( ( g_pGameRules->IsMultiplayer() && !g_pGameRules->IsCoOp() ) && pOther->IsPlayer() ) + { + CBasePlayer *pPlayer = (CBasePlayer *)pOther; + pTarget = GetClassPtr( (CBaseEntity *)VARS( EntSelectSpawnPoint( pPlayer ) ) ); + + if( pTarget ) + { + UTIL_ScreenFade( pPlayer, Vector( 0, 200, 0 ), 0.5, 0.5, 255, FFADE_IN ); + Vector tmp = pTarget->pev->origin; + UTIL_CleanSpawnPoint( tmp, 100 ); + + EMIT_SOUND( pPlayer->edict(), CHAN_BODY, "weapons/displacer_self.wav", 1, ATTN_NORM ); + + // make origin adjustments (origin in center, not at feet) + tmp.z -= pPlayer->pev->mins.z + 36; + tmp.z++; + + pPlayer->pev->flags &= ~FL_ONGROUND; + + UTIL_SetOrigin( pPlayer->pev, tmp ); + + pPlayer->pev->angles = pTarget->pev->angles; + + pPlayer->pev->v_angle = pTarget->pev->angles; + + pPlayer->pev->fixangle = TRUE; + + pPlayer->pev->velocity = pOther->pev->basevelocity = g_vecZero; + } + } + + pev->movetype = MOVETYPE_NONE; + + Circle(); + + SetThink(&CDisplacerBall::KillThink); + pev->nextthink = gpGlobals->time + ( g_pGameRules->IsMultiplayer() ? 0.2f : 0.5f ); +} + +void CDisplacerBall::Circle( void ) +{ + // portal circle + MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, pev->origin); + WRITE_BYTE(TE_BEAMCYLINDER); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z + 800); // reach damage radius over .2 seconds + WRITE_SHORT(iRingSprite); + WRITE_BYTE(0); // startframe + WRITE_BYTE(0); // framerate + WRITE_BYTE(3); // life + WRITE_BYTE(16); // width + WRITE_BYTE(0); // noise + WRITE_BYTE(255); // r, g, b + WRITE_BYTE(255); // r, g, b + WRITE_BYTE(255); // r, g, b + WRITE_BYTE(255); // brightness + WRITE_BYTE(0); // speed + MESSAGE_END(); + UTIL_MuzzleLight( pev->origin, 160.0f, 255, 180, 96, 1.0f, 100.0f ); +} + +void CDisplacerBall::KillThink( void ) +{ + if( pRemoveEnt ) + UTIL_Remove( pRemoveEnt ); + SetThink( &CDisplacerBall::ExplodeThink ); + pev->nextthink = gpGlobals->time + 0.2f; +} + +void CDisplacerBall::ExplodeThink( void ) +{ + ClearBeams(); + + pev->effects |= EF_NODRAW; + + EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/displacer_teleport.wav", VOL_NORM, ATTN_NORM); + + entvars_t *pevOwner; + if ( pev->owner ) + pevOwner = VARS( pev->owner ); + else + pevOwner = NULL; + pev->owner = NULL; + + UTIL_Remove( this ); + + ::RadiusDamage( pev->origin, pev, pevOwner, 300, 300, CLASS_NONE, DMG_ENERGYBEAM ); +} + +void CDisplacerBall::ClearBeams( void ) +{ + for( int i = 0;i < 8; i++ ) + { + if( m_pBeam[i] ) + { + UTIL_Remove( m_pBeam[i] ); + m_pBeam[i] = NULL; + } + } +} + + +#endif // !defined ( CLIENT_DLL ) + +enum displacer_e { + DISPLACER_IDLE1 = 0, + DISPLACER_IDLE2, + DISPLACER_SPINUP, + DISPLACER_SPIN, + DISPLACER_FIRE, + DISPLACER_DRAW, + DISPLACER_HOLSTER, +}; + +#define DISPLACER_SECONDARY_USAGE 60 +#define DISPLACER_PRIMARY_USAGE 20 + +LINK_ENTITY_TO_CLASS(weapon_displacer, CDisplacer); + +//========================================================= +// Purpose: +//========================================================= +int CDisplacer::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "uranium"; + p->iMaxAmmo1 = URANIUM_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = WEAPON_NOCLIP; + p->iFlags = 0; + p->iSlot = 3; + p->iPosition = 4; + p->iId = m_iId = WEAPON_DISPLACER; + p->iWeight = DISPLACER_WEIGHT; + + return 1; +} + +//========================================================= +// Purpose: +//========================================================= +int CDisplacer::AddToPlayer(CBasePlayer *pPlayer) +{ + if (CBasePlayerWeapon::AddToPlayer(pPlayer)) + { + MESSAGE_BEGIN(MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev); + WRITE_BYTE(m_iId); + MESSAGE_END(); + return TRUE; + } + return FALSE; +} + +//========================================================= +// Purpose: +//========================================================= +BOOL CDisplacer::PlayEmptySound(void) +{ + if (m_iPlayEmptySound) + { + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "buttons/button11.wav", 1, ATTN_NORM); + m_iPlayEmptySound = 0; + return 0; + } + return 0; +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::Spawn() +{ + Precache(); + m_iId = WEAPON_DISPLACER; + SET_MODEL(ENT(pev), "models/w_displacer.mdl"); + + m_iDefaultAmmo = DISPLACER_DEFAULT_GIVE; + + FallInit();// get ready to fall down. +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::Precache(void) +{ + PRECACHE_MODEL("models/v_displacer.mdl"); + PRECACHE_MODEL("models/w_displacer.mdl"); + PRECACHE_MODEL("models/p_displacer.mdl"); + + PRECACHE_SOUND("items/9mmclip1.wav"); + + PRECACHE_SOUND("weapons/displacer_fire.wav"); + PRECACHE_SOUND("weapons/displacer_self.wav"); + PRECACHE_SOUND("weapons/displacer_spin.wav"); + PRECACHE_SOUND("weapons/displacer_spin2.wav"); + PRECACHE_SOUND("weapons/displacer_teleport.wav"); + + PRECACHE_SOUND("buttons/button11.wav"); + PRECACHE_SOUND("buttons/button10.wav"); + + PRECACHE_MODEL("sprites/lgtning.spr"); + +#ifndef CLIENT_DLL + iPortalSprite = PRECACHE_MODEL("sprites/exit1.spr"); + iRingSprite = PRECACHE_MODEL("sprites/displacer_ring.spr"); +#endif + + UTIL_PrecacheOther("displacer_ball"); + + m_usDisplacer = PRECACHE_EVENT(1, "events/displacer.sc"); +} + +//========================================================= +// Purpose: +//========================================================= +BOOL CDisplacer::Deploy() +{ + return DefaultDeploy("models/v_displacer.mdl", "models/p_displacer.mdl", DISPLACER_DRAW, "displacer", UseDecrement()); +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::Holster(int skiplocal /* = 0 */) +{ + m_fInReload = FALSE;// cancel any reload in progress. + + ClearBeams(); + ClearSpin(); + + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0f; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0f; + SendWeaponAnim(DISPLACER_HOLSTER); +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::SecondaryAttack(void) +{ + if (m_fFireOnEmpty || !CanFireDisplacer(DISPLACER_SECONDARY_USAGE)) + { + PlayEmptySound(); + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.3f; + return; + } + + m_pPlayer->SetAnimation(PLAYER_ATTACK1); + + m_iFireMode = FIREMODE_BACKWARD; + + SetThink (&CDisplacer::SpinUp); + + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 4.0; + pev->nextthink = gpGlobals->time; +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::PrimaryAttack() +{ + if ( m_fFireOnEmpty || !CanFireDisplacer(DISPLACER_PRIMARY_USAGE)) + { + PlayEmptySound(); + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.3f; + return; + } + m_iFireMode = FIREMODE_FORWARD; + + SetThink (&CDisplacer::SpinUp); + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.6; + pev->nextthink = gpGlobals->time; +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::WeaponIdle(void) +{ + ResetEmptySound(); + + m_pPlayer->GetAutoaimVector(AUTOAIM_2DEGREES); + + if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) + return; + + int iAnim; + float flRand = UTIL_SharedRandomFloat(m_pPlayer->random_seed, 0, 1); + if (flRand <= 0.5) + { + iAnim = DISPLACER_IDLE1; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0f; + } + else + { + iAnim = DISPLACER_IDLE2; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0f; + } + + SendWeaponAnim(iAnim, UseDecrement()); +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::ClearSpin( void ) +{ + + switch (m_iFireMode) + { + case FIREMODE_FORWARD: + STOP_SOUND(ENT(pev), CHAN_WEAPON, "weapons/displacer_spin.wav"); + break; + case FIREMODE_BACKWARD: + STOP_SOUND(ENT(pev), CHAN_WEAPON, "weapons/displacer_spin2.wav"); + break; + } +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::SpinUp( void ) +{ + SendWeaponAnim( DISPLACER_SPINUP, UseDecrement()); + + LightningEffect(); + + if( m_iFireMode == FIREMODE_FORWARD ) + { + EMIT_SOUND( edict(), CHAN_WEAPON, "weapons/displacer_spin.wav", 1, ATTN_NORM ); + SetThink (&CDisplacer::Displace); + } + else + { + EMIT_SOUND( edict(), CHAN_WEAPON, "weapons/displacer_spin2.wav", 1, ATTN_NORM ); + SetThink (&CDisplacer::Teleport); + } + pev->nextthink = gpGlobals->time + 0.9; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.3; +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::Displace( void ) +{ + ClearBeams(); + ClearSpin(); + + SendWeaponAnim( DISPLACER_FIRE, UseDecrement()); + EMIT_SOUND( edict(), CHAN_WEAPON, "weapons/displacer_fire.wav", 1, ATTN_NORM ); + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + m_pPlayer->pev->punchangle.x -= 2; +#ifndef CLIENT_DLL + Vector vecSrc; + UseAmmo(DISPLACER_PRIMARY_USAGE); + + UTIL_MakeVectors(m_pPlayer->pev->v_angle); + + vecSrc = m_pPlayer->GetGunPosition(); + vecSrc = vecSrc + gpGlobals->v_forward * 22; + vecSrc = vecSrc + gpGlobals->v_right * 8; + vecSrc = vecSrc + gpGlobals->v_up * -12; + + CDisplacerBall::Shoot( m_pPlayer->pev, vecSrc, gpGlobals->v_forward * 500, m_pPlayer->pev->v_angle ); + + SetThink( NULL ); +#endif +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::Teleport( void ) +{ + const char *pszName; + ClearBeams(); + ClearSpin(); +#ifndef CLIENT_DLL + CBaseEntity *pTarget = NULL; + Vector tmp( 0, 0, 0 ); + + if( g_pGameRules->IsMultiplayer() && !g_pGameRules->IsCoOp() ) + { + pTarget = GetClassPtr( (CBaseEntity *)VARS( EntSelectSpawnPoint( m_pPlayer ) ) ); + } + else + { + if( !m_pPlayer->m_fInXen ) + pszName = "info_displacer_xen_target"; + else + pszName = "info_displacer_earth_target"; + pTarget = UTIL_FindEntityByClassname( 0, pszName ); + } + + if( pTarget ) + tmp = pTarget->pev->origin; + + if( pTarget && /*HACK*/( tmp != Vector( 0, 0, 0 )/*HACK*/ ) ) + { + UTIL_ScreenFade( m_pPlayer, Vector( 0, 200, 0 ), 0.5, 0.5, 255, FFADE_IN ); + m_flTimeWeaponIdle = UTIL_WeaponTimeBase(); + + UseAmmo(DISPLACER_SECONDARY_USAGE); + + UTIL_CleanSpawnPoint( tmp, 50 ); + + EMIT_SOUND( edict(), CHAN_BODY, "weapons/displacer_self.wav", 1, ATTN_NORM ); + CDisplacerBall::SelfCreate(m_pPlayer->pev, m_pPlayer->pev->origin); + + // make origin adjustments (origin in center, not at feet) + tmp.z -= m_pPlayer->pev->mins.z + 36; + tmp.z++; + + m_pPlayer->pev->flags &= ~FL_ONGROUND; + + UTIL_SetOrigin(m_pPlayer->pev, tmp); + + m_pPlayer->pev->angles = pTarget->pev->angles; + + m_pPlayer->pev->v_angle = pTarget->pev->angles; + + m_pPlayer->pev->fixangle = TRUE; + m_pPlayer->pev->velocity = m_pPlayer->pev->basevelocity = g_vecZero; + + if( !g_pGameRules->IsMultiplayer()) + { + m_pPlayer->m_fInXen = !m_pPlayer->m_fInXen; + /*if (m_pPlayer->m_fInXen) + m_pPlayer->pev->gravity = 0.5; + else + m_pPlayer->pev->gravity = 1.0;*/ + } + } + else + { + EMIT_SOUND( edict(), CHAN_BODY, "buttons/button10.wav", 1, ATTN_NORM ); + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 3.0; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.9; + } + + SetThink( NULL ); +#endif +} + +void CDisplacer::LightningEffect( void ) +{ +#ifndef CLIENT_DLL + int m_iBeams = 0; + + for( int i = 2; i < 5; ++i ) + { + if( !m_pBeam[m_iBeams] ) + m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 16 ); + m_pBeam[m_iBeams]->EntsInit( m_pPlayer->entindex(), m_pPlayer->entindex() ); + m_pBeam[m_iBeams]->SetStartAttachment( i ); + m_pBeam[m_iBeams]->SetEndAttachment( i == 4 ? i - 2 : i + 1 ); + m_pBeam[m_iBeams]->SetColor( 96, 128, 16 ); + m_pBeam[m_iBeams]->SetBrightness( 240 ); + m_pBeam[m_iBeams]->SetNoise( 60 ); + m_pBeam[m_iBeams]->SetScrollRate( 30 ); + m_pBeam[m_iBeams]->pev->scale = 10; + m_iBeams++; + } +#endif +} + +void CDisplacer::ClearBeams( void ) +{ +#ifndef CLIENT_DLL + for( int i = 0; i < 3; i++ ) + { + if( m_pBeam[i] ) + { + UTIL_Remove( m_pBeam[i] ); + m_pBeam[i] = NULL; + } + } +#endif +} + +//========================================================= +// Purpose: +//========================================================= +void CDisplacer::UseAmmo(int count) +{ + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= count; +} + +//========================================================= +// Purpose: +//========================================================= +BOOL CDisplacer::CanFireDisplacer( int count ) const +{ + return m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] >= count; +} diff --git a/dlls/CAd/eagle.cpp b/dlls/CAd/eagle.cpp new file mode 100644 index 00000000..c861243c --- /dev/null +++ b/dlls/CAd/eagle.cpp @@ -0,0 +1,299 @@ +//========= Copyright © 2004-2008, Raven City Team, All rights reserved. ============// +// // +// Purpose: // +// // +// $NoKeywords: $ // +//===================================================================================// +//Modifided by Lost_Gamer_ + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "player.h" + +enum deagle_e { + DEAGLE_IDLE1 = 0, + DEAGLE_IDLE2, + DEAGLE_IDLE3, + DEAGLE_IDLE4, + DEAGLE_IDLE5, + DEAGLE_SHOOT, + DEAGLE_SHOOT_EMPTY, + DEAGLE_RELOAD, + DEAGLE_RELOAD_NOT_EMPTY, + DEAGLE_DRAW, + DEAGLE_HOLSTER +}; +LINK_ENTITY_TO_CLASS( weapon_eagle, CEagle ) + +#ifndef CLIENT_DLL +LINK_ENTITY_TO_CLASS( eagle_laser, CLaserSpot ) +#endif + +void CEagle::Spawn( void ) +{ + + pev->classname = MAKE_STRING("weapon_eagle"); // hack to allow for old names + Precache( ); + m_iId = WEAPON_EAGLE; + SET_MODEL(ENT(pev), "models/w_desert_eagle.mdl"); + + m_iDefaultAmmo = EAGLE_DEFAULT_GIVE; + + FallInit();// get ready to fall down. +} + + +void CEagle::Precache( void ) +{ + UTIL_PrecacheOther( "eagle_laser" ); + PRECACHE_MODEL("models/v_desert_eagle.mdl"); + PRECACHE_MODEL("models/w_desert_eagle.mdl"); + PRECACHE_MODEL("models/p_desert_eagle.mdl"); + m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell + + PRECACHE_SOUND ("weapons/desert_eagle_reload.wav"); + PRECACHE_SOUND ("weapons/desert_eagle_fire.wav"); + PRECACHE_SOUND ("weapons/desert_eagle_sight.wav"); + PRECACHE_SOUND ("weapons/desert_eagle_sight2.wav"); + + m_usEagle = PRECACHE_EVENT( 1, "events/eagle.sc" ); +} + +int CEagle::GetItemInfo(ItemInfo *p) +{ + p->pszName = STRING(pev->classname); + p->pszAmmo1 = "357"; + p->iMaxAmmo1 = _357_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = EAGLE_MAX_CLIP; + p->iSlot = 1; + p->iPosition = 2; + p->iFlags = 0; + p->iId = m_iId = WEAPON_EAGLE; + p->iWeight = EAGLE_WEIGHT; + + return 1; +} + +BOOL CEagle::Deploy( ) +{ + return DefaultDeploy( "models/v_desert_eagle.mdl", "models/p_desert_eagle.mdl", DEAGLE_DRAW, "onehanded", 0 ); +} + +void CEagle::Holster( int skiplocal /* = 0 */ ) +{ + m_fInReload = FALSE;// cancel any reload in progress. + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; + SendWeaponAnim( DEAGLE_HOLSTER ); + + if (m_pEagleLaser) + { + m_pEagleLaser->Killed( NULL, GIB_NEVER ); + m_pEagleLaser = NULL; + } +} + +void CEagle::SecondaryAttack() +{ + m_fEagleLaserActive = ! m_fEagleLaserActive; + if (!m_fEagleLaserActive && m_pEagleLaser) + { + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/desert_eagle_sight2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM); + m_pEagleLaser->Killed( NULL, GIB_NORMAL ); + m_pEagleLaser = NULL; + } + else + { + EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/desert_eagle_sight.wav", 1.0, ATTN_NORM, 0, PITCH_NORM); + } + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5; +} + +void CEagle::PrimaryAttack() +{ + if (m_iClip <= 0) + { + if (m_fFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.2; + } + return; + } + + // don't fire underwater + if (m_pPlayer->pev->waterlevel == 3) + { + UpdateSpot( ); + PlayEmptySound( ); + m_flNextPrimaryAttack = UTIL_WeaponTimeBase()+ 0.15; + return; + } + + UpdateSpot( ); + + float flSpread = 0.01; + + m_iClip--; + + m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; + + int flags; + BOOL m_fLaserOn; + +#if defined( CLIENT_WEAPONS ) + flags = FEV_NOTHOST; +#else + flags = 0; +#endif + + // player "shoot" animation + m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; + m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; + + Vector vecSrc = m_pPlayer->GetGunPosition( ); + Vector vecAiming; + vecAiming = gpGlobals->v_forward; + + Vector vecDir; + if (m_pEagleLaser) + { + vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, Vector( flSpread, flSpread, flSpread ), 8192, BULLET_PLAYER_357, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + m_flNextPrimaryAttack = UTIL_WeaponTimeBase()+ 0.5; +#ifndef CLIENT_DLL + m_pEagleLaser->Suspend( 0.6 ); +#endif + m_fLaserOn = TRUE; + } + else + { + flSpread = 0.1; + vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_10DEGREES, 8192, BULLET_PLAYER_357, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed ); + m_flNextPrimaryAttack = UTIL_WeaponTimeBase()+ 0.22; + m_fLaserOn = FALSE; + } + + PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usEagle, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, flSpread, flSpread, ( m_iClip == 0 ) ? 1 : 0, 0 ); + + if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + // HEV suit - indicate out of ammo condition + m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); +} + + +void CEagle::Reload( void ) +{ + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == EAGLE_MAX_CLIP) + return; + + if ( m_pEagleLaser && m_fEagleLaserActive ) + { +#ifndef CLIENT_DLL + m_pEagleLaser->Suspend( 1.6 ); +#endif + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1.5; + } + + int iResult; + + if (m_iClip == 0) + iResult = DefaultReload( EAGLE_MAX_CLIP, DEAGLE_RELOAD, 1.5 ); + else + iResult = DefaultReload( EAGLE_MAX_CLIP, DEAGLE_RELOAD_NOT_EMPTY, 1.5 ); + + if (iResult) + { + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 ); + } +} + +void CEagle::UpdateSpot( void ) +{ +#ifndef CLIENT_DLL + if (m_fEagleLaserActive) + { + if (!m_pEagleLaser) + { + m_pEagleLaser = CLaserSpot::CreateSpot(); + m_pEagleLaser->pev->classname = MAKE_STRING("eagle_laser"); + m_pEagleLaser->pev->scale = 0.5; + } + + UTIL_MakeVectors( m_pPlayer->pev->v_angle ); + Vector vecSrc = m_pPlayer->GetGunPosition( );; + Vector vecAiming = gpGlobals->v_forward; + + TraceResult tr; + UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr ); + + UTIL_SetOrigin( m_pEagleLaser->pev, tr.vecEndPos ); + + if ( UTIL_PointContents(tr.vecEndPos) == CONTENT_SKY ) + { + UTIL_Remove( m_pEagleLaser ); + m_pEagleLaser = FALSE; + } + + } +#endif +} + + +void CEagle::WeaponIdle( void ) +{ + UpdateSpot( ); + + if (m_flTimeWeaponIdle < UTIL_WeaponTimeBase() ) + { + ResetEmptySound( ); + m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); + + // only idle if the slid isn't back + if (m_iClip != 0) + { + int iAnim; + float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0.0, 1.0 ); + + if (m_pEagleLaser) + { + if (flRand > 0.5 ) + { + iAnim = DEAGLE_IDLE5;//Done + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.0f; + } + else + { + iAnim = DEAGLE_IDLE4;//Done + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.5f; + } + } + else + { + if (flRand <= 0.3 ) + { + iAnim = DEAGLE_IDLE1;//Done + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.5f; + } + else if (flRand <= 0.6 ) + { + iAnim = DEAGLE_IDLE2; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.5f; + } + else + { + iAnim = DEAGLE_IDLE3;//Done + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.63f; + } + } + SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 ); + } + } +} diff --git a/dlls/CAd/gonome.cpp b/dlls/CAd/gonome.cpp new file mode 100644 index 00000000..59e674c6 --- /dev/null +++ b/dlls/CAd/gonome.cpp @@ -0,0 +1,863 @@ +/*** +* +* Copyright (c) 1996-2001, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +//========================================================= +// Gonome.cpp +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "schedule.h" +#include "player.h" +#include "bullsquid.h" +#include "decals.h" +#include "animation.h" +#include "studio.h" + +#define GONOME_SPRINT_DIST 256 // how close the squid has to get before starting to sprint and refusing to swerve + +#define GONOME_TOLERANCE_MELEE1_RANGE 85 +#define GONOME_TOLERANCE_MELEE2_RANGE 65 + +#define GONOME_TOLERANCE_MELEE1_DOT 0.7 +#define GONOME_TOLERANCE_MELEE2_DOT 0.7 + +#define GONOME_MELEE_ATTACK_RADIUS 70 + +enum +{ + TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1 +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +#define GONOME_AE_SLASH_RIGHT ( 1 ) +#define GONOME_AE_SLASH_LEFT ( 2 ) +#define GONOME_AE_THROW ( 4 ) + +#define GONOME_AE_BITE1 ( 19 ) +#define GONOME_AE_BITE2 ( 20 ) +#define GONOME_AE_BITE3 ( 21 ) +#define GONOME_AE_BITE4 ( 22 ) + +//========================================================= +// Gonome's guts projectile +//========================================================= +class CGonomeGuts : public CSquidSpit +{ +public: + void Spawn(void); + static void Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); + void Touch(CBaseEntity *pOther); +}; + +void CGonomeGuts::Spawn() +{ + pev->movetype = MOVETYPE_FLY; + pev->classname = MAKE_STRING( "gonomeguts" ); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAlpha; + pev->renderamt = 255; + + SET_MODEL( ENT( pev ), "sprites/blood_chnk.spr" ); + pev->frame = 0; + pev->scale = 0.5; + + UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); + + m_maxFrame = (float)MODEL_FRAMES( pev->modelindex ) - 1; +} + +void CGonomeGuts::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) +{ + CGonomeGuts *pSpit = GetClassPtr( (CGonomeGuts *)NULL ); + pSpit->Spawn(); + + UTIL_SetOrigin( pSpit->pev, vecStart ); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->owner = ENT( pevOwner ); + + pSpit->SetThink( &CSquidSpit::Animate ); + pSpit->pev->nextthink = gpGlobals->time + 0.1; +} + +void CGonomeGuts::Touch( CBaseEntity *pOther ) +{ + TraceResult tr; + int iPitch; + + // splat sound + iPitch = RANDOM_FLOAT( 90, 110 ); + + EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); + + switch( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } + + if( !pOther->pev->takedamage ) + { + // make a splat on the wall + UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); + UTIL_DecalTrace( &tr, DECAL_BLOOD1 + RANDOM_LONG( 0, 5 ) ); + } + else + { + pOther->TakeDamage( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); + } + + SetThink( &CBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + +//========================================================= +// CGonome +//========================================================= +class CGonome : public CBaseMonster +{ +public: + + void Spawn(void); + void Precache(void); + + int Classify(void); + void SetYawSpeed(); + void HandleAnimEvent(MonsterEvent_t *pEvent); + void IdleSound(void); + void PainSound(void); + void DeathSound(void); + void AlertSound(void); + void StartTask(Task_t *pTask); + + BOOL CheckMeleeAttack1(float flDot, float flDist); + BOOL CheckMeleeAttack2(float flDot, float flDist); + BOOL CheckRangeAttack1(float flDot, float flDist); + void RunAI(void); + Schedule_t *GetSchedule(); + Schedule_t *GetScheduleOfType( int Type ); + + int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + + void SetActivity( Activity NewActivity ); + + int Save(CSave &save); + int Restore(CRestore &restore); + + CUSTOM_SCHEDULES + static TYPEDESCRIPTION m_SaveData[]; + + static const char* pPainSounds[]; + static const char* pIdleSounds[]; + static const char* pDeathSounds[]; +protected: + int GonomeLookupActivity( void *pmodel, int activity ); + float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while. + float m_flNextSpitTime;// last time the gonome used the spit attack. + bool gonnaAttack1; +}; + +LINK_ENTITY_TO_CLASS(monster_gonome, CGonome) + +const char* CGonome::pPainSounds[] = { + "gonome/gonome_pain1.wav", + "gonome/gonome_pain2.wav", + "gonome/gonome_pain3.wav", + "gonome/gonome_pain4.wav" +}; + +const char* CGonome::pIdleSounds[] = { + "gonome/gonome_idle1.wav", + "gonome/gonome_idle2.wav", + "gonome/gonome_idle3.wav" +}; + +const char* CGonome::pDeathSounds[] = { + "gonome/gonome_death2.wav", + "gonome/gonome_death3.wav", + "gonome/gonome_death4.wav" +}; + +TYPEDESCRIPTION CGonome::m_SaveData[] = +{ + DEFINE_FIELD( CGonome, m_flLastHurtTime, FIELD_TIME ), + DEFINE_FIELD( CGonome, m_flNextSpitTime, FIELD_TIME ), +}; + +IMPLEMENT_SAVERESTORE( CGonome, CBaseMonster ) + +/* + * Hack to ignore activity weights when choosing melee attack animation + */ +int CGonome::GonomeLookupActivity( void *pmodel, int activity ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if( !pstudiohdr ) + return 0; + + mstudioseqdesc_t *pseqdesc; + + pseqdesc = (mstudioseqdesc_t *)( (byte *)pstudiohdr + pstudiohdr->seqindex ); + + int sameActivityNum = 0; + for( int i = 0; i < pstudiohdr->numseq && sameActivityNum < 2; i++ ) + { + if( pseqdesc[i].activity == activity ) + { + sameActivityNum++; + if (sameActivityNum == 1 && gonnaAttack1) { + return i; + } else if (sameActivityNum == 2) { + return i; + } + } + } + + return ACTIVITY_NOT_AVAILABLE; +} + +void CGonome::SetActivity( Activity NewActivity ) +{ + if (NewActivity != ACT_MELEE_ATTACK1) { + CBaseMonster::SetActivity(NewActivity); + } else { + ASSERT( NewActivity != 0 ); + void *pmodel = GET_MODEL_PTR( ENT( pev ) ); + int iSequence = GonomeLookupActivity( pmodel, NewActivity ); + + // Set to the desired anim, or default anim if the desired is not present + if( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if( pev->sequence != iSequence || !m_fSequenceLoops ) + { + // don't reset frame between walk and run + if( !( m_Activity == ACT_WALK || m_Activity == ACT_RUN ) || !( NewActivity == ACT_WALK || NewActivity == ACT_RUN ) ) + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo(); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT( at_aiconsole, "%s has no sequence for act:%d\n", STRING( pev->classname ), NewActivity ); + pev->sequence = 0; // Set to the reset anim (if it's there) + } + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // In case someone calls this with something other than the ideal activity + m_IdealActivity = m_Activity; + } +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CGonome::Classify(void) +{ + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// TakeDamage - overridden for gonome so we can keep track +// of how much time has passed since it was last injured +//========================================================= +int CGonome::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + float flDist; + Vector vecApex; + + // if the gonome is running, has an enemy, was hurt by the enemy, hasn't been hurt in the last 3 seconds, and isn't too close to the enemy, + // it will swerve. (whew). + if (m_hEnemy != NULL && IsMoving() && pevAttacker == m_hEnemy->pev) + { + flDist = (pev->origin - m_hEnemy->pev->origin).Length2D(); + + if (flDist > GONOME_SPRINT_DIST) + { + flDist = (pev->origin - m_Route[m_iRouteIndex].vecLocation).Length2D();// reusing flDist. + + if (FTriangulate(pev->origin, m_Route[m_iRouteIndex].vecLocation, flDist * 0.5, m_hEnemy, &vecApex)) + { + InsertWaypoint(vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY); + } + } + } + + return CBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CGonome::CheckRangeAttack1(float flDot, float flDist) +{ + if (IsMoving() && flDist >= 512) + { + // squid will far too far behind if he stops running to spit at this distance from the enemy. + return FALSE; + } + + if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextSpitTime) + { + if (m_hEnemy != NULL) + { + if (fabs(pev->origin.z - m_hEnemy->pev->origin.z) > 256) + { + // don't try to spit at someone up really high or down really low. + return FALSE; + } + } + + if (IsMoving()) + { + // don't spit again for a long time, resume chasing enemy. + m_flNextSpitTime = gpGlobals->time + 5; + } + else + { + // not moving, so spit again pretty soon. + m_flNextSpitTime = gpGlobals->time + 3; + } + + return TRUE; + } + + return FALSE; +} + +//========================================================= +// CheckMeleeAttack1 - gonome is a big guy, so has a longer +// melee range than most monsters. +//========================================================= +BOOL CGonome::CheckMeleeAttack1(float flDot, float flDist) +{ + if (flDist <= GONOME_TOLERANCE_MELEE2_RANGE && flDot >= GONOME_TOLERANCE_MELEE2_DOT && RANDOM_LONG(0,1) == 0) { + gonnaAttack1 = false; + return TRUE; + } + else if (flDist <= GONOME_TOLERANCE_MELEE1_RANGE && flDot >= GONOME_TOLERANCE_MELEE1_DOT) + { + gonnaAttack1 = true; + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckMeleeAttack2 - both gonome's melee attacks are ACT_MELEE_ATTACK1 +//========================================================= +BOOL CGonome::CheckMeleeAttack2(float flDot, float flDist) +{ + return FALSE; +} + + +//========================================================= +// IdleSound +//========================================================= +#define GONOME_ATTN_IDLE (float)1.5 +void CGonome::IdleSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, GONOME_ATTN_IDLE); +} + +//========================================================= +// PainSound +//========================================================= +void CGonome::PainSound(void) +{ + const int iPitch = RANDOM_LONG(85, 120); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1, ATTN_NORM, 0, iPitch); +} + +//========================================================= +// AlertSound +//========================================================= +void CGonome::AlertSound(void) +{ + int iPitch = RANDOM_LONG(140, 160); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM, 0, iPitch); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CGonome::SetYawSpeed( void ) +{ + int ys; + + ys = 0; + + switch ( m_Activity ) + { + case ACT_WALK: + ys = 90; + break; + case ACT_RUN: + ys = 90; + break; + case ACT_IDLE: + ys = 90; + break; + case ACT_RANGE_ATTACK1: + ys = 90; + break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CGonome::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case 1011: + // This may play sound twice + //EMIT_SOUND(ENT(pev), CHAN_VOICE, pEvent->options, 1, ATTN_NORM); + break; + case GONOME_AE_THROW: + { + Vector vecSpitOffset; + Vector vecSpitDir; + + UTIL_MakeVectors(pev->angles); + Vector vecArmPos, vecArmAng; + GetAttachment(0, vecArmPos, vecArmAng); + + vecSpitOffset = vecArmPos; + vecSpitDir = ((m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs) - vecSpitOffset).Normalize(); + + vecSpitDir.x += RANDOM_FLOAT(-0.05, 0.05); + vecSpitDir.y += RANDOM_FLOAT(-0.05, 0.05); + vecSpitDir.z += RANDOM_FLOAT(-0.05, 0); + + CGonomeGuts::Shoot(pev, vecSpitOffset, vecSpitDir * 1200); // Default: 900 + } + break; + + case GONOME_AE_SLASH_LEFT: + { + CBaseEntity *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.bullsquidDmgWhip, DMG_SLASH); + if (pHurt) + { + pHurt->pev->punchangle.z = 20; + pHurt->pev->punchangle.x = 20; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * 200; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_up * 100; + } + } + break; + + case GONOME_AE_SLASH_RIGHT: + { + CBaseEntity *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.bullsquidDmgWhip, DMG_SLASH); + if (pHurt) + { + pHurt->pev->punchangle.z = -20; + pHurt->pev->punchangle.x = 20; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * -200; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_up * 100; + } + } + break; + + case GONOME_AE_BITE1: + case GONOME_AE_BITE2: + case GONOME_AE_BITE3: + case GONOME_AE_BITE4: + { + int iPitch; + CBaseEntity *pHurt = CheckTraceHullAttack(GONOME_TOLERANCE_MELEE2_RANGE, gSkillData.bullsquidDmgBite, DMG_SLASH); + + if (pHurt) + { + // croonchy bite sound + iPitch = RANDOM_FLOAT(90, 110); + switch (RANDOM_LONG(0, 1)) + { + case 0: + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch); + break; + case 1: + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch); + break; + } + + pHurt->pev->punchangle.z = RANDOM_LONG(-10, 10); + pHurt->pev->punchangle.x = RANDOM_LONG(-35, -45); + pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_forward * 50; + pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_up * 50; + } + } + break; + + + + default: + CBaseMonster::HandleAnimEvent(pEvent); + } +} + +//========================================================= +// Spawn +//========================================================= +void CGonome::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/gonome.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.bullsquidHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_flNextSpitTime = gpGlobals->time; + + MonsterInit(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CGonome::Precache() +{ + PRECACHE_MODEL("models/gonome.mdl"); + + PRECACHE_MODEL("sprites/blood_chnk.spr");// spit projectile. + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + PRECACHE_SOUND("gonome/gonome_eat.wav"); + PRECACHE_SOUND("gonome/gonome_jumpattack.wav"); + PRECACHE_SOUND("gonome/gonome_melee1.wav"); + PRECACHE_SOUND("gonome/gonome_melee2.wav"); + + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pDeathSounds); + + PRECACHE_SOUND("gonome/gonome_run.wav"); + + PRECACHE_SOUND("bullchicken/bc_acid1.wav"); + + PRECACHE_SOUND("bullchicken/bc_bite2.wav"); + PRECACHE_SOUND("bullchicken/bc_bite3.wav"); + + PRECACHE_SOUND("bullchicken/bc_spithit1.wav"); + PRECACHE_SOUND("bullchicken/bc_spithit2.wav"); +} + +//========================================================= +// DeathSound +//========================================================= +void CGonome::DeathSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1, ATTN_NORM); +} + +//======================================================== +// RunAI - overridden for gonome because there are things +// that need to be checked every think. +//======================================================== +void CGonome::RunAI(void) +{ + // first, do base class stuff + CBaseMonster::RunAI(); + + if (m_hEnemy != NULL && m_Activity == ACT_RUN) + { + // chasing enemy. Sprint for last bit + if ((pev->origin - m_hEnemy->pev->origin).Length2D() < GONOME_SPRINT_DIST) + { + pev->framerate = 1.25; + } + } +} + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CGonome::GetSchedule( void ) +{ + switch( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { + // dead enemy + if( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CBaseMonster::GetSchedule(); + } + + if( HasConditions( bits_COND_NEW_ENEMY ) ) + { + return GetScheduleOfType( SCHED_WAKE_ANGRY ); + } + + if( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + + if( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); + } + + if( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); + } + + return GetScheduleOfType( SCHED_CHASE_ENEMY ); + break; + } + default: + break; + } + + return CBaseMonster::GetSchedule(); +} + +// primary range attack +Task_t tlGonomeRangeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slGonomeRangeAttack1[] = +{ + { + tlGonomeRangeAttack1, + ARRAYSIZE( tlGonomeRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "Gonome Range Attack1" + }, +}; + +// Chase enemy schedule +Task_t tlGonomeChaseEnemy1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation. + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slGonomeChaseEnemy[] = +{ + { + tlGonomeChaseEnemy1, + ARRAYSIZE( tlGonomeChaseEnemy1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_SMELL_FOOD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_TASK_FAILED, + 0, + "Gonome Chase Enemy" + }, +}; + +// victory dance (eating body) +Task_t tlGonomeVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.1 }, + { TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE } +}; + +Schedule_t slGonomeVictoryDance[] = +{ + { + tlGonomeVictoryDance, + ARRAYSIZE( tlGonomeVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "GonomeVictoryDance" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CGonome ) +{ + slGonomeRangeAttack1, + slGonomeChaseEnemy, + slGonomeVictoryDance, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CGonome, CBaseMonster ) + +Schedule_t* CGonome::GetScheduleOfType(int Type) +{ + switch ( Type ) + { + case SCHED_RANGE_ATTACK1: + return &slGonomeRangeAttack1[0]; + break; + case SCHED_CHASE_ENEMY: + return &slGonomeChaseEnemy[0]; + break; + case SCHED_VICTORY_DANCE: + return &slGonomeVictoryDance[0]; + break; + default: + break; + } + return CBaseMonster::GetScheduleOfType(Type); +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. +//========================================================= +void CGonome::StartTask(Task_t *pTask) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch (pTask->iTask) + { + case TASK_MELEE_ATTACK1: + { + if (gonnaAttack1) + EMIT_SOUND(ENT(pev), CHAN_VOICE, "gonome/gonome_melee1.wav", 1, ATTN_NORM); + else + EMIT_SOUND(ENT(pev), CHAN_VOICE, "gonome/gonome_melee2.wav", 1, ATTN_NORM); + CBaseMonster::StartTask(pTask); + } + break; + + case TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE: + { + UTIL_MakeVectors( pev->angles ); + if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 40, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT( at_aiconsole, "GonomeGetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + } + break; + default: + CBaseMonster::StartTask(pTask); + break; + + } +} + +//========================================================= +// DEAD GONOME PROP +//========================================================= +class CDeadGonome : public CBaseMonster +{ +public: + void Spawn(void); + int Classify(void) { return CLASS_ALIEN_MONSTER; } + void KeyValue( KeyValueData *pkvd ); + int m_iPose; + static const char *m_szPoses[3]; +}; + +const char *CDeadGonome::m_szPoses[] = { "dead_on_stomach1", "dead_on_back", "dead_on_side" }; + +void CDeadGonome::KeyValue( KeyValueData *pkvd ) +{ + if( FStrEq( pkvd->szKeyName, "pose" ) ) + { + m_iPose = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CBaseMonster::KeyValue( pkvd ); +} + +LINK_ENTITY_TO_CLASS(monster_gonome_dead, CDeadGonome) + +//========================================================= +// ********** DeadGonome SPAWN ********** +//========================================================= +void CDeadGonome::Spawn(void) +{ + PRECACHE_MODEL("models/gonome.mdl"); + SET_MODEL(ENT(pev), "models/gonome.mdl"); + + pev->effects = 0; + pev->yaw_speed = 8; + pev->sequence = 0; + m_bloodColor = BLOOD_COLOR_GREEN; + + pev->sequence = LookupSequence( m_szPoses[m_iPose] ); + if( pev->sequence == -1 ) + { + ALERT( at_console, "Dead gonome with bad pose\n" ); + } + + // Corpses have less health + pev->health = 8; + + MonsterInitDead(); +} diff --git a/dlls/CAd/otis.cpp b/dlls/CAd/otis.cpp new file mode 100644 index 00000000..500fcc4f --- /dev/null +++ b/dlls/CAd/otis.cpp @@ -0,0 +1,479 @@ +/*** +* +* Copyright (c) 1996-2001, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monster template +//========================================================= +// UNDONE: Holster weapon? + +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "talkmonster.h" +#include "schedule.h" +#include "defaultai.h" +#include "scripted.h" +#include "weapons.h" +#include "soundent.h" +#include "barney.h" + +#define NUM_OTIS_HEADS 2 // heads available for otis model + +#define GUN_GROUP 1 +#define HEAD_GROUP 2 + +#define HEAD_HAIR 0 +#define HEAD_BALD 1 + +#define GUN_NONE 0 +#define GUN_EAGLE 1 +#define GUN_DONUT 2 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +// first flag is Otis dying for scripted sequences? +#define OTIS_AE_DRAW ( 2 ) +#define OTIS_AE_SHOOT ( 3 ) +#define OTIS_AE_HOLSTER ( 4 ) + +#define OTIS_BODY_GUNHOLSTERED 0 +#define OTIS_BODY_GUNDRAWN 1 +#define OTIS_BODY_DONUT 2 + +class COtis : public CBarney +{ +public: +#if 1 + void KeyValue(KeyValueData *pkvd); +#endif + + void Spawn(void); + void Precache(void); + void BarneyFirePistol(void); + void AlertSound(void); + void HandleAnimEvent(MonsterEvent_t *pEvent); + + int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + + void DeclineFollowing(void); + + // Override these to set behavior + Schedule_t *GetSchedule(void); + + void TalkInit(void); + void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + void Killed(entvars_t *pevAttacker, int iGib); + + int head; + int bodystate; +}; + +LINK_ENTITY_TO_CLASS(monster_otis, COtis); + +//========================================================= +// ALertSound - otis says "Freeze!" +//========================================================= +void COtis::AlertSound(void) +{ + if (m_hEnemy != NULL) + { + if (FOkToSpeak()) + { + PlaySentence("OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE); + } + } +} + + + +//========================================================= +// BarneyFirePistol - shoots one round from the pistol at +// the enemy otis is facing. +//========================================================= +void COtis::BarneyFirePistol(void) +{ + Vector vecShootOrigin; + + UTIL_MakeVectors(pev->angles); + vecShootOrigin = pev->origin + Vector(0, 0, 55); + Vector vecShootDir = ShootAtEnemy(vecShootOrigin); + + Vector angDir = UTIL_VecToAngles(vecShootDir); + SetBlending(0, angDir.x); + pev->effects = EF_MUZZLEFLASH; + + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM); + + int pitchShift = RANDOM_LONG(0, 20); + + // Only shift about half the time + if (pitchShift > 10) + pitchShift = 0; + else + pitchShift -= 5; + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", 1, ATTN_NORM, 0, 100 + pitchShift); + + CSoundEnt::InsertSound(bits_SOUND_COMBAT, pev->origin, 384, 0.3); + + // UNDONE: Reload? + m_cAmmoLoaded--;// take away a bullet! +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void COtis::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case OTIS_AE_DRAW: + // otis' bodygroup switches here so he can pull gun from holster + // pev->body = OTIS_BODY_GUNDRAWN; + SetBodygroup( GUN_GROUP, GUN_EAGLE ); + m_fGunDrawn = TRUE; + break; + + case OTIS_AE_HOLSTER: + // change bodygroup to replace gun in holster + // pev->body = OTIS_BODY_GUNHOLSTERED; + SetBodygroup( GUN_GROUP, GUN_NONE ); + m_fGunDrawn = FALSE; + break; + + default: + CBarney::HandleAnimEvent(pEvent); + } +} + +//========================================================= +// Spawn +//========================================================= +void COtis::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/otis.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_fGunDrawn = FALSE; + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + // Make sure hands are white. + pev->skin = 0; + + // Select a random head. + if (head == -1) + { + SetBodygroup(HEAD_GROUP, RANDOM_LONG(0, NUM_OTIS_HEADS - 1)); + } + else + { + SetBodygroup(HEAD_GROUP, head); + } + + if (bodystate == -1) + { + SetBodygroup(GUN_GROUP, RANDOM_LONG(OTIS_BODY_GUNHOLSTERED, OTIS_BODY_GUNDRAWN)); // don't random donut + } + else + { + SetBodygroup(GUN_GROUP, bodystate); + } + + MonsterInit(); + SetUse(&COtis::FollowerUse); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void COtis::Precache() +{ + PRECACHE_MODEL("models/otis.mdl"); + + PRECACHE_SOUND("barney/desert_eagle_fire.wav"); + + PRECACHE_SOUND("barney/ba_pain1.wav"); + PRECACHE_SOUND("barney/ba_pain2.wav"); + PRECACHE_SOUND("barney/ba_pain3.wav"); + + PRECACHE_SOUND("barney/ba_die1.wav"); + PRECACHE_SOUND("barney/ba_die2.wav"); + PRECACHE_SOUND("barney/ba_die3.wav"); + + // every new otis must call this, otherwise + // when a level is loaded, nobody will talk (time is reset to 0) + TalkInit(); + CTalkMonster::Precache(); +} + +// Init talk data +void COtis::TalkInit() +{ + CTalkMonster::TalkInit(); + + // scientists speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = "OT_ANSWER"; + m_szGrp[TLK_QUESTION] = "OT_QUESTION"; + m_szGrp[TLK_IDLE] = "OT_IDLE"; + m_szGrp[TLK_STARE] = "OT_STARE"; + m_szGrp[TLK_USE] = "OT_OK"; + m_szGrp[TLK_UNUSE] = "OT_WAIT"; + m_szGrp[TLK_STOP] = "OT_STOP"; + + m_szGrp[TLK_NOSHOOT] = "OT_SCARED"; + m_szGrp[TLK_HELLO] = "OT_HELLO"; + + m_szGrp[TLK_PLHURT1] = "!OT_CUREA"; + m_szGrp[TLK_PLHURT2] = "!OT_CUREB"; + m_szGrp[TLK_PLHURT3] = "!OT_CUREC"; + + m_szGrp[TLK_PHELLO] = NULL; + m_szGrp[TLK_PIDLE] = NULL; + m_szGrp[TLK_PQUESTION] = NULL; + + m_szGrp[TLK_SMELL] = "OT_SMELL"; + + m_szGrp[TLK_WOUND] = "OT_WOUND"; + m_szGrp[TLK_MORTAL] = "OT_MORTAL"; + + // get voice for head - just one otis voice for now + m_voicePitch = 100; +} + + +static BOOL IsFacing(entvars_t *pevTest, const Vector &reference) +{ + Vector vecDir = (reference - pevTest->origin); + vecDir.z = 0; + vecDir = vecDir.Normalize(); + Vector forward, angle; + angle = pevTest->v_angle; + angle.x = 0; + UTIL_MakeVectorsPrivate(angle, forward, NULL, NULL); + // He's facing me, he meant it + if (DotProduct(forward, vecDir) > 0.96) // +/- 15 degrees or so + { + return TRUE; + } + return FALSE; +} + + +int COtis::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + // make sure friends talk about it if player hurts talkmonsters... + int ret = CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + if (!IsAlive() || pev->deadflag == DEAD_DYING) + return ret; + + if (m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT)) + { + m_flPlayerDamage += flDamage; + + // This is a heurstic to determine if the player intended to harm me + // If I have an enemy, we can't establish intent (may just be crossfire) + if (m_hEnemy == NULL) + { + // If the player was facing directly at me, or I'm already suspicious, get mad + if ((m_afMemory & bits_MEMORY_SUSPICIOUS) || IsFacing(pevAttacker, pev->origin)) + { + // Alright, now I'm pissed! + PlaySentence("OT_MAD", 4, VOL_NORM, ATTN_NORM); + + Remember(bits_MEMORY_PROVOKED); + StopFollowing(TRUE); + } + else + { + // Hey, be careful with that + PlaySentence("OT_SHOT", 4, VOL_NORM, ATTN_NORM); + Remember(bits_MEMORY_SUSPICIOUS); + } + } + else if (!(m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO) + { + PlaySentence("OT_SHOT", 4, VOL_NORM, ATTN_NORM); + } + } + + return ret; +} + +void COtis::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + switch (ptr->iHitgroup) + { + case HITGROUP_CHEST: + case HITGROUP_STOMACH: + if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST)) + { + flDamage = flDamage / 2; + } + break; + case 10: // Otis wears no helmet, so do not prevent taking headshot damage. + // always a head shot + ptr->iHitgroup = HITGROUP_HEAD; + break; + default: + break; + } + CTalkMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); +} + + +void COtis::Killed(entvars_t *pevAttacker, int iGib) +{ + if (GetBodygroup(GUN_GROUP) != OTIS_BODY_GUNHOLSTERED) + {// drop the gun! + Vector vecGunPos; + Vector vecGunAngles; + + SetBodygroup(GUN_GROUP, OTIS_BODY_GUNHOLSTERED); + + GetAttachment(0, vecGunPos, vecGunAngles); + + CBaseEntity *pGun = DropItem("weapon_eagle", vecGunPos, vecGunAngles); + } + + SetUse(NULL); + CTalkMonster::Killed(pevAttacker, iGib); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *COtis::GetSchedule(void) +{ + if (HasConditions(bits_COND_HEAR_SOUND)) + { + CSound *pSound; + pSound = PBestSound(); + + ASSERT(pSound != NULL); + if (pSound && (pSound->m_iType & bits_SOUND_DANGER)) + return GetScheduleOfType(SCHED_TAKE_COVER_FROM_BEST_SOUND); + } + + if (HasConditions(bits_COND_ENEMY_DEAD) && FOkToSpeak()) + { + PlaySentence("OT_KILL", 4, VOL_NORM, ATTN_NORM); + } + + return CBarney::GetSchedule(); +} + + +void COtis::DeclineFollowing(void) +{ + PlaySentence("OT_POK", 2, VOL_NORM, ATTN_NORM); +} + +void COtis::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "head")) + { + head = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBarney::KeyValue(pkvd); +} + + + + +//========================================================= +// DEAD OTIS PROP +// +// Designer selects a pose in worldcraft, 0 through num_poses-1 +// this value is added to what is selected as the 'first dead pose' +// among the monster's normal animations. All dead poses must +// appear sequentially in the model file. Be sure and set +// the m_iFirstPose properly! +// +//========================================================= +class CDeadOtis : public CBaseMonster +{ +public: + void Spawn(void); + int Classify(void) { return CLASS_PLAYER_ALLY; } + + void KeyValue(KeyValueData *pkvd); + + int m_iPose;// which sequence to display -- temporary, don't need to save + static char *m_szPoses[5]; +}; + +char *CDeadOtis::m_szPoses[] = { "lying_on_back", "lying_on_side", "lying_on_stomach", "stuffed_in_vent", "dead_sitting" }; + +void CDeadOtis::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "pose")) + { + m_iPose = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CBaseMonster::KeyValue(pkvd); +} + +LINK_ENTITY_TO_CLASS(monster_otis_dead, CDeadOtis); + +//========================================================= +// ********** DeadOtis SPAWN ********** +//========================================================= +void CDeadOtis::Spawn() +{ + PRECACHE_MODEL("models/otis.mdl"); + SET_MODEL(ENT(pev), "models/otis.mdl"); + + pev->effects = 0; + pev->yaw_speed = 8; + pev->sequence = 0; + m_bloodColor = BLOOD_COLOR_RED; + + pev->sequence = LookupSequence(m_szPoses[m_iPose]); + if (pev->sequence == -1) + { + ALERT(at_console, "Dead otis with bad pose\n"); + } + // Corpses have less health + pev->health = 8;//gSkillData.otisHealth; + + MonsterInitDead(); +} diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index e0b6a8b3..f06467b7 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -127,6 +127,10 @@ set (SVDLL_SOURCES world.cpp xen.cpp zombie.cpp + CAd/displacer.cpp + CAd/eagle.cpp + CAd/gonome + CAd/otis.cpp ../pm_shared/pm_debug.c ../pm_shared/pm_math.c ../pm_shared/pm_shared.c diff --git a/dlls/barney.cpp b/dlls/barney.cpp index 3fc5aef0..2b5950c0 100644 --- a/dlls/barney.cpp +++ b/dlls/barney.cpp @@ -27,6 +27,7 @@ #include "scripted.h" #include "weapons.h" #include "soundent.h" +#include "barney.h" //========================================================= // Monster's Anim Events Go Here @@ -40,54 +41,6 @@ #define BARNEY_BODY_GUNDRAWN 1 #define BARNEY_BODY_GUNGONE 2 -class CBarney : public CTalkMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int ISoundMask( void ); - void BarneyFirePistol( void ); - void AlertSound( void ); - int Classify( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - - void RunTask( Task_t *pTask ); - void StartTask( Task_t *pTask ); - virtual int ObjectCaps( void ) { return CTalkMonster :: ObjectCaps() | FCAP_IMPULSE_USE; } - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - BOOL CheckRangeAttack1( float flDot, float flDist ); - - void DeclineFollowing( void ); - - // Override these to set behavior - Schedule_t *GetScheduleOfType( int Type ); - Schedule_t *GetSchedule( void ); - MONSTERSTATE GetIdealState( void ); - - void DeathSound( void ); - void PainSound( void ); - - void TalkInit( void ); - - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - void Killed( entvars_t *pevAttacker, int iGib ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - BOOL m_fGunDrawn; - float m_painTime; - float m_checkAttackTime; - BOOL m_lastAttackCheck; - - // UNDONE: What is this for? It isn't used? - float m_flPlayerDamage;// how much pain has the player inflicted on me? - - CUSTOM_SCHEDULES -}; - LINK_ENTITY_TO_CLASS( monster_barney, CBarney ) TYPEDESCRIPTION CBarney::m_SaveData[] = diff --git a/dlls/barney.h b/dlls/barney.h new file mode 100644 index 00000000..4a307c2c --- /dev/null +++ b/dlls/barney.h @@ -0,0 +1,71 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monster template +//========================================================= +#ifndef BARNEY_H +#define BARNEY_H + +//========================================================= +// CBarney +//========================================================= +class CBarney : public CTalkMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int ISoundMask( void ); + void BarneyFirePistol( void ); + void AlertSound( void ); + int Classify( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + + void RunTask( Task_t *pTask ); + void StartTask( Task_t *pTask ); + virtual int ObjectCaps( void ) { return CTalkMonster :: ObjectCaps() | FCAP_IMPULSE_USE; } + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + BOOL CheckRangeAttack1( float flDot, float flDist ); + + void DeclineFollowing( void ); + + // Override these to set behavior + Schedule_t *GetScheduleOfType( int Type ); + Schedule_t *GetSchedule( void ); + MONSTERSTATE GetIdealState( void ); + + void DeathSound( void ); + void PainSound( void ); + + void TalkInit( void ); + + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + void Killed( entvars_t *pevAttacker, int iGib ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + BOOL m_fGunDrawn; + float m_painTime; + float m_checkAttackTime; + BOOL m_lastAttackCheck; + + // UNDONE: What is this for? It isn't used? + float m_flPlayerDamage;// how much pain has the player inflicted on me? + + CUSTOM_SCHEDULES +}; +#endif diff --git a/dlls/bullsquid.cpp b/dlls/bullsquid.cpp index af3ab88a..d86b1f04 100644 --- a/dlls/bullsquid.cpp +++ b/dlls/bullsquid.cpp @@ -26,6 +26,7 @@ #include "decals.h" #include "soundent.h" #include "game.h" +#include "bullsquid.h" #define SQUID_SPRINT_DIST 256 // how close the squid has to get before starting to sprint and refusing to swerve @@ -55,21 +56,6 @@ enum //========================================================= // Bullsquid's spit projectile //========================================================= -class CSquidSpit : public CBaseEntity -{ -public: - void Spawn( void ); - - static void Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - void Touch( CBaseEntity *pOther ); - void EXPORT Animate( void ); - - virtual int Save( CSave &save ); - virtual int Restore( CRestore &restore ); - static TYPEDESCRIPTION m_SaveData[]; - - int m_maxFrame; -}; LINK_ENTITY_TO_CLASS( squidspit, CSquidSpit ) @@ -184,46 +170,6 @@ void CSquidSpit::Touch( CBaseEntity *pOther ) #define BSQUID_AE_HOP ( 5 ) #define BSQUID_AE_THROW ( 6 ) -class CBullsquid : public CBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int ISoundMask( void ); - int Classify( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void IdleSound( void ); - void PainSound( void ); - void DeathSound( void ); - void AlertSound( void ); - void AttackSound( void ); - void StartTask( Task_t *pTask ); - void RunTask( Task_t *pTask ); - BOOL CheckMeleeAttack1( float flDot, float flDist ); - BOOL CheckMeleeAttack2( float flDot, float flDist ); - BOOL CheckRangeAttack1( float flDot, float flDist ); - void RunAI( void ); - BOOL FValidateHintType( short sHint ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType( int Type ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - int IRelationship( CBaseEntity *pTarget ); - int IgnoreConditions( void ); - MONSTERSTATE GetIdealState( void ); - - int Save( CSave &save ); - int Restore( CRestore &restore ); - - CUSTOM_SCHEDULES - static TYPEDESCRIPTION m_SaveData[]; - - BOOL m_fCanThreatDisplay;// this is so the squid only does the "I see a headcrab!" dance one time. - - float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while. - float m_flNextSpitTime;// last time the bullsquid used the spit attack. -}; - LINK_ENTITY_TO_CLASS( monster_bullchicken, CBullsquid ) TYPEDESCRIPTION CBullsquid::m_SaveData[] = diff --git a/dlls/bullsquid.h b/dlls/bullsquid.h new file mode 100644 index 00000000..4994433a --- /dev/null +++ b/dlls/bullsquid.h @@ -0,0 +1,82 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// bullsquid - big, spotty tentacle-mouthed meanie. +//========================================================= +#ifndef BULLSQUID_H +#define BULLSQUID_H + +//========================================================= +// Bullsquid's spit projectile +//========================================================= +class CSquidSpit : public CBaseEntity +{ +public: + void Spawn( void ); + + static void Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); + void Touch( CBaseEntity *pOther ); + void EXPORT Animate( void ); + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + int m_maxFrame; +}; + +//========================================================= +// CBullsquid +//========================================================= +class CBullsquid : public CBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int ISoundMask( void ); + int Classify( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void IdleSound( void ); + void PainSound( void ); + void DeathSound( void ); + void AlertSound( void ); + void AttackSound( void ); + void StartTask( Task_t *pTask ); + void RunTask( Task_t *pTask ); + BOOL CheckMeleeAttack1( float flDot, float flDist ); + BOOL CheckMeleeAttack2( float flDot, float flDist ); + BOOL CheckRangeAttack1( float flDot, float flDist ); + void RunAI( void ); + BOOL FValidateHintType( short sHint ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType( int Type ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + int IRelationship( CBaseEntity *pTarget ); + int IgnoreConditions( void ); + MONSTERSTATE GetIdealState( void ); + + int Save( CSave &save ); + int Restore( CRestore &restore ); + + CUSTOM_SCHEDULES + static TYPEDESCRIPTION m_SaveData[]; + + BOOL m_fCanThreatDisplay;// this is so the squid only does the "I see a headcrab!" dance one time. + + float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while. + float m_flNextSpitTime;// last time the bullsquid used the spit attack. +}; +#endif diff --git a/dlls/player.cpp b/dlls/player.cpp index d0a1f763..ea803ecf 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -119,6 +119,7 @@ TYPEDESCRIPTION CBasePlayer::m_playerSaveData[] = DEFINE_FIELD( CBasePlayer, m_iHideHUD, FIELD_INTEGER ), DEFINE_FIELD( CBasePlayer, m_iFOV, FIELD_INTEGER ), + DEFINE_FIELD( CBasePlayer, m_fInXen, FIELD_BOOLEAN ), //DEFINE_FIELD( CBasePlayer, m_fDeadTime, FIELD_FLOAT ), // only used in multiplayer games //DEFINE_FIELD( CBasePlayer, m_fGameHUDInitialized, FIELD_INTEGER ), // only used in multiplayer games //DEFINE_FIELD( CBasePlayer, m_flStopExtraSoundTime, FIELD_TIME ), @@ -2804,6 +2805,8 @@ void CBasePlayer::Spawn( void ) m_afPhysicsFlags = 0; m_fLongJump = FALSE;// no longjump module. + m_fInXen = FALSE; + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); @@ -3466,6 +3469,8 @@ void CBasePlayer::CheatImpulseCommands( int iImpulse ) GiveNamedItem( "weapon_satchel" ); GiveNamedItem( "weapon_snark" ); GiveNamedItem( "weapon_hornetgun" ); + GiveNamedItem( "weapon_eagle" ); + GiveNamedItem( "weapon_displacer" ); #endif gEvilImpulse101 = FALSE; break; diff --git a/dlls/player.h b/dlls/player.h index fecaf3c9..59a0edb1 100644 --- a/dlls/player.h +++ b/dlls/player.h @@ -323,7 +323,7 @@ public: char m_SbarString1[SBAR_STRING_SIZE]; float m_flNextChatTime; - + BOOL m_fInXen; bool m_bSentBhopcap; // If false, the player just joined and needs a bhopcap message. }; diff --git a/dlls/util.cpp b/dlls/util.cpp index f382ac58..6dcd3feb 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -31,6 +31,22 @@ #include "weapons.h" #include "gamerules.h" +void UTIL_MuzzleLight( Vector vecSrc, float flRadius, byte r, byte g, byte b, float flTime, float flDecay ) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE( TE_DLIGHT ); + WRITE_COORD( vecSrc.x ); // X + WRITE_COORD( vecSrc.y ); // Y + WRITE_COORD( vecSrc.z ); // Z + WRITE_BYTE( flRadius * 0.1f ); // radius * 0.1 + WRITE_BYTE( r ); // r + WRITE_BYTE( g ); // g + WRITE_BYTE( b ); // b + WRITE_BYTE( flTime * 10.0f ); // time * 10 + WRITE_BYTE( flDecay * 0.1f ); // decay * 0.1 + MESSAGE_END( ); +} + float UTIL_WeaponTimeBase( void ) { #if defined( CLIENT_WEAPONS ) @@ -2477,3 +2493,19 @@ int CRestore::BufferCheckZString( const char *string ) } return 0; } + +void UTIL_CleanSpawnPoint( Vector origin, float dist ) +{ + CBaseEntity *ent = NULL; + while( ( ent = UTIL_FindEntityInSphere( ent, origin, dist ) ) != NULL ) + { + if( ent->IsPlayer() ) + { + TraceResult tr; + UTIL_TraceHull( ent->pev->origin + Vector( 0, 0, 36), ent->pev->origin + Vector( RANDOM_FLOAT( -150, 150 ), RANDOM_FLOAT( -150, 150 ), 0 ), dont_ignore_monsters, human_hull, ent->edict(), &tr); + //UTIL_TraceModel( ent->pev->origin + Vector( 0, 0, 36), ent->pev->origin + Vector( RANDOM_FLOAT( -150, 150 ), RANDOM_FLOAT( -150, 150 ), 0 ), 0, ent->edict(), &tr); + if( !tr.fAllSolid ) + UTIL_SetOrigin(ent->pev, tr.vecEndPos); + } + } +} diff --git a/dlls/util.h b/dlls/util.h index 2ffe536e..2b39892e 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -575,3 +575,5 @@ int UTIL_SharedRandomLong( unsigned int seed, int low, int high ); float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ); float UTIL_WeaponTimeBase( void ); +void UTIL_CleanSpawnPoint( Vector origin, float dist ); +void UTIL_MuzzleLight( Vector vecSrc, float flRadius, byte r, byte g, byte b, float flTime, float flDecay ); diff --git a/dlls/weapons.cpp b/dlls/weapons.cpp index 5e4f8f78..fc1dd1ec 100644 --- a/dlls/weapons.cpp +++ b/dlls/weapons.cpp @@ -339,6 +339,12 @@ void W_Precache( void ) // egon UTIL_PrecacheOtherWeapon( "weapon_egon" ); + + // desert eagle + UTIL_PrecacheOtherWeapon( "weapon_eagle" ); + + // displacer + UTIL_PrecacheOtherWeapon( "weapon_displacer" ); #endif // tripmine UTIL_PrecacheOtherWeapon( "weapon_tripmine" ); @@ -1628,3 +1634,18 @@ TYPEDESCRIPTION CSatchel::m_SaveData[] = }; IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon ) + +TYPEDESCRIPTION CDisplacer::m_SaveData[] = +{ + DEFINE_FIELD( CDisplacer, m_iFireMode, FIELD_INTEGER ), + DEFINE_ARRAY( CDisplacer, m_pBeam, FIELD_CLASSPTR, 3 ), +}; + +IMPLEMENT_SAVERESTORE( CDisplacer, CBasePlayerWeapon ) + +TYPEDESCRIPTION CEagle::m_SaveData[] = +{ + DEFINE_FIELD( CEagle, m_fEagleLaserActive, FIELD_INTEGER ), +}; + +IMPLEMENT_SAVERESTORE( CEagle, CBasePlayerWeapon ) diff --git a/dlls/weapons.h b/dlls/weapons.h index 6c03105b..f9933889 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -77,6 +77,8 @@ public: #define WEAPON_TRIPMINE 13 #define WEAPON_SATCHEL 14 #define WEAPON_SNARK 15 +#define WEAPON_DISPLACER 16 +#define WEAPON_EAGLE 17 #define WEAPON_ALLWEAPONS (~(1<