NS/main/source/mod/AvHMarineEquipment.cpp

2498 lines
68 KiB
C++
Raw Normal View History

//======== (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.51 2015/12/03 fmoraw
// - observatory detection method no longer uses vector2d
//
// 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 "AvHMarineEquipment.h"
#include "AvHPlayer.h"
#include "AvHMarineEquipmentConstants.h"
#include "AvHPlayerUpgrade.h"
#include "AvHServerUtil.h"
#include "AvHGamerules.h"
#include "../dlls/client.h"
#include "../common/vec_op.h"
#include "../common/vector_util.h"
#include "AvHSoundListManager.h"
#include "../dlls/weapons.h"
#include "AvHServerVariables.h"
#include "AvHConstants.h"
#include "AvHSharedUtil.h"
#include "AvHMovementUtil.h"
#include "../dlls/explode.h"
#include "../dlls/weapons.h"
#include "../util/MathUtil.h"
#include "AvHTitles.h"
#include "AvHServerUtil.h"
#include "AvHParticleConstants.h"
#include "AvHMarineTurret.h"
#include "AvHSiegeTurret.h"
#include "AvHHulls.h"
Bot integration for v3.3b8 (#156) * Initial bot commit * Added server commands and cvars for adding AI players to the game. * Added auto modes for automating the adding and removal of bots * Bots connect to the server and join teams correctly * Added round restart and new map detection for AI system Push before new project added for detour * Initial bot integration * Integrated all basic bot code for navigation and task performing * Added support for multi_managers to better understand how buttons and triggers affect doors * Improved bot understanding of door triggers and weldables * Reworked nav profiles Nav profiles for bots are now dynamically updated to take into account changing capabilities, such as picking up a welder * Improved bot door usage * Added weldable obstacles back into navigation Bots now understand how to get around weldable barriers * Replaced fixed arrays with vectors * Resource node and hive lists are now vectors. * Further improved bot weld behaviour * Added dynamic reachability calculations When barriers and doors are open/closed, new reachability calculations are done for structures and items so bots understand when items/structures become reachable or unreachable as the match progresses. * Added team-based reachability calculations Reachabilities for structures and items are now based on the team, so bots understand when they can't reach a structure from their spawn point. * Implemented long-range off-mesh connections and dynamic off-mesh connections * Implemented fully dynamic off-mesh connections Phase gates now use connections rather than custom path finding. Much more performant. * Replaced arrays with vectors for simpler code * Started Bot Swimming * Bots understand trigger_changetarget Bots can now navigate doors operated with a trigger_changetarget so they understand the sequence in which triggers must be activated to make it work * Push before trying to fix long-range connections * Implement new off-mesh connection system * Redid population of door triggers * Fixed trigger types and links to doors * Added lift and moving platform support * Lift improvements * Bots avoid getting crushed under a lift when summoning it * Bots are better at judging which stop a platform needs to be at * Tweak lift and welder usage * Fixed bug with multiple off-mesh connections close together * Finish lift movement * Fixed dodgy path finding * Improved skulk ladder usage and lerk lift usage * Fix crash with path finding * Re-implement commander AI * Commander improvements * Improve commander sieging * Commander scanning tweak * Reimplemented regular marine AI * Start reimplementing alien AI * Implement gorge building behaviours * Start alien tactical decisioning * Continuing alien building and other non-combat logic * More alien role work * Adjusted base node definitions * Iterate Capper Logic * Alien assault AI * Alien Combat * Fix grenade throwing, better combat * Marine combat AI improvements * Commander improvements * Commander + nav improvements * Drop mines * Improved bot stuck detection * Commander supply improvements * Bot fill timing config * Added nsbots.cfg to configure internal bots * Changed bot config file to "nsbots.cfg" * Bug fixing with navigation * Fix skulk movement on ladders * Improved commander placement and tactical refresh * Fixed bug with ladder climbing * Doors block off-mesh connections * Finished doors blocking connections * Marine and alien tactical bug fixes * Add commander beacon back in * Start combat mode stuff * First pass at combat mode * Bots attack turrets * Fix ladder and wall climbing * Commander chat request * Improved skulk ladders * Added nav meshes for new bot code * Added bot configuration to listen server menu * Added bot config file * Added default bot config to listenserver.cfg * Added default bot settings to server.cfg * Include VS filter for bot files * Crash fixes * Bot improvements * Bot stability and mine placement improvements * Fixed crash on new map start with bots * Reverted Svencoop fix * Fixed crash, added more cvars * Performance improvement * Commander building improvements * Stop bot spasming when waiting to take command * Fixed doors not blocking connections * Added bot disabled guard to round start * Commander improvements, movement improvements * Tweaked level load sequence * Performance improvements * Bot load spread * Fixed commander update * Refactor bot frame handling * Bug fixes + Pierow's dynamic load spread * Minor bug fixes * Fix door detection, prep for test * Fixed commander siege spam * linux compile test * fix hardcoded inlcudes * O1 compile flag for detour - fix linux server crash * Revert detour compile flags to original for windows * linux build update * remove x64 build configs * update bot nav meshes and configs * fix bot physics at high server fps, update navmeshes. from @RGreenlees --------- Co-authored-by: RGreenlees <RGreenlees@users.noreply.github.com> Co-authored-by: RichardGreenlees <richard.greenlees@forecast.global>
2024-03-21 18:17:18 +00:00
#include "AvHAIPlayerManager.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;
// : 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(ns_cvar_float(&avh_drawdamage))
{
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)
{
// : 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())//: 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())//: 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);
AvHSUPushbackPlayersTouchingPlayer(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;
bool theIsResearching=false;
const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(this->GetTeamNumber()));
if ( theTeam ) {
theIsResearching=theTeam->GetResearchManager().GetIsResearching(this->entindex());
}
if(this->pev && this->GetIsBuilt() && (!theIsResearching))
{
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);
AvHSUPushbackPlayersTouchingPlayer(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;
}
// : Uncomment for the new IP from Alpha
//int AvHInfantryPortal::GetDeployAnimation() const
//{
// return 0;
//}
//int AvHInfantryPortal::GetSpawnAnimation() const
//{
// return 1;
//}
// :
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
Bot integration for v3.3b8 (#156) * Initial bot commit * Added server commands and cvars for adding AI players to the game. * Added auto modes for automating the adding and removal of bots * Bots connect to the server and join teams correctly * Added round restart and new map detection for AI system Push before new project added for detour * Initial bot integration * Integrated all basic bot code for navigation and task performing * Added support for multi_managers to better understand how buttons and triggers affect doors * Improved bot understanding of door triggers and weldables * Reworked nav profiles Nav profiles for bots are now dynamically updated to take into account changing capabilities, such as picking up a welder * Improved bot door usage * Added weldable obstacles back into navigation Bots now understand how to get around weldable barriers * Replaced fixed arrays with vectors * Resource node and hive lists are now vectors. * Further improved bot weld behaviour * Added dynamic reachability calculations When barriers and doors are open/closed, new reachability calculations are done for structures and items so bots understand when items/structures become reachable or unreachable as the match progresses. * Added team-based reachability calculations Reachabilities for structures and items are now based on the team, so bots understand when they can't reach a structure from their spawn point. * Implemented long-range off-mesh connections and dynamic off-mesh connections * Implemented fully dynamic off-mesh connections Phase gates now use connections rather than custom path finding. Much more performant. * Replaced arrays with vectors for simpler code * Started Bot Swimming * Bots understand trigger_changetarget Bots can now navigate doors operated with a trigger_changetarget so they understand the sequence in which triggers must be activated to make it work * Push before trying to fix long-range connections * Implement new off-mesh connection system * Redid population of door triggers * Fixed trigger types and links to doors * Added lift and moving platform support * Lift improvements * Bots avoid getting crushed under a lift when summoning it * Bots are better at judging which stop a platform needs to be at * Tweak lift and welder usage * Fixed bug with multiple off-mesh connections close together * Finish lift movement * Fixed dodgy path finding * Improved skulk ladder usage and lerk lift usage * Fix crash with path finding * Re-implement commander AI * Commander improvements * Improve commander sieging * Commander scanning tweak * Reimplemented regular marine AI * Start reimplementing alien AI * Implement gorge building behaviours * Start alien tactical decisioning * Continuing alien building and other non-combat logic * More alien role work * Adjusted base node definitions * Iterate Capper Logic * Alien assault AI * Alien Combat * Fix grenade throwing, better combat * Marine combat AI improvements * Commander improvements * Commander + nav improvements * Drop mines * Improved bot stuck detection * Commander supply improvements * Bot fill timing config * Added nsbots.cfg to configure internal bots * Changed bot config file to "nsbots.cfg" * Bug fixing with navigation * Fix skulk movement on ladders * Improved commander placement and tactical refresh * Fixed bug with ladder climbing * Doors block off-mesh connections * Finished doors blocking connections * Marine and alien tactical bug fixes * Add commander beacon back in * Start combat mode stuff * First pass at combat mode * Bots attack turrets * Fix ladder and wall climbing * Commander chat request * Improved skulk ladders * Added nav meshes for new bot code * Added bot configuration to listen server menu * Added bot config file * Added default bot config to listenserver.cfg * Added default bot settings to server.cfg * Include VS filter for bot files * Crash fixes * Bot improvements * Bot stability and mine placement improvements * Fixed crash on new map start with bots * Reverted Svencoop fix * Fixed crash, added more cvars * Performance improvement * Commander building improvements * Stop bot spasming when waiting to take command * Fixed doors not blocking connections * Added bot disabled guard to round start * Commander improvements, movement improvements * Tweaked level load sequence * Performance improvements * Bot load spread * Fixed commander update * Refactor bot frame handling * Bug fixes + Pierow's dynamic load spread * Minor bug fixes * Fix door detection, prep for test * Fixed commander siege spam * linux compile test * fix hardcoded inlcudes * O1 compile flag for detour - fix linux server crash * Revert detour compile flags to original for windows * linux build update * remove x64 build configs * update bot nav meshes and configs * fix bot physics at high server fps, update navmeshes. from @RGreenlees --------- Co-authored-by: RGreenlees <RGreenlees@users.noreply.github.com> Co-authored-by: RichardGreenlees <richard.greenlees@forecast.global>
2024-03-21 18:17:18 +00:00
if(thePlayer && !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)
{
Bot integration for v3.3b8 (#156) * Initial bot commit * Added server commands and cvars for adding AI players to the game. * Added auto modes for automating the adding and removal of bots * Bots connect to the server and join teams correctly * Added round restart and new map detection for AI system Push before new project added for detour * Initial bot integration * Integrated all basic bot code for navigation and task performing * Added support for multi_managers to better understand how buttons and triggers affect doors * Improved bot understanding of door triggers and weldables * Reworked nav profiles Nav profiles for bots are now dynamically updated to take into account changing capabilities, such as picking up a welder * Improved bot door usage * Added weldable obstacles back into navigation Bots now understand how to get around weldable barriers * Replaced fixed arrays with vectors * Resource node and hive lists are now vectors. * Further improved bot weld behaviour * Added dynamic reachability calculations When barriers and doors are open/closed, new reachability calculations are done for structures and items so bots understand when items/structures become reachable or unreachable as the match progresses. * Added team-based reachability calculations Reachabilities for structures and items are now based on the team, so bots understand when they can't reach a structure from their spawn point. * Implemented long-range off-mesh connections and dynamic off-mesh connections * Implemented fully dynamic off-mesh connections Phase gates now use connections rather than custom path finding. Much more performant. * Replaced arrays with vectors for simpler code * Started Bot Swimming * Bots understand trigger_changetarget Bots can now navigate doors operated with a trigger_changetarget so they understand the sequence in which triggers must be activated to make it work * Push before trying to fix long-range connections * Implement new off-mesh connection system * Redid population of door triggers * Fixed trigger types and links to doors * Added lift and moving platform support * Lift improvements * Bots avoid getting crushed under a lift when summoning it * Bots are better at judging which stop a platform needs to be at * Tweak lift and welder usage * Fixed bug with multiple off-mesh connections close together * Finish lift movement * Fixed dodgy path finding * Improved skulk ladder usage and lerk lift usage * Fix crash with path finding * Re-implement commander AI * Commander improvements * Improve commander sieging * Commander scanning tweak * Reimplemented regular marine AI * Start reimplementing alien AI * Implement gorge building behaviours * Start alien tactical decisioning * Continuing alien building and other non-combat logic * More alien role work * Adjusted base node definitions * Iterate Capper Logic * Alien assault AI * Alien Combat * Fix grenade throwing, better combat * Marine combat AI improvements * Commander improvements * Commander + nav improvements * Drop mines * Improved bot stuck detection * Commander supply improvements * Bot fill timing config * Added nsbots.cfg to configure internal bots * Changed bot config file to "nsbots.cfg" * Bug fixing with navigation * Fix skulk movement on ladders * Improved commander placement and tactical refresh * Fixed bug with ladder climbing * Doors block off-mesh connections * Finished doors blocking connections * Marine and alien tactical bug fixes * Add commander beacon back in * Start combat mode stuff * First pass at combat mode * Bots attack turrets * Fix ladder and wall climbing * Commander chat request * Improved skulk ladders * Added nav meshes for new bot code * Added bot configuration to listen server menu * Added bot config file * Added default bot config to listenserver.cfg * Added default bot settings to server.cfg * Include VS filter for bot files * Crash fixes * Bot improvements * Bot stability and mine placement improvements * Fixed crash on new map start with bots * Reverted Svencoop fix * Fixed crash, added more cvars * Performance improvement * Commander building improvements * Stop bot spasming when waiting to take command * Fixed doors not blocking connections * Added bot disabled guard to round start * Commander improvements, movement improvements * Tweaked level load sequence * Performance improvements * Bot load spread * Fixed commander update * Refactor bot frame handling * Bug fixes + Pierow's dynamic load spread * Minor bug fixes * Fix door detection, prep for test * Fixed commander siege spam * linux compile test * fix hardcoded inlcudes * O1 compile flag for detour - fix linux server crash * Revert detour compile flags to original for windows * linux build update * remove x64 build configs * update bot nav meshes and configs * fix bot physics at high server fps, update navmeshes. from @RGreenlees --------- Co-authored-by: RGreenlees <RGreenlees@users.noreply.github.com> Co-authored-by: RichardGreenlees <richard.greenlees@forecast.global>
2024-03-21 18:17:18 +00:00
// A human used the comm chair, kick the AI commander out if they're in there and don't let them re-enter for 20 seconds
if (!(thePlayer->pev->flags & FL_FAKECLIENT))
{
AIMGR_SetCommanderAllowedTime(theStationTeamNumber, gpGlobals->time + 20.0f);
}
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())
{
// : 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 = VectorDistance(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;
}