ns/releases/3.2.0/source/mod/AvHMarineEquipment.cpp
puzl 5dcc163bd5 made a copy
git-svn-id: https://unknownworlds.svn.cloudforge.com/ns1@379 67975925-1194-0748-b3d5-c16f83f1a3a1
2006-04-02 10:49:08 +00:00

2478 lines
69 KiB
C++

//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: AvHMarineEquipment.cpp $
// $Date: 2002/11/22 21:28:16 $
//
//-------------------------------------------------------------------------------
// $Log: AvHMarineEquipment.cpp,v $
// Revision 1.50 2002/11/22 21:28:16 Flayra
// - mp_consistency changes
//
// Revision 1.49 2002/11/12 02:25:29 Flayra
// - HLTV updates
//
// Revision 1.48 2002/11/06 01:40:17 Flayra
// - Turrets now need an active turret factory to keep firing
//
// Revision 1.47 2002/11/03 04:50:43 Flayra
// - Hard-coded gameplay constants instead of putting in skill.cfg
// - Ammo and health now expire
//
// Revision 1.46 2002/10/28 20:35:46 Flayra
// - Allow HAs and jetpacks to only be picked up by marines
//
// Revision 1.45 2002/10/25 23:19:30 Flayra
// - Fixed bug where people were being telefragged improperly
//
// Revision 1.44 2002/10/24 21:32:09 Flayra
// - All heavy armor to be given via console
// - Fix for AFKers on inf portals, also for REIN players when recycling portals
//
// Revision 1.43 2002/10/20 21:10:48 Flayra
// - Optimizations
//
// Revision 1.42 2002/10/19 22:33:44 Flayra
// - Various server optimizations
//
// Revision 1.41 2002/10/18 22:20:49 Flayra
// - Reduce llamability of placing phases near drops or hazards
//
// Revision 1.40 2002/10/16 20:54:30 Flayra
// - Added phase gate sound
// - Fixed ghostly command station view model problem after building it
//
// Revision 1.39 2002/10/16 01:00:14 Flayra
// - Allow any jetpack to be picked up (needed for cheat, but this is the way all weapons work too, may need to be changed for marine vs. marine, not sure)
//
// Revision 1.38 2002/10/03 18:57:20 Flayra
// - Picking up heavy armor holsters your weapon for view model switch
// - Added "armory's upgrading, ammo not available" message but removed it for some reason (I think it was acting strange, like playing way too often)
//
// Revision 1.37 2002/09/25 21:12:26 Flayra
// - Undid solidity change (causes Sys_Error)
//
// Revision 1.36 2002/09/25 20:48:47 Flayra
// - Phase gates no longer solid
//
// Revision 1.35 2002/09/23 22:21:21 Flayra
// - Added jetpack and heavy armor
// - Added "cc online" sound
// - Turret factories now upgrade to advanced turret factories for siege
// - Added automatic resupply at armory, but removed it
// - Observatories scan in 2D now, to match commander range overlay
//
// Revision 1.34 2002/09/09 19:59:31 Flayra
// - Fixed up phase gates (no longer teleport you unless you have two, and they work properly now)
// - Refactored reinforcements
// - Fixed bug where secondary command station couldn't be built
//
// Revision 1.33 2002/08/31 18:01:02 Flayra
// - Work at VALVe
//
// Revision 1.32 2002/08/16 02:39:14 Flayra
// - Fixed command station problems after game ends
//
// Revision 1.31 2002/08/09 01:06:02 Flayra
// - Removed ability for command station to be resurrected
// - Fixed up phase gate effects
//
// Revision 1.30 2002/08/02 21:55:55 Flayra
// - Allow phase teleport to fail nicely. For some reason, players don't hear their own phase sound anymore, it seems to play at the point where they left instead of where they arrive
//
// Revision 1.29 2002/07/26 23:05:54 Flayra
// - Numerical event feedback
// - Started to add sparks when buildings were hit but didn't know the 3D point to play it at
//
// Revision 1.28 2002/07/24 18:45:42 Flayra
// - Linux and scripting changes
//
// Revision 1.27 2002/07/23 17:11:47 Flayra
// - Phase gates must be built and can be destroyed, observatories decloak aliens, hooks for fast reinforcements upgrade, nuke damage increased, commander banning
//
// Revision 1.26 2002/07/08 17:02:57 Flayra
// - Refactored reinforcements, updated entities for new artwork
//
// Revision 1.25 2002/07/01 21:29:59 Flayra
// - Removed phase particles, added implosion instead, don't select command station on log-in (messy for drawing building radii)
//
// Revision 1.24 2002/06/25 18:04:43 Flayra
// - Renamed some buildings, armory is now upgraded to advanced armory
//
// Revision 1.23 2002/06/10 19:58:17 Flayra
// - Scan update happens more often, in case aliens go cloaked during scan
//
// Revision 1.22 2002/06/03 16:50:35 Flayra
// - Renamed weapons factory and armory, added ammo resupplying
//
// Revision 1.21 2002/05/28 17:51:34 Flayra
// - Tried to make nuke sound play, extended shake duration to sound length, reinforcement refactoring, mark command stations as mapper placed, so they aren't deleted on level cleanup, support for point-entity buildable command stations
//
// Revision 1.20 2002/05/23 02:33:42 Flayra
// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development.
//
//===============================================================================
#include "mod/AvHMarineEquipment.h"
#include "mod/AvHPlayer.h"
#include "mod/AvHMarineEquipmentConstants.h"
#include "mod/AvHPlayerUpgrade.h"
#include "mod/AvHServerUtil.h"
#include "mod/AvHGamerules.h"
#include "dlls/client.h"
#include "common/vec_op.h"
#include "common/vector_util.h"
#include "mod/AvHSoundListManager.h"
#include "dlls/weapons.h"
#include "mod/AvHServerVariables.h"
#include "mod/AvHConstants.h"
#include "mod/AvHSharedUtil.h"
#include "mod/AvHMovementUtil.h"
#include "dlls/explode.h"
#include "dlls/weapons.h"
#include "util/MathUtil.h"
#include "mod/AvHTitles.h"
#include "mod/AvHServerUtil.h"
#include "mod/AvHParticleConstants.h"
#include "mod/AvHMarineTurret.h"
#include "mod/AvHSiegeTurret.h"
#include "mod/AvHHulls.h"
//LINK_ENTITY_TO_CLASS(kwMine, AvHMine);
//LINK_ENTITY_TO_CLASS(kwDeployedTurret, AvHDeployedTurret);
//LINK_ENTITY_TO_CLASS(kwTurret, AvHTurret);
LINK_ENTITY_TO_CLASS(kwDeployedMine, AvHDeployedMine);
LINK_ENTITY_TO_CLASS(kwHealth, AvHHealth);
LINK_ENTITY_TO_CLASS(kwCatalyst, AvHCatalyst);
LINK_ENTITY_TO_CLASS(kwGenericAmmo, AvHGenericAmmo);
LINK_ENTITY_TO_CLASS(kwJetpack, AvHJetpack);
LINK_ENTITY_TO_CLASS(kwAmmoPack, AvHAmmoPack);
LINK_ENTITY_TO_CLASS(kwHeavyArmor, AvHHeavyArmor);
LINK_ENTITY_TO_CLASS(kwWelder, AvHWelder);
LINK_ENTITY_TO_CLASS(kwScan, AvHScan);
LINK_ENTITY_TO_CLASS(kwPhaseGate, AvHPhaseGate);
//LINK_ENTITY_TO_CLASS(kwSiegeTurret, AvHSiegeTurret);
LINK_ENTITY_TO_CLASS(kwNuke, AvHNuke);
LINK_ENTITY_TO_CLASS(kwInfantryPortal, AvHInfantryPortal);
LINK_ENTITY_TO_CLASS(kwTeamCommand, AvHCommandStation);
LINK_ENTITY_TO_CLASS(kwTurretFactory, AvHTurretFactory);
LINK_ENTITY_TO_CLASS(kwArmory, AvHArmory);
LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHArmory);
//LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHAdvancedArmory);
LINK_ENTITY_TO_CLASS(kwArmsLab, AvHArmsLab);
LINK_ENTITY_TO_CLASS(kwPrototypeLab, AvHPrototypeLab);
LINK_ENTITY_TO_CLASS(kwObservatory, AvHObservatory);
extern int gTeleportEventID;
extern int gSiegeHitEventID;
extern int gSiegeViewHitEventID;
extern AvHSoundListManager gSoundListManager;
void AvHDeployedMine::Precache(void)
{
PRECACHE_UNMODIFIED_MODEL(kTripmineWModel);
PRECACHE_UNMODIFIED_MODEL(kTripmineW2Model);
PRECACHE_UNMODIFIED_SOUND(kTripmineDeploySound);
PRECACHE_UNMODIFIED_SOUND(kTripmineActivateSound);
PRECACHE_UNMODIFIED_SOUND(kTripmineChargeSound);
PRECACHE_UNMODIFIED_SOUND(kTripmineStepOnSound);
}
void AvHDeployedMine::ActiveTouch(CBaseEntity* inOther)
{
float kTimeBetweenBeeps = 3.0f;
// Hack to circumvent the hack where owners can't collide. If this isn't done, then mines will blowup when touching the world sometimes.
//edict_t* theTempOwner = this->pev->owner;
//this->pev->owner = this->m_pRealOwner;
bool theEntityCanDoDamage = GetGameRules()->CanEntityDoDamageTo(this, inOther);
//this->pev->owner = theTempOwner;
// Check team here and only emit warning beep for friendlies
if(theEntityCanDoDamage && (this->pev->team != inOther->pev->team))
{
GetGameRules()->MarkDramaticEvent(kMineExplodePriority, inOther, this);
this->Detonate();
}
else
{
if(gpGlobals->time > this->mLastTimeTouched + kTimeBetweenBeeps)
{
// Only players trigger this, not buildings or other mines
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
if(thePlayer)
{
// Play warning proximity beep
EMIT_SOUND( ENT(pev), CHAN_BODY, kTripmineStepOnSound, 0.5, ATTN_NORM ); // shut off chargeup
this->mLastTimeTouched = gpGlobals->time;
}
}
}
}
// Ripped from old grenade
void AvHDeployedMine::Explode(TraceResult* inTrace, int inBitsDamageType)
{
float flRndSound;// sound randomizer
pev->model = iStringNull;//invisible
pev->solid = SOLID_NOT;// intangible
float theDamageModifier;
int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier);
float theDamage = this->pev->dmg*theDamageModifier;
pev->takedamage = DAMAGE_NO;
// TODO: Look at upgrade and mark up damage
// Pull out of the wall a bit
if ( inTrace->flFraction != 1.0 )
{
pev->origin = inTrace->vecEndPos + (inTrace->vecPlaneNormal * (theDamage - 24) * 0.6);
}
int iContents = UTIL_PointContents ( pev->origin );
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound
WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
if (iContents != CONTENTS_WATER)
{
WRITE_SHORT( g_sModelIndexFireball );
}
else
{
WRITE_SHORT( g_sModelIndexWExplosion );
}
WRITE_BYTE( (theDamage - 50) * .60 ); // scale * 10
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 );
//RadiusDamage(this->pev, this->pevOwner, theDamage, CLASS_NONE, inBitsDamageType);
int theRadius = BALANCE_VAR(kMineRadius);
RadiusDamage(this->pev->origin, this->pev, this->mPlacer, theDamage, theRadius, CLASS_NONE, inBitsDamageType);
// Play view shake here
float theShakeAmplitude = 80;
float theShakeFrequency = 100;
float theShakeDuration = 1.0f;
float theShakeRadius = 700;
UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius);
if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 )
{
UTIL_DecalTrace( inTrace, DECAL_SCORCH1 );
}
else
{
UTIL_DecalTrace( inTrace, DECAL_SCORCH2 );
}
flRndSound = RANDOM_FLOAT( 0 , 1 );
switch ( RANDOM_LONG( 0, 2 ) )
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM); break;
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM); break;
}
pev->effects |= EF_NODRAW;
SetThink( &CGrenade::Smoke );
pev->velocity = g_vecZero;
pev->nextthink = gpGlobals->time + 0.3;
if (iContents != CONTENTS_WATER)
{
int sparkCount = RANDOM_LONG(0,3);
for ( int i = 0; i < sparkCount; i++ )
Create( "spark_shower", pev->origin, inTrace->vecPlaneNormal, NULL );
}
}
void AvHDeployedMine::Smoke(void)
{
if (UTIL_PointContents(this->pev->origin ) == CONTENTS_WATER)
{
UTIL_Bubbles(this->pev->origin - Vector( 64, 64, 64 ), this->pev->origin + Vector( 64, 64, 64 ), 100 );
}
else
{
float theDamageModifier;
int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier);
int theDamage = this->pev->dmg*theDamageModifier;
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, this->pev->origin);
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( this->pev->origin.x );
WRITE_COORD( this->pev->origin.y );
WRITE_COORD( this->pev->origin.z );
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( (theDamage - 50) * 0.80 ); // scale * 10
WRITE_BYTE( 12 ); // framerate
MESSAGE_END();
}
UTIL_Remove( this );
}
void AvHDeployedMine::Killed(entvars_t* inAttacker, int inGib)
{
this->Detonate();
CBasePlayerItem::Killed(inAttacker, inGib);
}
int AvHDeployedMine::TakeDamage(entvars_t *inInflictor, entvars_t *inAttacker, float inDamage, int inBitsDamageType)
{
// Don't include this for potential overflow reasons
//UTIL_Sparks(this->pev->origin);
return CBasePlayerItem::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType);
}
void AvHDeployedMine::Detonate()
{
// Stop charging up
EMIT_SOUND(ENT(this->pev), CHAN_BODY, "common/null.wav", 0.5, ATTN_NORM );
if(!this->mDetonated && this->mPoweredUp)
{
TraceResult tr;
UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 64, dont_ignore_monsters, ENT(this->pev), &tr);
this->Explode(&tr, NS_DMG_NORMAL);
this->mDetonated = true;
}
}
const float kTripmineActiveThinkTime = .8f;
const float kTripminePowerUpThinkTime = .2f;
const float kTripminePowerUpTime = 3.8f;
const float kTripmineFailTime = 20.0f;
void AvHDeployedMine::SetPlacer(entvars_t* inPlacer)
{
this->mPlacer = inPlacer;
}
void AvHDeployedMine::PowerupThink()
{
// Find an owner
if(this->mOwner == NULL)
{
edict_t* theOldOwner = this->pev->owner;
this->pev->owner = NULL;
TraceResult tr;
UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 32, dont_ignore_monsters, ENT(this->pev), &tr);
if (tr.fStartSolid || (theOldOwner && tr.pHit == theOldOwner))
{
this->pev->owner = theOldOwner;
}
else if (tr.flFraction < 1.0)
{
this->pev->owner = tr.pHit;
this->mOwner = CBaseEntity::Instance(this->pev->owner);
this->mOwnerOrigin = this->mOwner->pev->origin;
this->mOwnerAngles = this->mOwner->pev->angles;
}
else
{
STOP_SOUND(ENT(this->pev), CHAN_VOICE, kTripmineDeploySound);
STOP_SOUND(ENT(this->pev), CHAN_BODY, kTripmineChargeSound);
SetThink(&CBaseEntity::SUB_Remove);
this->pev->nextthink = gpGlobals->time + 0.1;
ALERT(at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", this->pev->origin.x, this->pev->origin.y, this->pev->origin.z);
//KillBeam();
return;
}
}
else
{
this->DetonateIfOwnerInvalid();
}
if(!this->mPoweredUp)
{
if(gpGlobals->time > (this->mTimePlaced + kTripminePowerUpTime))
{
//if(this->pev->solid == SOLID_BBOX)
//{
// play enabled sound
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, kTripmineActivateSound, 0.5, ATTN_NORM, 1.0, 75 );
SetTouch(&AvHDeployedMine::ActiveTouch);
SetThink(&AvHDeployedMine::ActiveThink);
this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime;
this->mPoweredUp = true;
//}
}
}
if(!this->mPoweredUp)
{
this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime;
}
}
void AvHDeployedMine::DetonateIfOwnerInvalid()
{
if((this->mOwner == NULL) || (this->mOwner->pev->origin != this->mOwnerOrigin) || (this->mOwner->pev->angles != this->mOwnerAngles))
{
this->Detonate();
}
}
// Check to see if our position is no longer valid
void AvHDeployedMine::ActiveThink()
{
this->DetonateIfOwnerInvalid();
this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime;
}
void AvHDeployedMine::Spawn(void)
{
this->Precache();
this->pev->movetype = MOVETYPE_FLY;
this->pev->solid = SOLID_BBOX;
this->pev->classname = MAKE_STRING(kwsDeployedMine);
this->pev->iuser3 = AVH_USER3_MINE;
SET_MODEL(ENT(pev), kTripmineWModel);
UTIL_SetSize(this->pev, kMineMinSize, kMineMaxSize);
UTIL_SetOrigin(this->pev, this->pev->origin );
// Can't emit beep until active
this->mTimePlaced = gpGlobals->time;
this->mLastTimeTouched = this->mTimePlaced;
SetThink(&AvHDeployedMine::PowerupThink);
this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime;
// give them hit points
this->pev->takedamage = DAMAGE_YES;
this->pev->health = BALANCE_VAR(kMineHealth);
this->pev->dmg = BALANCE_VAR(kMineDamage);
// play deploy sound
EMIT_SOUND( ENT(this->pev), CHAN_VOICE, kTripmineDeploySound, .8f, ATTN_NORM );
EMIT_SOUND( ENT(this->pev), CHAN_BODY, kTripmineChargeSound, .5f, ATTN_NORM ); // chargeup
UTIL_MakeAimVectors(this->pev->angles);
this->mVecDir = gpGlobals->v_forward;
this->mVecEnd = this->pev->origin + this->mVecDir * 2048;
this->mDetonated = false;
this->mPoweredUp = false;
this->mOwner = NULL;
this->mPlacer = NULL;
}
AvHPlayerEquipment::AvHPlayerEquipment()
{
this->mIsPersistent = false;
this->mLifetime = -1;
}
void AvHPlayerEquipment::KeyValue(KeyValueData* pkvd)
{
// Any entity placed by the mapper is persistent
this->SetPersistent();
if(FStrEq(pkvd->szKeyName, "teamchoice"))
{
//this->mTeam = (AvHTeamNumber)(atoi(pkvd->szValue));
this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue));
pkvd->fHandled = TRUE;
}
else if(FStrEq(pkvd->szKeyName, "angles"))
{
// TODO: Insert code here
//pkvd->fHandled = TRUE;
int a = 0;
}
else if(FStrEq(pkvd->szKeyName, "lifetime"))
{
this->mLifetime = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
{
CBasePlayerItem::KeyValue(pkvd);
}
}
// 0 means never expire, -1 means no value was set so use default
int AvHPlayerEquipment::GetLifetime() const
{
int theLifetime = this->mLifetime;
if(theLifetime < 0)
{
theLifetime = AvHSUGetWeaponStayTime();
}
return theLifetime;
}
bool AvHPlayerEquipment::GetIsPersistent() const
{
return this->mIsPersistent;
}
void AvHPlayerEquipment::SetPersistent()
{
this->mIsPersistent = true;
}
void AvHHealth::Precache(void)
{
PRECACHE_UNMODIFIED_MODEL(kHealthModel);
PRECACHE_UNMODIFIED_SOUND(kHealthPickupSound);
}
void AvHHealth::Spawn(void)
{
this->Precache();
SET_MODEL(ENT(pev), kHealthModel);
this->pev->movetype = MOVETYPE_TOSS;
this->pev->solid = SOLID_TRIGGER;
this->pev->framerate = 1.0;
UTIL_SetSize(pev, kHealthMinSize, kHealthMaxSize);
UTIL_SetOrigin( pev, pev->origin );
SetTouch(&AvHHealth::Touch);
// Expire after a time.
int theLifetime = this->GetLifetime();
if(theLifetime > 0)
{
SetThink(&AvHHealth::SUB_Remove);
this->pev->nextthink = gpGlobals->time + theLifetime;
}
this->pev->iuser3 = AVH_USER3_MARINEITEM;
}
BOOL AvHHealth::GiveHealth(CBaseEntity* inOther, float points)
{
BOOL theSuccess = FALSE;
// puzl: 1017
// Amount of health to give is now a paramater to allow us to vary the resupply amount for the armoury
// float thePointsPerHealth = BALANCE_VAR(kPointsPerHealth)
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine())
{
float thePlayerMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, thePlayer->GetUser3(), thePlayer->GetExperienceLevel());
if(thePlayer->pev->health < thePlayerMaxHealth)
{
float thePointsGiven = min(points, (thePlayerMaxHealth - thePlayer->pev->health));
thePlayer->pev->health += thePointsGiven;
if(CVAR_GET_FLOAT(kvDrawDamage))
{
thePlayer->PlaybackNumericalEvent(kNumericalInfoHealthEvent, thePointsGiven);
}
// Remove parasite if player has one
//int& theUser4 = thePlayer->pev->iuser4;
//SetUpgradeMask(&theUser4, MASK_PARASITED, false);
EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHealthPickupSound, 1, ATTN_NORM);
theSuccess = TRUE;
}
}
return theSuccess;
}
void AvHHealth::Touch(CBaseEntity* inOther)
{
// puzl: 1017 medpack health amount
if(AvHHealth::GiveHealth(inOther, BALANCE_VAR(kPointsPerHealth)))
{
UTIL_Remove(this);
}
}
void AvHCatalyst::Precache(void)
{
PRECACHE_UNMODIFIED_MODEL(kCatalystModel);
PRECACHE_UNMODIFIED_SOUND(kCatalystPickupSound);
}
void AvHCatalyst::Spawn(void)
{
this->Precache();
SET_MODEL(ENT(pev), kCatalystModel);
this->pev->movetype = MOVETYPE_TOSS;
this->pev->solid = SOLID_TRIGGER;
UTIL_SetSize(pev, kCatalystMinSize, kCatalystMaxSize);
UTIL_SetOrigin( pev, pev->origin );
SetTouch(&AvHCatalyst::Touch);
// Expire after a time.
int theLifetime = this->GetLifetime();
if(theLifetime > 0)
{
SetThink(&AvHCatalyst::SUB_Remove);
this->pev->nextthink = gpGlobals->time + theLifetime;
}
this->pev->iuser3 = AVH_USER3_MARINEITEM;
}
BOOL AvHCatalyst::GiveCatalyst(CBaseEntity* inOther)
{
BOOL theSuccess = FALSE;
float theCatalystDuration = BALANCE_VAR(kCatalystDuration);
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine() && !thePlayer->GetIsCatalysted())
{
//// The player takes damage too
//float theDamagePercent = BALANCE_VAR(kCatalystDamagePercent);
//float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3);
//float theDamage = theDamagePercent*theMaxHealth;
//
//// Never kill the player
//theDamage = min(theDamage, thePlayer->pev->health - 1);
//thePlayer->TakeDamage(thePlayer->pev, thePlayer->pev, theDamage, DMG_GENERIC | DMG_IGNOREARMOR);
EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kCatalystPickupSound, 1, ATTN_NORM);
thePlayer->SetIsCatalysted(true, theCatalystDuration);
theSuccess = TRUE;
}
return theSuccess;
}
void AvHCatalyst::Touch(CBaseEntity* inOther)
{
if(AvHCatalyst::GiveCatalyst(inOther))
{
UTIL_Remove(this);
}
}
void AvHHeavyArmor::Precache(void)
{
PRECACHE_UNMODIFIED_MODEL(kHeavyModel);
PRECACHE_UNMODIFIED_SOUND(kHeavyPickupSound);
}
void AvHHeavyArmor::Spawn(void)
{
this->Precache();
SET_MODEL(ENT(pev), kHeavyModel);
this->pev->movetype = MOVETYPE_TOSS;
this->pev->solid = SOLID_TRIGGER;
UTIL_SetSize(pev, kHeavyMinSize, kHeavyMaxSize);
UTIL_SetOrigin( pev, pev->origin );
SetTouch(&AvHHeavyArmor::Touch);
this->pev->iuser3 = AVH_USER3_HEAVY;
}
void AvHHeavyArmor::Touch(CBaseEntity* inOther)
{
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
if(thePlayer && thePlayer->GetIsRelevant())
{
if(thePlayer->GetIsMarine())
{
// Check to make sure they don't have heavy armor or jetpack already
if((!thePlayer->GetHasJetpack() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasHeavyArmor())//voogru: ignore in combat mode since were trying to touch it.
{
// Needed because view model changes
if(thePlayer->HolsterWeaponToUse())
{
// Give player heavy armor
SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7, false);
SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13);
// Mark player with heavy armor
thePlayer->EffectivePlayerClassChanged();
// Set new armor value
thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3);
EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHeavyPickupSound, 1, ATTN_NORM);
UTIL_Remove(this);
}
}
}
}
}
void AvHJetpack::Precache(void)
{
PRECACHE_UNMODIFIED_MODEL(kJetpackModel);
PRECACHE_UNMODIFIED_SOUND(kJetpackPickupSound);
}
void AvHJetpack::Spawn(void)
{
this->Precache();
SET_MODEL(ENT(pev), kJetpackModel);
this->pev->movetype = MOVETYPE_TOSS;
this->pev->solid = SOLID_TRIGGER;
UTIL_SetSize(pev, kJetpackMinSize, kJetpackMaxSize);
UTIL_SetOrigin( pev, pev->origin );
SetTouch(&AvHJetpack::Touch);
this->pev->iuser3 = AVH_USER3_JETPACK;
}
void AvHJetpack::Touch(CBaseEntity* inOther)
{
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
if(thePlayer && thePlayer->GetIsRelevant())
{
if(thePlayer->GetIsMarine())
{
// Check to make sure they don't have heavy armor or jetpack already
if((!thePlayer->GetHasHeavyArmor() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasJetpack())//voogru: ignore in combat mode since were trying to touch it.
{
if(thePlayer->HolsterWeaponToUse())
{
// Give player jetpack
SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13, false);
SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7);
// Mark player with jetpack
thePlayer->EffectivePlayerClassChanged();
EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kJetpackPickupSound, 1, ATTN_NORM);
// Set new armor value
thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3);
// Set full energy to start
thePlayer->pev->fuser3 = 1.0f*kNormalizationNetworkFactor;
UTIL_Remove(this);
}
}
}
}
}
void AvHAmmoPack :: Precache( void )
{
PRECACHE_UNMODIFIED_MODEL(kAmmoPackModel);
PRECACHE_UNMODIFIED_SOUND(kAmmoPackPickupSound);
}
void AvHAmmoPack :: Spawn( void )
{
this->Precache();
SET_MODEL(ENT(pev), kAmmoPackModel);
this->pev->movetype = MOVETYPE_TOSS;
this->pev->solid = SOLID_TRIGGER;
this->m_flNoTouch = gpGlobals->time + 0.25;
UTIL_SetSize(pev, kAmmoPackMinSize, kAmmoPackMaxSize);
UTIL_SetOrigin( pev, pev->origin );
SetTouch(&AvHAmmoPack::Touch);
}
void AvHAmmoPack :: Touch( CBaseEntity *inOther)
{
if(this->m_flNoTouch > gpGlobals->time)
return;
//Dont touch non-players
if(!inOther->IsPlayer())
return;
AvHPlayer *thePlayer = dynamic_cast<AvHPlayer*>(inOther);
//if they dont have a weapon that uses this ammo, dont pick up the ammo pack.
if ( !(thePlayer->pev->weapons & (1<<m_iWeaponID)) )
return;
if( thePlayer->GiveAmmo(this->m_iAmmoAmt, this->m_szAmmoType, this->m_iMaxAmmo) != -1)
{
EMIT_SOUND(ENT(thePlayer->pev), CHAN_ITEM, kAmmoPackPickupSound, 1, ATTN_NORM);
thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0);
UTIL_Remove(this);
}
}
BOOL AvHGenericAmmo::GiveAmmo(CBaseEntity* inOther)
{
// Give ammo to the player. It will be added as generic ammo, added as one clip to
// the player's current weapon.
if (inOther->GiveAmmo( 0, kwsGenericAmmo, 0 ) != -1)
{
EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kAmmoPickupSound, 1, ATTN_NORM);
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
if(thePlayer)
{
// Just say "ammo received" instead of number of bullets, too confusing
thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0);
}
return TRUE;
}
return FALSE;
}
BOOL AvHGenericAmmo::AddAmmo( CBaseEntity *pOther )
{
if(this->mDropped)
{
return GiveAmmo(pOther);
}
return FALSE;
}
void AvHGenericAmmo::Dropped(void)
{
this->mDropped = true;
SetThink(NULL);
// Expire after a time
SetThink(&AvHGenericAmmo::SUB_Remove);
this->pev->nextthink = gpGlobals->time + AvHSUGetWeaponStayTime();
}
void AvHGenericAmmo::Precache( void )
{
PRECACHE_UNMODIFIED_MODEL(kAmmoModel);
PRECACHE_UNMODIFIED_SOUND(kAmmoPickupSound);
}
void AvHGenericAmmo::Spawn( void )
{
Precache( );
SET_MODEL(ENT(pev), kAmmoModel);
UTIL_SetSize(pev, kAmmoMinSize, kAmmoMaxSize);
CBasePlayerAmmo::Spawn( );
this->mDropped = false;
SetThink(&AvHGenericAmmo::Dropped);
this->pev->nextthink = gpGlobals->time + .15f;
this->pev->iuser3 = AVH_USER3_MARINEITEM;
}
const float kScanThinkTime = .5f;
AvHScan::AvHScan()
{
}
void AvHScan::Precache(void)
{
CBaseAnimating::Precache();
PRECACHE_UNMODIFIED_MODEL(kScanModel);
PRECACHE_UNMODIFIED_SOUND(kScanSound);
}
void AvHScan::Spawn(void)
{
// this->Precache();
// CBaseAnimating::Spawn();
//
// SET_MODEL(ENT(this->pev), kScanModel);
//
// this->pev->movetype = MOVETYPE_NONE;
// this->pev->solid = SOLID_NOT;
//
// this->pev->classname = MAKE_STRING(kwsScan);
//
// this->pev->takedamage = DAMAGE_NO;
//
// // Start animating
// this->pev->sequence = 0;
// this->pev->frame = 0;
// ResetSequenceInfo();
this->pev->effects |= (/*EF_BRIGHTFIELD |*/ EF_DIMLIGHT);
this->mTimeCreated = gpGlobals->time;
SetThink(&AvHScan::ScanThink);
this->pev->nextthink = gpGlobals->time + GetGameRules()->GetFirstScanThinkTime();
EMIT_SOUND(this->edict(), CHAN_AUTO, kScanSound, 1.0f, ATTN_NORM);
AvHSUPlayParticleEvent(kpsScanEffect, this->edict(), this->pev->origin);
}
void AvHScan::ScanThink()
{
// Remove cloaking from nearby enemies
FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team))
{
// Check that entity is in range of scan
float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin);
if(theDistance < BALANCE_VAR(kScanRadius))
{
// Remove cloaking, if player has it
theEntity->TriggerUncloak();
}
}
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
// Look in sphere for cloakables
CBaseEntity* theSphereEntity = NULL;
while ((theSphereEntity = UTIL_FindEntityInSphere(theSphereEntity, this->pev->origin, BALANCE_VAR(kScanRadius))) != NULL)
{
if(!AvHSUGetIsExternalClassName(STRING(theSphereEntity->pev->classname)))
{
// TODO: Check team here?
AvHCloakable* theCloakable = dynamic_cast<AvHCloakable*>(theSphereEntity);
if(theCloakable)
{
theCloakable->Uncloak();
}
}
}
float theScanTime = GetGameRules()->GetBuildTimeForMessageID(BUILD_SCAN);
if(gpGlobals->time < (this->mTimeCreated + theScanTime))
{
this->pev->nextthink = gpGlobals->time + kScanThinkTime;
}
else
{
UTIL_Remove(this);
}
}
AvHPhaseGate::AvHPhaseGate() : AvHMarineBaseBuildable(TECH_PHASE_GATE, BUILD_PHASEGATE, kwsPhaseGate, AVH_USER3_PHASEGATE)
{
this->mEnabled = false;
this->mTimeOfLastDeparture=0.0f;
this->mHasWarmedUp=false;
}
int AvHPhaseGate::GetSequenceForBoundingBox() const
{
return 1;
}
void AvHPhaseGate::Killed(entvars_t* inAttacker, int inGib)
{
this->SetEnabled(false);
AvHMarineBaseBuildable::Killed(inAttacker, inGib);
GetGameRules()->MarkDramaticEvent(kPGDeathPriority, this->entindex(), inAttacker);
}
void AvHPhaseGate::Precache(void)
{
CBaseAnimating::Precache();
PRECACHE_UNMODIFIED_MODEL(kPhaseGateModel);
PRECACHE_UNMODIFIED_SOUND(kPhaseGateSound);
PRECACHE_UNMODIFIED_SOUND(kPhaseGateTransportSound);
}
void AvHPhaseGate::SetHasBeenBuilt()
{
AvHBuildable::SetHasBeenBuilt();
// Include a "warm-up" time so movement chambers don't teleport the player immediately
this->mTimeOfLastDeparture=gpGlobals->time;
SetThink(&AvHPhaseGate::IdleThink);
this->pev->nextthink = gpGlobals->time + kBuildingUseWarmupTime;
}
int AvHPhaseGate::GetIdleAnimation() const
{
return 0;
}
void AvHPhaseGate::IdleThink()
{
bool theIsEnabled = false;
this->mHasWarmedUp=true;
// Check if there are any other phase gates on our team and set our enabled state accordingly
bool theDone = false;
bool potentialDeadlock=false;
int theNumPhaseGates=0;
if(this->GetIsBuilt() && !this->GetIsRecycling())
{
edict_t* thePhaseGateEdict = ENT(this->pev);
// Keep looping until we come back to ourself or we find another gate on our team to phase to
while(!theDone)
{
thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate);
AvHPhaseGate* thePhaseGate = dynamic_cast<AvHPhaseGate*>(CBaseEntity::Instance(thePhaseGateEdict));
// This assert fails in normal operation, seemingly harmlessly
//ASSERT(thePhaseGate);
if(thePhaseGateEdict == ENT(this->pev))
{
theDone = true;
}
else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsBuilt() && !thePhaseGate->GetIsRecycling() && thePhaseGate->HasWarmedUp() )
{
theIsEnabled = true;
theDone = true;
}
theNumPhaseGates++;
}
}
AvHBaseBuildable::AnimateThink();
this->SetEnabled(theIsEnabled);
this->pev->nextthink = gpGlobals->time + kPhaseGateIdleThink;
}
void AvHPhaseGate::ResetEntity()
{
this->SetEnabled(false);
AvHMarineBaseBuildable::ResetEntity();
}
void AvHPhaseGate::SetEnabled(bool inEnabledState)
{
if(inEnabledState != this->mEnabled)
{
if(inEnabledState)
{
// Start animating
this->pev->sequence = 0;
this->pev->frame = 0;
ResetSequenceInfo();
//this->pev->effects |= EF_BRIGHTLIGHT;
this->pev->effects |= EF_DIMLIGHT;
UTIL_EmitAmbientSound( ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, ATTN_NORM, 0, 100);
SetUse(&AvHPhaseGate::TeleportUse);
this->mEnabled = true;
}
else
{
// Stop animating
this->pev->sequence = 1;
this->pev->frame = 0;
ResetSequenceInfo();
this->pev->effects &= ~EF_DIMLIGHT;
UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100);
SetUse(NULL);
this->mEnabled = false;
}
}
}
bool AvHPhaseGate::GetAreTeammatesBlocking(AvHPlayer* inPlayer, const Vector& inOrigin) const
{
// This is based off the logic of AvHSUKillPlayersTouchingPlayer so that
// the results are consistent.
bool theResult = false;
FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
if(theEntity->GetIsRelevant() && theEntity->pev->team == inPlayer->pev->team)
{
float theDistanceToPlayer = VectorDistance(inOrigin, theEntity->pev->origin);
if(theDistanceToPlayer < 30)
{
if (gpGlobals->time - theEntity->GetTimeOfLastTeleport() < kPhaseGateAmnestyTime)
{
theResult = true;
}
}
}
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
return theResult;
}
void AvHPhaseGate::KillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor)
{
FOR_ALL_BASEENTITIES();
AvHBaseBuildable* theBuildable = dynamic_cast<AvHBaseBuildable*>(theBaseEntity);
if(theBuildable)
{
if (theBuildable->pev->iuser3 != AVH_USER3_HIVE && theBuildable->pev->iuser3 != AVH_USER3_PHASEGATE)
{
float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theBuildable->pev->origin);
if(theDistanceToPlayer < 50)
{
theBuildable->TakeDamage(inInflictor, theBuildable->pev, 10000, DMG_GENERIC);
}
}
}
END_FOR_ALL_BASEENTITIES();
}
bool AvHPhaseGate::HasWarmedUp() const
{
return this->mHasWarmedUp;
}
bool AvHPhaseGate::GetIsEnabled() const
{
return this->GetIsBuilt() && this->mEnabled && !this->GetIsRecycling();
}
void AvHPhaseGate::SetTimeOfLastDeparture(float timeOfLastDeparture)
{
mTimeOfLastDeparture=timeOfLastDeparture;
}
bool AvHPhaseGate::IsReadyToUse()
{
bool theReturn=false;
if ( (gpGlobals->time - mTimeOfLastDeparture) > BALANCE_VAR(kPhaseGateDepartureInterval) )
{
theReturn=true;
}
return theReturn;
}
void AvHPhaseGate::TeleportUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pActivator);
CBasePlayerItem* thePlayerItem = dynamic_cast<CBasePlayerItem*>(pActivator);
// Only players on the team of the person that created the phase gate can travel through,
bool theTeleportAllowed = (thePlayer != NULL) && (thePlayer->pev->team == this->pev->team) && thePlayer->GetIsAbleToAct();
//bool theTeleportAllowed = thePlayerItem || (thePlayer && ((pActivator->pev->team == this->pev->team) && (this->pev->team != 0)));
if(theTeleportAllowed && this->GetIsEnabled())
{
float theLastTeleportTime = thePlayer->GetTimeOfLastTeleport();
theTeleportAllowed = (theLastTeleportTime == -1) || ((gpGlobals->time - theLastTeleportTime) >= BALANCE_VAR(kPhaseGateDelay));
if(theTeleportAllowed)
{
if(!GetGameRules()->GetIsTesting())
{
// Teleport to "next" gate. If there isn't more then one gate, respawn the player
vec3_t theOrigin;
bool theDone = false;
bool theSuccess = false;
edict_t* thePhaseGateEdict = ENT(this->pev);
AvHPhaseGate *theTargetGate=0;
// Keep looping until we come back to ourself or we find another gate on our team to phase to
while(!theDone)
{
thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate);
AvHPhaseGate* thePhaseGate = dynamic_cast<AvHPhaseGate*>(CBaseEntity::Instance(thePhaseGateEdict));
// This assert fails in normal operation, seemingly harmlessly
//ASSERT(thePhaseGate);
if(thePhaseGateEdict == ENT(this->pev))
{
theDone = true;
}
else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsEnabled() && this->IsReadyToUse() )
{
// Players come through on top of phase gate, plus a little above
theOrigin = thePhaseGateEdict->v.origin;
theOrigin.z +=kRespawnFudgeFactorHeight;
// Add in proper hull size so players don't get stuck
Vector thePlayerMinSize, thePlayerMaxSize;
thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize);
theOrigin.z += -thePlayerMinSize.z;
//Elven Thief - Changed from AvHUGetHull(false to true to allow crouching marines access to phase gates.
// check out bug 487
if(AvHSUGetIsEnoughRoomForHull(theOrigin, AvHMUGetHull(true, thePlayer->pev->iuser3), thePlayer->edict(), true, true))
{
theSuccess = true;
theDone = true;
}
// Now check to see if there are any teammates blocking who just phased in.
if (GetAreTeammatesBlocking(thePlayer, theOrigin))
{
theSuccess = false;
theDone = true;
}
if ( theDone == true )
{
theTargetGate=thePhaseGate;
}
}
}
if(theSuccess)
{
// Mark the player as just having teleported so he doesn't teleport immediately again
thePlayer->SetPosition(theOrigin);
thePlayer->pev->velocity = g_vecZero;
thePlayer->SetTimeOfLastTeleport(gpGlobals->time);
int theFlags = 0;//thePlayer ? FEV_NOTHOST : 0;
this->SetTimeOfLastDeparture(gpGlobals->time);
AvHSUPlayPhaseInEffect(theFlags, this, thePlayer);
AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev);
KillBuildablesTouchingPlayer(thePlayer, this->pev);
Vector theFadeColor;
theFadeColor.x = 235;
theFadeColor.y = 255;
theFadeColor.z = 255;
UTIL_ScreenFade(thePlayer, theFadeColor, .9f, 0.0f, 255, FFADE_IN);
}
}
}
}
}
void AvHPhaseGate::UpdateOnRecycle(void)
{
this->SetEnabled(false);
}
void AvHPhaseGate::UpdateOnRemove(void)
{
// Make sure sound gets turned off when round ends without it being killed
if(this->pev)
{
UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100);
}
}
//AvHSiegeTurret::AvHSiegeTurret()
//{
// float theStartTime = RANDOM_FLOAT(0, avh_siegerof.value);
// this->mTimeLastFired = gpGlobals->time - theStartTime;
// this->mTimeToConstruct = GetGameRules()->GetBuildTimeForMessageID(BUILD_SIEGE);
//}
//
//CBaseEntity* AvHSiegeTurret::BestVisibleEnemy(void)
//{
// CBaseEntity *theCurrentEnemy = NULL;
// CBaseEntity *theBestEnemy = NULL;
// float theDistanceToBestEnemy = -1;
//
// while((theCurrentEnemy = UTIL_FindEntityInSphere(theCurrentEnemy, this->pev->origin, avh_siegemaxrange.value)) != NULL)
// {
// // If entity is a valid target and within our valid range
// if(this->GetIsValidTarget(theCurrentEnemy))
// {
// // Is it closer than our current target?
// float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, theCurrentEnemy);
// if((theDistanceToBestEnemy == -1) || (theDistanceToCurrentEnemy < theDistanceToBestEnemy))
// {
// theBestEnemy = theCurrentEnemy;
// theDistanceToBestEnemy = theDistanceToCurrentEnemy;
// }
// }
// }
// return theBestEnemy;
//}
//
//bool AvHSiegeTurret::GetIsValidTarget(CBaseEntity* inEntity) const
//{
// bool theTargetIsValid = false;
//
// if(AvHDeployedTurret::GetIsValidTarget(inEntity))
// {
// float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, inEntity);
// if(theDistanceToCurrentEnemy >= this->GetMinimumRange())
// {
// // We have to see it as well
// Vector vecMid = this->pev->origin + this->pev->view_ofs;
// Vector vecMidEnemy = inEntity->BodyTarget(vecMid);
// if(FBoxVisible(this->pev, inEntity->pev, vecMidEnemy))
// {
// theTargetIsValid = true;
// }
// }
// }
// return theTargetIsValid;
//}
//
//char* AvHSiegeTurret::GetActiveSound() const
//{
// return kSiegeActive;
//}
//
//char* AvHSiegeTurret::GetAlertSound() const
//{
// return kSiegeAlert;
//}
//
//char* AvHSiegeTurret::GetDeploySound() const
//{
// return kSiegeDeploy;
//}
//
//char* AvHSiegeTurret::GetPingSound() const
//{
// return kSiegePing;
//}
//
//int AvHSiegeTurret::GetPointValueOfTarget(void) const
//{
// return 3;
//}
//
//int AvHSiegeTurret::GetMinimumRange() const
//{
// return avh_siegeminrange.value;
//}
//
//bool AvHSiegeTurret::NeedsLineOfSight() const
//{
// return true;
//}
//
//void AvHSiegeTurret::Precache(void)
//{
// AvHDeployedTurret::Precache();
//
// PRECACHE_MODEL(kSiegeTurretModel);
// PRECACHE_SOUND(kSiegeTurretFire1);
//}
//
//
//void AvHSiegeTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
//{
// // Only fire once every few seconds...this is hacky but there's no way to override think functions so it must be done
// // I wish it was easier to change the update rate but it's not so...
// if((gpGlobals->time - this->mTimeLastFired) > avh_siegerof.value)
// {
// // Find enemy player in range, ignore walls and everything else
// if(this->m_hEnemy)
// {
// if(this->GetIsValidTarget(this->m_hEnemy))
// {
// // Apply damage, taking upgrade into account
// float theDamageMultiplier;
// AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser4, &theDamageMultiplier);
// float theDamage = theDamageMultiplier*avh_siegedamage.value;
//
// if(!GetGameRules()->GetIsTesting())
// {
// ::RadiusDamage(this->m_hEnemy->pev->origin, this->pev, this->pev, theDamage, avh_siegesplashradius.value, CLASS_NONE, DMG_BLAST);
// }
//
// // Play fire sound
// EMIT_SOUND_DYN(ENT(this->pev), CHAN_AUTO, kSiegeTurretFire1, 1.0, ATTN_NORM, 0, PITCH_NORM);
//
// this->pev->effects &= ~EF_MUZZLEFLASH;
//
// // Send normal effect to all
// PLAYBACK_EVENT_FULL(0, this->m_hEnemy->edict(), gSiegeHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
//
// if(this->m_hEnemy->IsPlayer())
// {
// // Send personal view shake to recipient only (check for splash here, pass param to lessen effect for others?)
// // TODO: Use upgrade level to parameterize screen shake and fade?
// PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->m_hEnemy->edict(), gSiegeViewHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
//
// Vector theFadeColor;
// theFadeColor.x = 255;
// theFadeColor.y = 100;
// theFadeColor.z = 100;
// UTIL_ScreenFade(this->m_hEnemy, theFadeColor, .3f, 0.0f, 255, FFADE_OUT);
// }
// }
// else
// {
// this->m_hEnemy = NULL;
// }
// }
//
// this->mTimeLastFired = gpGlobals->time;
// }
//}
//
//void AvHSiegeTurret::Spawn()
//{
// this->Precache();
// AvHDeployedTurret::Spawn();
//
// SET_MODEL(ENT(this->pev), kSiegeTurretModel);
// UTIL_SetSize(pev, kSiegeMinSize, kSiegeMaxSize);
//
// this->pev->classname = MAKE_STRING(kwsSiegeTurret);
//
// this->pev->takedamage = 1;
// this->pev->health = (int)(avh_siegehealth.value);
// this->pev->armorvalue = (int)(avh_siegehealth.value);
//}
AvHMarineBaseBuildable::AvHMarineBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser4) : AvHBaseBuildable(inTechID, inMessageID, inClassName, inUser4)
{
this->SetEnergy(0.0f);
}
char* AvHMarineBaseBuildable::GetDeploySound() const
{
return kMarineBuildingDeploy;
}
bool AvHMarineBaseBuildable::GetIsTechnologyAvailable(AvHMessageID inMessageID) const
{
bool theIsAvailable = AvHBaseBuildable::GetIsTechnologyAvailable(inMessageID);
// Disable scanning if not enough energy
if(theIsAvailable && AvHSHUGetDoesTechCostEnergy(inMessageID))
{
int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID);
if(this->mEnergy < theEnergyCost)
{
theIsAvailable = false;
}
}
return theIsAvailable;
}
char* AvHMarineBaseBuildable::GetKilledSound() const
{
return kMarineBuildingKilled;
}
int AvHMarineBaseBuildable::GetPointValue() const
{
return BALANCE_VAR(kScoringMarineBuildableValue);
}
int AvHMarineBaseBuildable::GetTakeDamageAnimation() const
{
return -1;
}
void AvHMarineBaseBuildable::ResetEntity()
{
AvHBaseBuildable::ResetEntity();
}
void AvHMarineBaseBuildable::SetEnergy(float inEnergy)
{
this->mEnergy = max(min(inEnergy, kMarineStructureMaxEnergy), 0.0f);
float theNormValue = this->mEnergy/kMarineStructureMaxEnergy;
if(this->pev && this->GetIsBuilt())
{
AvHSHUSetEnergyState(this->pev->iuser3, this->pev->fuser1, theNormValue);
}
}
int AvHMarineBaseBuildable::TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType)
{
//UTIL_Sparks()
return AvHBaseBuildable::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType);
}
void AvHMarineBaseBuildable::TechnologyBuilt(AvHMessageID inMessageID)
{
// Pay energy cost
if(AvHSHUGetDoesTechCostEnergy(inMessageID))
{
int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID);
float theNewEnergy = max(this->mEnergy - theEnergyCost, 0.0f);
this->SetEnergy(theNewEnergy);
// Play animation?
this->PlayAnimationAtIndex(this->GetActiveAnimation(), true);
}
}
// Marine buildings
const float kNukeThinkInterval = 1.5f;
AvHNuke::AvHNuke() : AvHMarineBaseBuildable(TECH_NULL, BUILD_NUKE, kwsNuke, AVH_USER3_NUKE)
{
}
void AvHNuke::Precache()
{
AvHMarineBaseBuildable::Precache();
PRECACHE_UNMODIFIED_SOUND(kNukeActive);
PRECACHE_UNMODIFIED_SOUND(kNukeExplode);
}
void AvHNuke::ActiveThink()
{
float theThinkInterval = kNukeThinkInterval;
// If not active
if(!this->mActive)
{
// Set construction complete
this->SetConstructionComplete();
// Play sounds
// Set active
this->mActive = true;
// Mark as a monster
SetBits(this->pev->flags, FL_MONSTER);
this->mTimeActivated = gpGlobals->time;
}
// Emit higher and higher frequency sound
float theTimeToDetonate = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID());
float thePercentDone = (gpGlobals->time - this->mTimeActivated)/theTimeToDetonate;
thePercentDone = min(max(0.0f, thePercentDone), 1.0f);
int thePitch = 100 + thePercentDone*3;
EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, kNukeActive, 1.0, ATTN_NORM, 0, thePitch);
// If active
if(this->mActive)
{
// If enough time has passed
ASSERT(this->mTimeActivated != -1);
if(gpGlobals->time > (this->mTimeActivated + theTimeToDetonate))
{
// Detonation visuals (any bigger magnitude then this and the explosion doesn't show up)
int theMagnitude = 300;
ExplosionCreate(this->pev->origin, this->pev->angles, this->edict(), theMagnitude, FALSE, this->pev->team);
// Do damage (theDamage at epicenter, falls off from there, doesn't hurt people in water, only hurts people that can be seen by blast)
float theDamage = kNukeDamage;
float theRadius = kNukeRange;
::RadiusDamage(this->pev->origin, this->pev, this->pev, theDamage, theRadius, CLASS_NONE, DMG_BLAST);
// Play view shake here
float theShakeAmplitude = 100;
float theShakeFrequency = 150;
float theShakeDuration = 10.0f;
float theShakeRadius = 2000;
UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius);
// Add white out effect for players in radius
FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
if(theEntity->GetIsRelevant() && !theEntity->GetIsInTopDownMode())
{
// If near the epicenter, or if it's visible, we're blinded
bool theNearEpicenter = false;
float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin);
if(theDistance < theRadius/2)
{
theNearEpicenter = true;
}
bool theExplosionVisible = false;
if(theEntity->GetIsEntityInSight(this))
{
theExplosionVisible = true;
}
if(theNearEpicenter || theExplosionVisible)
{
Vector theFadeColor;
theFadeColor.x = 255;
theFadeColor.y = 255;
theFadeColor.z = 255;
UTIL_ScreenFade(theEntity, theFadeColor, 1.5f, .5f, 255, FFADE_IN);
}
}
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
//this->pev->flags |= EF_NODRAW;
SET_MODEL(ENT(pev), kNullModel);
this->pev->flags |= EF_BRIGHTFIELD;
this->pev->flags |= EF_BRIGHTLIGHT;
// Remove entity
SetThink(&AvHNuke::DeathThink);
theThinkInterval = theShakeDuration;
// Play special nuke sound (not playing for some reason)
EMIT_SOUND(this->edict(), CHAN_BODY, kNukeExplode, 1.0f, ATTN_IDLE);
}
}
this->pev->nextthink = gpGlobals->time + theThinkInterval;
}
void AvHNuke::DeathThink()
{
UTIL_Remove(this);
}
void AvHNuke::Spawn()
{
AvHBaseBuildable::Spawn();
// Set ActiveThink
this->mActive = false;
this->mTimeActivated = -1;
SetThink(&AvHNuke::ActiveThink);
this->pev->nextthink = gpGlobals->time + kNukeThinkInterval;
}
char* AvHNuke::GetDeploySound() const
{
return kNukeDeploy;
}
char* AvHNuke::GetKilledSound() const
{
return kNukeKilled;
}
const float kInfantryPortalThinkTime = 1.0f;
#define kInfantryPortalLightEffect EF_LIGHT
AvHInfantryPortal::AvHInfantryPortal() : AvHMarineBaseBuildable(TECH_INFANTRYPORTAL, BUILD_INFANTRYPORTAL, kwsInfantryPortal, AVH_USER3_INFANTRYPORTAL)
{
}
void AvHInfantryPortal::Killed(entvars_t* inAttacker, int inGib)
{
AvHBaseBuildable::Killed(inAttacker, inGib);
GetGameRules()->MarkDramaticEvent(kIPDeathPriority, this->entindex(), inAttacker);
}
float AvHInfantryPortal::GetReinforceTime() const
{
float theReinforceTime = BALANCE_VAR(kMarineRespawnTime);
if(GetGameRules()->GetCheatsEnabled())
{
theReinforceTime = 2;
}
theReinforceTime = max(0.0f, theReinforceTime);
return theReinforceTime;
}
void AvHInfantryPortal::PortalThink()
{
this->UpdateReinforcements();
AvHBaseBuildable::AnimateThink();
this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime;
}
void AvHInfantryPortal::ResetEntity()
{
AvHMarineBaseBuildable::ResetEntity();
AvHReinforceable::ResetEntity();
}
void AvHInfantryPortal::SetHasBeenBuilt()
{
AvHBuildable::SetHasBeenBuilt();
SetThink(&AvHInfantryPortal::PortalThink);
this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime;
// this->pev->effects |= kInfantryPortalLightEffect;
}
void AvHInfantryPortal::Precache()
{
AvHMarineBaseBuildable::Precache();
PRECACHE_UNMODIFIED_SOUND(kTransportSound);
}
void AvHInfantryPortal::CueRespawnEffect(AvHPlayer* inPlayer)
{
// Playback teleporting event
PLAYBACK_EVENT_FULL(0, inPlayer->edict(), gTeleportEventID, 0, inPlayer->pev->origin, inPlayer->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kTransportSound, .8, ATTN_NORM);
}
bool AvHInfantryPortal::GetCanReinforce() const
{
return this->GetIsBuilt() && !GetGameRules()->GetIsCombatMode();
}
bool AvHInfantryPortal::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const
{
// Set player position to on top of gate
vec3_t thePosition = AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs);
Vector theIPMinSize, theIPMaxSize;
AvHSHUGetSizeForTech(BUILD_INFANTRYPORTAL, theIPMinSize, theIPMaxSize);
thePosition.z += theIPMaxSize.z;
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inPlayer);
ASSERT(thePlayer);
// Respawn player on top of portal
Vector thePlayerMinSize, thePlayerMaxSize;
thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize);
thePosition.z += (-thePlayerMinSize.z + kRespawnFudgeFactorHeight);
VectorCopy(thePosition, outLocation);
return true;
}
AvHTeamNumber AvHInfantryPortal::GetReinforceTeamNumber() const
{
return this->GetTeamNumber();
}
void AvHInfantryPortal::ResetReinforcingPlayer(bool inSuccess)
{
// If we're respawning, we telefrag. Make sure to telefrag after the player's new location has been set
bool theTelefrag = false;
AvHPlayer* thePlayer = this->GetReinforcingPlayer();
if(inSuccess && thePlayer)
{
theTelefrag = true;
}
AvHReinforceable::ResetReinforcingPlayer(inSuccess);
if(theTelefrag)
{
AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev);
}
}
void AvHInfantryPortal::UpdateOnRecycle(void)
{
this->ResetReinforcingPlayer(false);
// this->pev->effects &= ~kInfantryPortalLightEffect;
}
void AvHInfantryPortal::UpdateOnRemove(void)
{
this->ResetReinforcingPlayer(false);
SetThink(NULL);
}
int AvHInfantryPortal::GetIdleAnimation() const
{
// AvHBaseBuildable randomly shows Idle2, which we don't have in this model.
return 2;
}
int AvHInfantryPortal::GetIdle1Animation() const
{
// AvHBaseBuildable randomly shows Idle2, which we don't have in this model.
return 2;
}
int AvHInfantryPortal::GetIdle2Animation() const
{
// AvHBaseBuildable randomly shows Idle2, which we don't have in this model.
return 2;
}
// tankefugl: Uncomment for the new IP from Alpha
//int AvHInfantryPortal::GetDeployAnimation() const
//{
// return 0;
//}
//int AvHInfantryPortal::GetSpawnAnimation() const
//{
// return 1;
//}
// :tankefugl
const int kCommandStationExitAnimation = 12;
AvHCommandStation::AvHCommandStation() : AvHMarineBaseBuildable(TECH_COMMAND_CENTER, BUILD_COMMANDSTATION, kwsTeamCommand, AVH_USER3_COMMANDER_STATION)
{
this->mCommanderAtThisStation = -1;
this->mTimeToPlayOnlineSound = -1;
}
int AvHCommandStation::GetIdleAnimation() const
{
int theAnimation = 2;
// If we're in use
if(this->mCommanderAtThisStation != -1 || GetGameRules()->GetIsCombatMode())
{
theAnimation = 3;
}
return theAnimation;
}
int AvHCommandStation::GetPointValue() const
{
return BALANCE_VAR(kScoringCCValue);
}
void AvHCommandStation::Killed( entvars_t *pevAttacker, int iGib )
{
this->SetInactive();
AvHMarineBaseBuildable::Killed(pevAttacker, iGib);
}
int AvHCommandStation::ObjectCaps( void )
{
// Recycled command stations cannot be used.
int theObjectCaps = 0;
if ( !(pev->effects & EF_NODRAW) )
{
theObjectCaps |= FCAP_CONTINUOUS_USE;
}
return theObjectCaps;
}
void AvHCommandStation::CommandTouch(CBaseEntity* pOther)
{
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pOther);
if(thePlayer)
{
AvHTeamNumber theStationTeamNumber = this->GetTeamNumber();
if(thePlayer->pev->team == theStationTeamNumber)
{
// Check to see if we already have a commander and don't make the person a commander if so
if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0)
{
thePlayer->SendMessage(kHelpTextCSAttractMode, TOOLTIP);
}
}
}
}
void AvHCommandStation::CommandUse( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value )
{
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pActivator);
// Mapper-placed CCs can be killed but they don't go away
if(thePlayer && !(thePlayer->pev->flags & FL_FAKECLIENT) && !this->GetHasBeenKilled() && thePlayer->GetIsAbleToAct())
{
AvHTeam* theTeam = thePlayer->GetTeamPointer();
if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE))
{
AvHTeamNumber theStationTeamNumber = this->GetTeamNumber();
if(thePlayer->pev->team == theStationTeamNumber)
{
// Check to see if we already have a commander and don't make the person a commander if so
if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0)
{
string theErrorMessage;
if(thePlayer->GetCanCommand(theErrorMessage))
{
if(this->pev->health > 0 && !this->GetIsRecycling())
{
thePlayer->SetUser3(AVH_USER3_COMMANDER_PLAYER);
//thePlayer->SetSelection(this->entindex());
EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationStartSound, .8, kCommandStationAttenuation);
SetThink(&AvHCommandStation::CommanderUsingThink);
this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval;
const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationUseTeamOne : kTargetCommandStationUseTeamTwo;
FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f);
this->mCommanderAtThisStation = thePlayer->entindex();
this->PlayAnimationAtIndex(5, true);
this->mTimeToPlayOnlineSound = this->GetTimeAnimationDone();
GetGameRules()->MarkDramaticEvent(kCCNewCommanderPriority, thePlayer, this);
}
else
{
thePlayer->SendMessage(kCommandStationDestroyed, TOOLTIP);
}
}
else
{
thePlayer->SendMessage(theErrorMessage.c_str());
}
}
else
{
// The player somehow touches the command station while still a commander
if(thePlayer->GetUser3() != AVH_USER3_COMMANDER_PLAYER)
{
thePlayer->SendMessage(kCommandStationInUse, TOOLTIP);
}
}
}
else
{
thePlayer->SendMessage(kCommandStationForOtherTeam, TOOLTIP);
}
}
}
}
void AvHCommandStation::Precache(void)
{
AvHMarineBaseBuildable::Precache();
PRECACHE_UNMODIFIED_MODEL(kCommandStationModel);
PRECACHE_UNMODIFIED_SOUND(kCommandStationStartSound);
PRECACHE_UNMODIFIED_SOUND(kCommandStationEndSound);
}
void AvHCommandStation::ActivateThink(void)
{
// Don't allow use of the Command station in Combat mode
if(!GetGameRules()->GetIsCombatMode())
{
SetTouch(&AvHCommandStation::CommandTouch);
SetUse(&AvHCommandStation::CommandUse);
}
SetThink(&AvHBaseBuildable::AnimateThink);
this->pev->nextthink = gpGlobals->time + .1f;
}
void AvHCommandStation::SetHasBeenBuilt()
{
AvHBuildable::SetHasBeenBuilt();
SetThink(&AvHCommandStation::ActivateThink);
this->pev->nextthink = gpGlobals->time + 2.0f;
}
void AvHCommandStation::EjectCommander()
{
// If this command station had a commander, log him out!
if(this->mCommanderAtThisStation != -1)
{
// if the command station is killed, kick out any commander
FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
if(theEntity->pev->team == this->pev->team)
{
if(theEntity->GetUser3() == AVH_USER3_COMMANDER_PLAYER)
{
theEntity->StopTopDownMode();
theEntity->SetUser3(AVH_USER3_MARINE_PLAYER);
this->PlayAnimationAtIndex(kCommandStationExitAnimation, true);
GetGameRules()->MarkDramaticEvent(kCCEjectPriority, theEntity, this);
break;
}
}
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
}
this->mCommanderAtThisStation = -1;
}
char* AvHCommandStation::GetKilledSound() const
{
return kCommandStationDeathSound;
}
void AvHCommandStation::SetInactive()
{
AvHBaseBuildable::SetInactive();
this->EjectCommander();
}
void AvHCommandStation::Materialize()
{
AvHMarineBaseBuildable::Materialize();
this->mCommanderAtThisStation = -1;
//this->ResetEntity();
}
void AvHCommandStation::Spawn(void)
{
AvHMarineBaseBuildable::Spawn();
this->Materialize();
}
int AvHCommandStation::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
int theReturnCode = 0;
if(this->pev->health > 0)
{
theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
if(this->pev->health <= 0)
{
AvHTeamNumber theStationTeamNumber = (AvHTeamNumber)this->pev->team;
const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationDestroyedTeamOne : kTargetCommandStationDestroyedTeamTwo;
FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f);
GetGameRules()->MarkDramaticEvent(kCCDeathPriority, this->entindex(), false, pevAttacker);
}
}
return theReturnCode;
}
void AvHCommandStation::CommanderUsingThink(void)
{
AvHTeamNumber theStationTeamNumber = this->GetTeamNumber();
// Check to see if we already have a commander and don't make the person a commander if so
if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0)
{
this->mCommanderAtThisStation = -1;
this->mTimeToPlayOnlineSound = -1;
EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationEndSound, .8, kCommandStationAttenuation);
this->PlayAnimationAtIndex(kCommandStationExitAnimation, true);
//SetThink(NULL);
SetThink(&AvHBaseBuildable::AnimateThink);
this->pev->nextthink = gpGlobals->time + .1f;
}
else
{
if(this->mTimeToPlayOnlineSound > 0.0f)
{
if(gpGlobals->time > this->mTimeToPlayOnlineSound)
{
ASSERT(this->mCommanderAtThisStation > 0);
// If enough time has passed and we haven't played online sound, play it
AvHPlayer* theCommander = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommanderAtThisStation)));
if(theCommander)
{
theCommander->PlayHUDSound(HUD_SOUND_MARINE_CCONLINE);
}
this->mTimeToPlayOnlineSound = -1;
}
}
AvHBaseBuildable::AnimateThink();
this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval;
}
}
bool AvHCommandStation::GetIsTechnologyAvailable(AvHMessageID inMessageID) const
{
bool theTechIsAvailable = AvHMarineBaseBuildable::GetIsTechnologyAvailable(inMessageID);
// Only allow CC to be recycled if it's unoccupied
if(inMessageID == BUILD_RECYCLE)
{
theTechIsAvailable = (this->mCommanderAtThisStation == -1);
}
return theTechIsAvailable;
}
AvHTurretFactory::AvHTurretFactory() : AvHMarineBaseBuildable(TECH_TURRET_FACTORY, BUILD_TURRET_FACTORY, kwsTurretFactory, AVH_USER3_TURRET_FACTORY)
{
}
void AvHTurretFactory::CheckTurretEnabledState() const
{
// Check to see if turrets should come online
FOR_ALL_ENTITIES(kwsDeployedTurret, AvHMarineTurret*)
if(theEntity->pev->team == this->pev->team)
{
theEntity->CheckEnabledState();
}
END_FOR_ALL_ENTITIES(kwsDeployedTurret);
FOR_ALL_ENTITIES(kwsSiegeTurret, AvHSiegeTurret*)
if(theEntity->pev->team == this->pev->team)
{
theEntity->CheckEnabledState();
}
END_FOR_ALL_ENTITIES(kwsSiegeTurret)
}
void AvHTurretFactory::TriggerAddTech() const
{
AvHBuildable::TriggerAddTech();
this->CheckTurretEnabledState();
}
void AvHTurretFactory::TriggerRemoveTech() const
{
AvHBuildable::TriggerRemoveTech();
this->CheckTurretEnabledState();
}
int AvHTurretFactory::GetIdle1Animation() const
{
int theAnimation = 3;
// Different animation when electrified
if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11))
{
theAnimation = 12;
}
return theAnimation;
}
int AvHTurretFactory::GetIdle2Animation() const
{
return this->GetIdle1Animation();
}
int AvHTurretFactory::GetResearchAnimation() const
{
return 4;
}
void AvHTurretFactory::SetHasBeenBuilt()
{
AvHBuildable::SetHasBeenBuilt();
// Animate
SetThink(&AvHBaseBuildable::AnimateThink);
this->pev->nextthink = gpGlobals->time + .1f;
}
bool AvHTurretFactory::GetSupportsTechID(AvHTechID inTechID) const
{
bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID);
if(this->GetTechID() == TECH_ADVANCED_TURRET_FACTORY)
{
if(!theSuccess)
{
// Adv. turret factory also counts as a turret factory
theSuccess = (inTechID == TECH_TURRET_FACTORY);
}
}
return theSuccess;
}
void AvHTurretFactory::Upgrade()
{
// Set iuser3
this->pev->iuser3 = AVH_USER3_ADVANCED_TURRET_FACTORY;
// Set classname
this->pev->classname = MAKE_STRING(kwsAdvancedTurretFactory);
this->mMessageID = TURRET_FACTORY_UPGRADE;
this->SetTechID(TECH_ADVANCED_TURRET_FACTORY);
this->SetSelectID(this->pev->iuser3);
this->SetConstructionComplete(true);
this->HealthChanged();
}
AvHArmory::AvHArmory() : AvHMarineBaseBuildable(TECH_ARMORY, BUILD_ARMORY, kwsArmory, AVH_USER3_ARMORY)
{
}
int AvHArmory::GetSequenceForBoundingBox() const
{
return 2;
}
bool AvHArmory::GetSupportsTechID(AvHTechID inTechID) const
{
bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID);
if(this->GetTechID() == TECH_ADVANCED_ARMORY)
{
// Adv. armory also counts as an armory
if(!theSuccess)
{
theSuccess = (inTechID == TECH_ARMORY);
}
}
return theSuccess;
}
int AvHArmory::GetIdle1Animation() const
{
int theAnim = 2;
if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY)
{
theAnim = 3;
}
return theAnim;
}
int AvHArmory::GetIdle2Animation() const
{
return this->GetIdle1Animation();
}
int AvHArmory::GetActiveAnimation() const
{
int theAnim = 5;
if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY)
{
theAnim = 12;
}
return theAnim;
}
int AvHArmory::GetResearchAnimation() const
{
return 5;
}
void AvHArmory::Precache()
{
AvHMarineBaseBuildable::Precache();
PRECACHE_UNMODIFIED_SOUND(kArmoryResupplySound);
}
void AvHArmory::ResupplyUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue)
{
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inCaller);
if(thePlayer && (thePlayer->pev->team == this->pev->team) && this->GetIsBuilt() && !this->GetIsRecycling() && thePlayer->GetIsAbleToAct())
{
if(thePlayer->GetCanBeResupplied())
{
// puzl: 1017
// // Give health back occasionally
// bool theGiveHealthIfNeeded = (RANDOM_LONG(0, 3) == 0);
//
// resupply gives 10 health each use
thePlayer->Resupply(true);
// Always play "getting ammo" sound when ammo or health are needed, to indicate to player when to stop pressing +use
EMIT_SOUND(thePlayer->edict(), CHAN_WEAPON, kArmoryResupplySound, .3f, ATTN_NORM);
}
}
}
void AvHArmory::SetHasBeenBuilt()
{
AvHBuildable::SetHasBeenBuilt();
SetUse(&AvHArmory::ResupplyUse);
}
void AvHArmory::Upgrade()
{
// Set iuser3
this->pev->iuser3 = AVH_USER3_ADVANCED_ARMORY;
// Set classname
this->pev->classname = MAKE_STRING(kwsAdvancedArmory);
this->mMessageID = ARMORY_UPGRADE;
this->SetTechID(TECH_ADVANCED_ARMORY);
this->SetSelectID(this->pev->iuser3);
this->SetConstructionComplete(true);
this->HealthChanged();
}
AvHArmsLab::AvHArmsLab() : AvHMarineBaseBuildable(TECH_ARMSLAB, BUILD_ARMSLAB, kwsArmsLab, AVH_USER3_ARMSLAB)
{
}
int AvHArmsLab::GetResearchAnimation() const
{
return 5;
}
int AvHArmsLab::GetSequenceForBoundingBox() const
{
return 1;
}
AvHPrototypeLab::AvHPrototypeLab() : AvHMarineBaseBuildable(TECH_PROTOTYPE_LAB, BUILD_PROTOTYPE_LAB, kwsPrototypeLab, AVH_USER3_PROTOTYPE_LAB)
{
}
const float kObservatoryThinkTime = 1.0f;
const float kObservatoryStartEnergy = 40;
AvHObservatory::AvHObservatory() : AvHMarineBaseBuildable(TECH_OBSERVATORY, BUILD_OBSERVATORY, kwsObservatory, AVH_USER3_OBSERVATORY)
{
}
int AvHObservatory::GetSequenceForBoundingBox() const
{
return 1;
}
void AvHObservatory::ObservatoryThink()
{
// Remove cloaking from nearby enemies
if ( !this->GetIsRecycling() ) {
FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team))
{
// Check that entity is in range of scan
float theDistance = VectorDistance2D(theEntity->pev->origin, this->pev->origin);
if(theDistance < BALANCE_VAR(kObservatoryXYDetectionRadius))
{
// Remove cloaking, if player has it
theEntity->TriggerUncloak();
}
}
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
}
AvHBaseBuildable::AnimateThink();
// Update scanning energy
float theRate = kMarineStructureEnergyRate;
if(GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch))
{
theRate *= 6;
}
this->SetEnergy(this->mEnergy + (kObservatoryThinkTime*theRate));
this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime;
}
void AvHObservatory::Materialize()
{
AvHMarineBaseBuildable::Materialize();
}
void AvHObservatory::ResetEntity()
{
AvHMarineBaseBuildable::ResetEntity();
this->SetEnergy(0);
}
void AvHObservatory::SetHasBeenBuilt()
{
AvHBuildable::SetHasBeenBuilt();
SetThink(&AvHObservatory::ObservatoryThink);
this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime;
this->SetEnergy(kObservatoryStartEnergy);
}
void AvHObservatory::Spawn()
{
AvHMarineBaseBuildable::Spawn();
}
int AvHObservatory::GetActiveAnimation() const
{
return 2;
}
int AvHObservatory::GetIdle1Animation() const
{
return 3;
}
int AvHObservatory::GetIdle2Animation() const
{
return 3;
}
int AvHObservatory::GetResearchAnimation() const
{
return 4;
}