This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View File

@@ -0,0 +1,249 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: TF2 specific CBaseCombatCharacter code.
//
//=============================================================================//
#include "cbase.h"
#include "basecombatcharacter.h"
#include "engine/IEngineSound.h"
#include "tf_player.h"
#include "tf_stats.h"
extern char *g_pszEMPPulseStart;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseCombatCharacter::HasPowerup( int iPowerup )
{
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
return ( m_iPowerups & (1 << iPowerup) ) != 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseCombatCharacter::CanPowerupEver( int iPowerup )
{
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
// Only objects use power
if ( iPowerup == POWERUP_POWER )
return false;
// Accept everything else
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseCombatCharacter::CanPowerupNow( int iPowerup )
{
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
if ( !CanPowerupEver(iPowerup) )
return false;
switch( iPowerup )
{
case POWERUP_BOOST:
{
// Am I taking EMP damage, or is a technician trying to drain me?
if ( HasPowerup( POWERUP_EMP ) || ( (m_flPowerupAttemptTimes[POWERUP_EMP] + 0.5) > gpGlobals->curtime ) )
{
// Reduce EMP time
m_flPowerupEndTimes[POWERUP_EMP] -= 0.05;
// Don't apply any boost effects
return false;
}
}
break;
case POWERUP_EMP:
{
// Was I just boosted? If so, I don't take EMP damage for a bit
if ( (m_flPowerupAttemptTimes[POWERUP_BOOST] + 0.5) > gpGlobals->curtime )
return false;
}
break;
default:
break;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCombatCharacter::SetPowerup( int iPowerup, bool bState, float flTime, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier )
{
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
// Some powerups trigger their on state continuously, as opposed to turning it on for some time.
bool bTriggerStart = ( bState && !HasPowerup( iPowerup ) );
if ( bState && iPowerup == POWERUP_BOOST )
{
// Health boost always triggers
bTriggerStart = true;
}
bool bHadPowerup = false;
if ( HasPowerup( iPowerup ) && !bState )
{
bHadPowerup = true;
}
if ( bState )
{
m_iPowerups |= (1 << iPowerup);
}
else
{
m_iPowerups &= ~(1 << iPowerup);
}
// Fire start/end triggers
if ( bTriggerStart )
{
PowerupStart( iPowerup, flAmount, pAttacker, pDamageModifier );
}
else if ( bHadPowerup )
{
PowerupEnd( iPowerup );
}
// If we've got an active powerup, keep thinking
if ( m_iPowerups )
{
SetContextThink( PowerupThink, gpGlobals->curtime + 0.1, POWERUP_THINK_CONTEXT );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseCombatCharacter::PowerupThink( void )
{
// If we don't have any powerups, stop thinking
if ( !m_iPowerups )
return;
// Check all the powerups
for ( int i = 0; i < MAX_POWERUPS; i++ )
{
// Don't check power, because it never runs out naturally
if ( i == POWERUP_POWER )
continue;
if ( m_iPowerups & (1 << i) )
{
// Should it finish now?
if ( m_flPowerupEndTimes[i] < gpGlobals->curtime )
{
SetPowerup( i, false );
}
}
}
SetNextThink( gpGlobals->curtime + 0.1, POWERUP_THINK_CONTEXT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseCombatCharacter::AttemptToPowerup( int iPowerup, float flTime, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier )
{
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
// Ignore it if I'm dead
if ( !IsAlive() )
return false;
m_flPowerupAttemptTimes[iPowerup] = gpGlobals->curtime;
// If we can't be powerup this type, abort
if ( !CanPowerupNow( iPowerup ) )
return false;
// Get the correct duration
flTime = PowerupDuration( iPowerup, flTime );
m_flPowerupEndTimes[iPowerup] = MAX( m_flPowerupEndTimes[iPowerup], gpGlobals->curtime + flTime );
// Turn it on
SetPowerup( iPowerup, true, flTime, flAmount, pAttacker, pDamageModifier );
// Add the damage modifier to the player
if ( pDamageModifier )
{
pDamageModifier->AddModifierToEntity( this );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Powerup has just started
//-----------------------------------------------------------------------------
void CBaseCombatCharacter::PowerupStart( int iPowerup, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier )
{
Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
switch( iPowerup )
{
case POWERUP_BOOST:
{
// Players can be boosted over their max
int iMaxBoostedHealth;
if ( IsPlayer() )
{
iMaxBoostedHealth = GetMaxHealth() + GetMaxHealth() / 2;
}
else
{
iMaxBoostedHealth = GetMaxHealth();
}
// Can we boost health further?
if ( GetHealth() < iMaxBoostedHealth )
{
int maxHealthToAdd = iMaxBoostedHealth - GetHealth();
// It uses floating point in here so it doesn't lose the fractional healing part on small frame times.
float flHealthToAdd = flAmount + m_flFractionalBoost;
int nHealthToAdd = (int)flHealthToAdd;
m_flFractionalBoost = flHealthToAdd - nHealthToAdd;
if ( nHealthToAdd )
{
int nHealthAdded = MIN( nHealthToAdd, maxHealthToAdd );
if ( IsPlayer() )
{
((CBaseTFPlayer*)this)->TakeHealthBoost( nHealthAdded, GetMaxHealth(), 25 );
}
else
{
TakeHealth( nHealthAdded, DMG_GENERIC );
}
TFStats()->IncrementPlayerStat( pAttacker, TF_PLAYER_STAT_HEALTH_GIVEN, nHealthAdded );
}
}
}
break;
case POWERUP_EMP:
{
// EMP removes adrenalin rush
SetPowerup( POWERUP_RUSH, false );
}
break;
default:
break;
}
}

View File

@@ -0,0 +1,424 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Basic BOT handling.
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "player.h"
#include "tf_player.h"
#include "menu_base.h"
#include "in_buttons.h"
#include "movehelper_server.h"
#include "weapon_twohandedcontainer.h"
void ParseCommand( CBaseTFPlayer *pPlayer, const char *pcmd, const char *pargs );
void ClientPutInServer( edict_t *pEdict, const char *playername );
void Bot_Think( CBaseTFPlayer *pBot );
ConVar bot_forcefireweapon( "bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
ConVar bot_forceattack2( "bot_forceattack2", "0", 0, "When firing, use attack2." );
ConVar bot_forceattackon( "bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
ConVar bot_flipout( "bot_flipout", "0", 0, "When on, all bots fire their guns." );
ConVar bot_defend( "bot_defend", "0", 0, "Set to a team number, and that team will all keep their combat shields raised." );
ConVar bot_changeclass( "bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
static ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
static int BotNumber = 1;
static int g_iNextBotTeam = -1;
static int g_iNextBotClass = -1;
//-----------------------------------------------------------------------------
// Purpose: Create a new Bot and put it in the game.
// Output : Pointer to the new Bot, or NULL if there's no free clients.
//-----------------------------------------------------------------------------
CBasePlayer *BotPutInServer( bool bFrozen, int iTeam, int iClass )
{
g_iNextBotTeam = iTeam;
g_iNextBotClass = iClass;
char botname[ 64 ];
Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber );
edict_t *pEdict = NULL;
{
bool oldLock = engine->LockNetworkStringTables( false );
pEdict = engine->CreateFakeClient( botname );
engine->LockNetworkStringTables( oldLock );
}
if ( !pEdict )
{
Msg( "Failed to create Bot.\n");
return NULL;
}
// Allocate a CBasePlayer for the bot, and call spawn
ClientPutInServer( pEdict, botname );
CBaseTFPlayer *pPlayer = ((CBaseTFPlayer *)CBaseEntity::Instance( pEdict ));
pPlayer->ClearFlags();
pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT );
if ( bFrozen )
pPlayer->AddEFlags( EFL_BOT_FROZEN );
if ( iTeam != -1 )
{
pPlayer->ChangeTeam( iTeam );
}
pPlayer->ForceRespawn();
BotNumber++;
return pPlayer;
}
//-----------------------------------------------------------------------------
// Purpose: Run through all the Bots in the game and let them think.
//-----------------------------------------------------------------------------
void Bot_RunAll( void )
{
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex( i ) );
if ( pPlayer && (pPlayer->GetFlags() & FL_FAKECLIENT) )
{
Bot_Think( pPlayer );
}
}
}
typedef struct
{
bool backwards;
float nextturntime;
bool lastturntoright;
float nextstrafetime;
float sidemove;
QAngle forwardAngle;
QAngle lastAngles;
} botdata_t;
static botdata_t g_BotData[ MAX_PLAYERS ];
bool RunMimicCommand( CUserCmd& cmd )
{
if ( bot_mimic.GetInt() <= 0 )
return false;
if ( bot_mimic.GetInt() > gpGlobals->maxClients )
return false;
CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() );
if ( !pPlayer )
return false;
if ( !pPlayer->GetLastUserCommand() )
return false;
cmd = *pPlayer->GetLastUserCommand();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Simulates a single frame of movement for a player
// Input : *fakeclient -
// *viewangles -
// forwardmove -
// sidemove -
// upmove -
// buttons -
// impulse -
// msec -
// Output : virtual void
//-----------------------------------------------------------------------------
static void RunPlayerMove( CBaseTFPlayer *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
{
if ( !fakeclient )
return;
CUserCmd cmd;
// Store off the globals.. they're gonna get whacked
float flOldFrametime = gpGlobals->frametime;
float flOldCurtime = gpGlobals->curtime;
float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
fakeclient->SetTimeBase( flTimeBase );
Q_memset( &cmd, 0, sizeof( cmd ) );
if ( !RunMimicCommand( cmd ) )
{
VectorCopy( viewangles, cmd.viewangles );
cmd.forwardmove = forwardmove;
cmd.sidemove = sidemove;
cmd.upmove = upmove;
cmd.buttons = buttons;
cmd.impulse = impulse;
cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
}
MoveHelperServer()->SetHost( fakeclient );
fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() );
// save off the last good usercmd
fakeclient->SetLastUserCommand( cmd );
// Clear out any fixangle that has been set
fakeclient->pl.fixangle = FIXANGLE_NONE;
// Restore the globals..
gpGlobals->frametime = flOldFrametime;
gpGlobals->curtime = flOldCurtime;
}
//-----------------------------------------------------------------------------
// Purpose: Run this Bot's AI for one frame.
//-----------------------------------------------------------------------------
void Bot_Think( CBaseTFPlayer *pBot )
{
// Hack to make Bots use Menus
if ( pBot->m_pCurrentMenu == gMenus[MENU_CLASS] )
{
int iClass = g_iNextBotClass;
if ( iClass == -1 )
iClass = random->RandomInt( 1, TFCLASS_CLASS_COUNT );
pBot->m_pCurrentMenu->Input( pBot, iClass );
}
else if ( bot_changeclass.GetInt() && bot_changeclass.GetInt() != pBot->PlayerClass() )
{
pBot->m_pCurrentMenu = gMenus[MENU_CLASS];
pBot->m_pCurrentMenu->Input( pBot, bot_changeclass.GetInt() );
}
// Make sure we stay being a bot
pBot->AddFlag( FL_FAKECLIENT );
botdata_t *botdata = &g_BotData[ ENTINDEX( pBot->edict() ) - 1 ];
QAngle vecViewAngles;
float forwardmove = 0.0;
float sidemove = botdata->sidemove;
float upmove = 0.0;
unsigned short buttons = 0;
byte impulse = 0;
float frametime = gpGlobals->frametime;
vecViewAngles = pBot->GetLocalAngles();
// Create some random values
if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) )
{
trace_t trace;
// Stop when shot
if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) )
{
if ( pBot->m_iHealth == 100 )
{
forwardmove = 600 * ( botdata->backwards ? -1 : 1 );
if ( botdata->sidemove != 0.0f )
{
forwardmove *= random->RandomFloat( 0.1, 1.0f );
}
}
else
{
forwardmove = 0;
}
}
// Only turn if I haven't been hurt
if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 )
{
Vector vecEnd;
Vector forward;
QAngle angle;
float angledelta = 15.0;
int maxtries = (int)360.0/angledelta;
if ( botdata->lastturntoright )
{
angledelta = -angledelta;
}
angle = pBot->GetLocalAngles();
Vector vecSrc;
while ( --maxtries >= 0 )
{
AngleVectors( angle, &forward );
vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 );
vecEnd = vecSrc + forward * 10;
UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN_SCALED( pBot ), VEC_HULL_MAX_SCALED( pBot ),
MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace );
if ( trace.fraction == 1.0 )
{
if ( gpGlobals->curtime < botdata->nextturntime )
{
break;
}
}
angle.y += angledelta;
if ( angle.y > 180 )
angle.y -= 360;
else if ( angle.y < -180 )
angle.y += 360;
botdata->nextturntime = gpGlobals->curtime + 2.0;
botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false;
botdata->forwardAngle = angle;
botdata->lastAngles = angle;
}
if ( gpGlobals->curtime >= botdata->nextstrafetime )
{
botdata->nextstrafetime = gpGlobals->curtime + 1.0f;
if ( random->RandomInt( 0, 5 ) == 0 )
{
botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 );
}
else
{
botdata->sidemove = 0;
}
sidemove = botdata->sidemove;
if ( random->RandomInt( 0, 20 ) == 0 )
{
botdata->backwards = true;
}
else
{
botdata->backwards = false;
}
}
pBot->SetLocalAngles( angle );
vecViewAngles = angle;
}
// Is my team being forced to defend?
if ( bot_defend.GetInt() == pBot->GetTeamNumber() )
{
buttons |= IN_ATTACK2;
}
// If bots are being forced to fire a weapon, see if I have it
else if ( bot_forcefireweapon.GetString() )
{
CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() );
if ( pWeapon )
{
// Switch to it if we don't have it out
CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon();
// Is it a twohandedweapon? If so, get the left weapon
CWeaponTwoHandedContainer *pContainer = dynamic_cast< CWeaponTwoHandedContainer * >( pActiveWeapon );
if ( pContainer )
{
pActiveWeapon = pContainer->GetLeftWeapon();
}
// Switch?
if ( pActiveWeapon != pWeapon )
{
pBot->Weapon_Switch( pWeapon );
}
else
{
// Start firing
// Some weapons require releases, so randomise firing
if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
{
buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
}
}
}
}
if ( bot_flipout.GetInt() )
{
if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
{
buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
}
}
}
else
{
// Wait for Reinforcement wave
if ( !pBot->IsAlive() )
{
// Try hitting my buttons occasionally
if ( random->RandomInt( 0, 100 ) > 80 )
{
// Respawn the bot
if ( random->RandomInt( 0, 1 ) == 0 )
{
buttons |= IN_JUMP;
}
else
{
buttons = 0;
}
}
}
}
if ( bot_flipout.GetInt() >= 2 )
{
QAngle angOffset = RandomAngle( -1, 1 );
botdata->lastAngles += angOffset;
for ( int i = 0 ; i < 2; i++ )
{
if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f )
{
if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] )
{
botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15;
}
else
{
botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15;
}
}
}
botdata->lastAngles[ 2 ] = 0;
pBot->SetLocalAngles( botdata->lastAngles );
}
RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime );
}

View File

@@ -0,0 +1,19 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef BOT_BASE_H
#define BOT_BASE_H
#ifdef _WIN32
#pragma once
#endif
// If iTeam or iClass is -1, then a team or class is randomly chosen.
CBasePlayer *BotPutInServer( bool bFrozen, int iTeam, int iClass );
#endif // BOT_BASE_H

View File

@@ -0,0 +1,43 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "c_obj_armor_upgrade.h"
IMPLEMENT_CLIENTCLASS_DT(C_ArmorUpgrade, DT_ArmorUpgrade, CArmorUpgrade)
END_RECV_TABLE()
C_ArmorUpgrade::C_ArmorUpgrade()
{
}
int C_ArmorUpgrade::DrawModel( int flags )
{
C_BaseEntity *pParent = GetMoveParent();
if ( pParent )
{
C_BaseAnimating *pAnimating = dynamic_cast< C_BaseAnimating* >( pParent );
if ( pAnimating )
{
SetModelPointer( pParent->GetModel() );
SetSequence( pAnimating->GetSequence() );
m_nSkin = pAnimating->m_nSkin;
m_nBody = pAnimating->m_nBody;
SetLocalOrigin( Vector( 0, 0, 50 ) );
SetLocalAngles( QAngle( 0, 0, 0 ) );
InvalidateBoneCache();
}
}
return BaseClass::DrawModel( 0 );
}

View File

@@ -0,0 +1,32 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef C_OBJ_ARMOR_UPGRADE_H
#define C_OBJ_ARMOR_UPGRADE_H
#ifdef _WIN32
#pragma once
#endif
#include "tf_obj_baseupgrade_shared.h"
class C_ArmorUpgrade : public C_BaseObjectUpgrade
{
public:
DECLARE_CLASS( C_ArmorUpgrade, C_BaseObjectUpgrade );
DECLARE_CLIENTCLASS();
C_ArmorUpgrade();
virtual int DrawModel( int flags );
private:
C_ArmorUpgrade( const C_ArmorUpgrade & ) {}
};
#endif // C_OBJ_ARMOR_UPGRADE_H

View File

@@ -0,0 +1,331 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Complete definition of the ControlZone behavioral entity
//
// $NoKeywords: $
//=============================================================================//
#include "tf_shareddefs.h"
#include "cbase.h"
#include "EntityOutput.h"
#include "tf_player.h"
#include "controlzone.h"
#include "team.h"
//-----------------------------------------------------------------------------
// Purpose: Since the control zone is a data only class, force it to always be sent ( shouldn't change often so )
// bandwidth usage should be small.
// Input : **ppSendTable -
// *recipient -
// *pvs -
// clientArea -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
int CControlZone::UpdateTransmitState()
{
if ( IsEffectActive( EF_NODRAW ) )
{
return SetTransmitState( FL_EDICT_DONTSEND );
}
else
{
return SetTransmitState( FL_EDICT_ALWAYS );
}
}
IMPLEMENT_SERVERCLASS_ST(CControlZone, DT_ControlZone)
SendPropInt( SENDINFO(m_nZoneNumber), 8, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( trigger_controlzone, CControlZone);
BEGIN_DATADESC( CControlZone )
// outputs
DEFINE_OUTPUT( m_ControllingTeam, "ControllingTeam" ),
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "SetTeam", InputSetTeam ),
DEFINE_INPUTFUNC( FIELD_VOID, "LockTeam", InputLockControllingTeam ),
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iLockAfterChange, FIELD_INTEGER, "LockAfterChange" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flTimeTillCaptured, FIELD_FLOAT, "UncontestedTime" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flTimeTillContested, FIELD_FLOAT, "ContestedTime" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nZoneNumber, FIELD_INTEGER, "ZoneNumber" ),
END_DATADESC()
// Control Zone Ent Flags
#define CZF_DONT_USE_TOUCHES 1
//-----------------------------------------------------------------------------
// Purpose: Initializes the control zone
// Records who was the original controlling team (for control locking)
//-----------------------------------------------------------------------------
void CControlZone::Spawn( void )
{
// set the starting controlling team
m_ControllingTeam.Set( GetTeamNumber(), this, this );
// remember who the original controlling team was (for control locking)
m_iDefendingTeam = GetTeamNumber();
// Solid
SetSolid( SOLID_BSP );
AddSolidFlags( FSOLID_TRIGGER );
SetMoveType( MOVETYPE_NONE );
SetModel( STRING( GetModelName() ) ); // set size and link into world
// TF2 rules
m_flTimeTillContested = 10.0; // Go to contested 10 seconds after enemies enter the zone
m_flTimeTillCaptured = 5.0; // Go to captured state as soon as only one team holds the zone
if ( m_nZoneNumber == 0 )
{
Warning( "Warning, trigger_controlzone without Zone Number set\n" );
}
m_ZonePlayerList.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Records that a player has entered the zone, and updates it's state
// according, maybe starting to change team.
// Input : *pOther - the entity that left the zone
//-----------------------------------------------------------------------------
void CControlZone::StartTouch( CBaseEntity *pOther )
{
CBaseTFPlayer *pl = ToBaseTFPlayer( pOther );
if ( !pl )
return;
CHandle< CBaseTFPlayer > hHandle;
hHandle = pl;
m_ZonePlayerList.AddToTail( hHandle );
ReevaluateControllingTeam();
// Set this player's current zone to this zone
pl->SetCurrentZone( this );
}
//-----------------------------------------------------------------------------
// Purpose: Records that a player has left the zone, and updates it's state
// according, maybe starting to change team.
// Input : *pOther - the entity that left the zone
//-----------------------------------------------------------------------------
void CControlZone::EndTouch( CBaseEntity *pOther )
{
CBaseTFPlayer *pl = ToBaseTFPlayer( pOther );
if ( !pl )
return;
CHandle< CBaseTFPlayer > hHandle;
hHandle = pl;
m_ZonePlayerList.FindAndRemove( hHandle );
ReevaluateControllingTeam();
// Unset this player's current zone if it's this one
if ( pl->GetCurrentZone() == this )
pl->SetCurrentZone( NULL );
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if it's time to change controllers
//-----------------------------------------------------------------------------
void CControlZone::ReevaluateControllingTeam( void )
{
// Count the number of players in each team
int i;
memset( m_iPlayersInZone, 0, sizeof( m_iPlayersInZone ) );
for ( i = 0; i < m_ZonePlayerList.Size(); i++ )
{
if ( m_ZonePlayerList[i] != NULL && (m_ZonePlayerList[i]->GetTeamNumber() > 0) )
{
m_iPlayersInZone[ m_ZonePlayerList[i]->GetTeamNumber() ] += 1;
}
}
// Abort immediately if we're not using touches to changes teams
if ( HasSpawnFlags( CZF_DONT_USE_TOUCHES ) )
return;
// if we're locked in place, no changes can occur to controlling team except through an explicit map ResetTeam
if ( m_iLocked )
return;
bool foundAnyTeam = false;
int teamFound = 0;
// check to see if any teams have no players
for ( i = 0; i < GetNumberOfTeams(); i++ )
{
if ( m_iPlayersInZone[i] )
{
if ( foundAnyTeam )
{
// we've already found a team, so it's being contested;
teamFound = ZONE_CONTESTED;
break;
}
foundAnyTeam = true;
teamFound = i;
}
}
// no one in the area!
if ( teamFound == 0 )
{
// just leave it as it is, let it continue to change team
// exception: if the zone state is contested, and there aren't any players in the zone,
// just return to the team who used to own the zone.
if ( GetTeamNumber() == ZONE_CONTESTED )
{
ChangeTeam(m_iDefendingTeam);
SetControllingTeam( this, m_iDefendingTeam );
}
return;
}
// if it's the same controlling team, don't worry about it
if ( teamFound == GetTeamNumber() )
{
// the right team is in control, don't even think of switching
m_iTryingToChangeToTeam = 0;
SetNextThink( TICK_NEVER_THINK );
return;
}
// Find out if the zone isn't owned by anyone at all (hasn't been touched since the map started, and it started un-owned)
bool bHasBeenOwned = true;
if ( m_iDefendingTeam == 0 && GetTeamNumber() == 0 )
bHasBeenOwned = false;
// if it's not contested, always go to contested mode
if ( GetTeamNumber() != ZONE_CONTESTED && teamFound != GetTeamNumber() )
{
// Unowned zones are captured immediately (no contesting stage)
if ( bHasBeenOwned )
teamFound = ZONE_CONTESTED;
}
// if it's the team we're trying to change to, don't worry about it
if ( teamFound == m_iTryingToChangeToTeam )
return;
// set up the time to change to the new team soon
m_iTryingToChangeToTeam = teamFound;
// changing from contested->uncontested and visa-versa have different delays
if ( m_iTryingToChangeToTeam != ZONE_CONTESTED )
{
if ( !bHasBeenOwned )
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to %d NOW\n", GetDebugName(), m_iTryingToChangeToTeam );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to %d in %.2f seconds\n", GetDebugName(), m_iTryingToChangeToTeam, m_flTimeTillCaptured );
SetNextThink( gpGlobals->curtime + m_flTimeTillCaptured );
}
}
else
{
DevMsg( 1, "trigger_controlzone: (%s) changing to contested in %f seconds\n", GetDebugName(), m_flTimeTillContested );
SetNextThink( gpGlobals->curtime + m_flTimeTillContested );
}
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if an uncontested territory is ready to change state
// to the new controlling team.
//-----------------------------------------------------------------------------
void CControlZone::Think( void )
{
if ( m_iTryingToChangeToTeam != 0 )
{
// held zone long enough
SetControllingTeam( this, m_iTryingToChangeToTeam );
// lock against further change if set
if ( m_iLockAfterChange )
{
LockControllingTeam();
}
// Re-evaluate controlling team if we were changing to Contested (enemy may have withdrawn)
if ( GetTeamNumber() == ZONE_CONTESTED )
{
ReevaluateControllingTeam();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: set it so the team can no longer change, until a set controlling team action occurs
//-----------------------------------------------------------------------------
void CControlZone::InputLockControllingTeam( inputdata_t &inputdata )
{
LockControllingTeam();
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that sets the controlling team to the activator's team.
//-----------------------------------------------------------------------------
void CControlZone::InputSetTeam( inputdata_t &inputdata )
{
// Abort if it's already the defending team
if ( inputdata.pActivator->GetTeamNumber() == GetTeamNumber() )
return;
// set the new team
ChangeTeam(inputdata.pActivator->GetTeamNumber());
SetControllingTeam( inputdata.pActivator, GetTeamNumber() );
}
//-----------------------------------------------------------------------------
// Purpose: Changes the team controlling this zone
// Input : newTeam - the new team to change to
//-----------------------------------------------------------------------------
void CControlZone::SetControllingTeam( CBaseEntity *pActivator, int newTeam )
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to: %d\n", GetDebugName(), newTeam );
// remember this team as the defenders of the zone
m_iDefendingTeam = GetTeamNumber();
// reset state, firing the output
ChangeTeam(newTeam);
m_ControllingTeam.Set( GetTeamNumber(), pActivator, this );
m_iLocked = FALSE;
m_iTryingToChangeToTeam = 0;
SetNextThink( TICK_NEVER_THINK );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CControlZone::LockControllingTeam( void )
{
// never lock a zone in contested mode
if ( GetTeamNumber() == ZONE_CONTESTED )
return;
// zones never lock to the defenders
if ( GetTeamNumber() == m_iDefendingTeam )
return;
m_iLocked = TRUE;
}

View File

@@ -0,0 +1,60 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Control Zone entity
//
// $NoKeywords: $
//=============================================================================//
#ifndef CONTROLZONE_H
#define CONTROLZONE_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Purpose: Defines a team zone of control
// Usually the parent of many trigger entities
//-----------------------------------------------------------------------------
class CControlZone : public CBaseEntity
{
public:
DECLARE_CLASS( CControlZone, CBaseEntity );
DECLARE_SERVERCLASS();
void Spawn( void );
void StartTouch( CBaseEntity * );
void EndTouch( CBaseEntity * );
void Think( void );
virtual int UpdateTransmitState();
// input functions
void InputLockControllingTeam( inputdata_t &inputdata );
void InputSetTeam( inputdata_t &inputdata );
// internal methods
void LockControllingTeam( void );
void ReevaluateControllingTeam( void );
void SetControllingTeam( CBaseEntity *pActivator, int newTeam );
// outputs
int GetControllingTeam( void ) { return m_ControllingTeam.Get(); };
COutputInt m_ControllingTeam; // outputs the team currently controlling this spot, whenever it changes - this is -1 when contended
public:
// Data
CNetworkVar( int, m_nZoneNumber );
int m_iDefendingTeam; // the original defeind team
int m_iLocked; // no more changes, until a reset it called
int m_iLockAfterChange; // auto-lock after the control zone changes hands through combat
float m_flTimeTillCaptured; // time that the control zone has to be uncontested for it to succesfully change teams
float m_flTimeTillContested; // time that the control zone has to be contested for for it to change to Contested mode (no team)
int m_iTryingToChangeToTeam; // the team is trying to change to
CUtlVector< CHandle<CBaseTFPlayer> > m_ZonePlayerList; // List of all players in the zone at the moment
int m_iPlayersInZone[MAX_TF_TEAMS+1]; // count of players in the zone divided by team
DECLARE_DATADESC();
};
#endif // CONTROLZONE_H

View File

@@ -0,0 +1,123 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc.h"
#include "animation.h"
#include "vstdlib/random.h"
#include "h_cycler.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CCycler_TF2Commando : public CCycler
{
DECLARE_CLASS( CCycler_TF2Commando, CCycler );
public:
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual void Spawn( void );
virtual void Think( void );
// Inputs
void InputRaiseShield( inputdata_t &inputdata );
void InputLowerShield( inputdata_t &inputdata );
private:
CNetworkVar( bool, m_bShieldActive );
CNetworkVar( float, m_flShieldRaiseTime );
CNetworkVar( float, m_flShieldLowerTime );
};
IMPLEMENT_SERVERCLASS_ST(CCycler_TF2Commando, DT_Cycler_TF2Commando)
SendPropInt (SENDINFO(m_bShieldActive), 1, SPROP_UNSIGNED ),
SendPropFloat(SENDINFO(m_flShieldRaiseTime), 0, SPROP_NOSCALE ),
SendPropFloat(SENDINFO(m_flShieldLowerTime), 0, SPROP_NOSCALE ),
END_SEND_TABLE();
LINK_ENTITY_TO_CLASS( cycler_tf2commando, CCycler_TF2Commando );
LINK_ENTITY_TO_CLASS( cycler_aliencommando, CCycler_TF2Commando );
BEGIN_DATADESC( CCycler_TF2Commando )
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "RaiseShield", InputRaiseShield ),
DEFINE_INPUTFUNC( FIELD_VOID, "LowerShield", InputLowerShield ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCycler_TF2Commando::Spawn( void )
{
if (GetTeamNumber() == 1)
{
GenericCyclerSpawn( "models/player/human_commando.mdl", Vector(-16, -16, 0), Vector(16, 16, 72) );
}
else
{
GenericCyclerSpawn( "models/player/alien_commando.mdl", Vector(-16, -16, 0), Vector(16, 16, 72) );
}
m_bShieldActive = false;
BaseClass::Spawn();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCycler_TF2Commando::Think( void )
{
// Change sequence
if ( IsSequenceFinished() )
{
// Raising our shield?
if ( m_bShieldActive )
{
ResetSequence( LookupSequence( "ShieldUpIdle" ) );
}
else if ( GetSequence() == LookupSequence( "ShieldDown" ) )
{
ResetSequence( LookupSequence( "Idle" ) );
}
}
SetNextThink( gpGlobals->curtime + 0.1f );
if (m_animate)
{
StudioFrameAdvance ( );
}
}
//-----------------------------------------------------------------------------
// Purpose: Input that raises the cycler's shield
//-----------------------------------------------------------------------------
void CCycler_TF2Commando::InputRaiseShield( inputdata_t &inputdata )
{
if (m_animate)
{
m_bShieldActive = true;
ResetSequence( LookupSequence( "ShieldUp" ) );
m_flShieldRaiseTime = gpGlobals->curtime;
}
}
//-----------------------------------------------------------------------------
// Purpose: Input that lowers the cycler's shield
//-----------------------------------------------------------------------------
void CCycler_TF2Commando::InputLowerShield( inputdata_t &inputdata )
{
if (m_animate)
{
m_bShieldActive = false;
ResetSequence( LookupSequence( "ShieldDown" ) );
m_flShieldLowerTime = gpGlobals->curtime;
}
}

View File

@@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "entity_burn_effect.h"
IMPLEMENT_SERVERCLASS_ST_NOBASE( CEntityBurnEffect, DT_EntityBurnEffect )
SendPropEHandle( SENDINFO( m_hBurningEntity ) )
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( entity_burn_effect, CEntityBurnEffect );
CEntityBurnEffect* CEntityBurnEffect::Create( CBaseEntity *pBurningEntity )
{
CEntityBurnEffect *pEffect = static_cast<CEntityBurnEffect*>(CreateEntityByName( "entity_burn_effect" ));
if ( pEffect )
{
pEffect->m_hBurningEntity = pBurningEntity;
return pEffect;
}
else
{
return NULL;
}
}
int CEntityBurnEffect::UpdateTransmitState()
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
int CEntityBurnEffect::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
CBaseEntity *pEnt = m_hBurningEntity;
if ( pEnt )
return pEnt->ShouldTransmit( pInfo );
else
return FL_EDICT_DONTSEND;
}

View File

@@ -0,0 +1,41 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ENTITY_BURN_EFFECT_H
#define ENTITY_BURN_EFFECT_H
#ifdef _WIN32
#pragma once
#endif
#include "baseentity.h"
#include "server_class.h"
class CEntityBurnEffect : public CBaseEntity
{
public:
DECLARE_CLASS( CEntityBurnEffect, CBaseEntity );
DECLARE_SERVERCLASS();
static CEntityBurnEffect* Create( CBaseEntity *pBurningEntity );
// Overrides.
public:
virtual int UpdateTransmitState();
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
private:
CNetworkHandle( CBaseEntity, m_hBurningEntity );
};
#endif // ENTITY_BURN_EFFECT_H

View File

@@ -0,0 +1,201 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#define MAX_ROCK_MODELS 6
// Rock models
char *sRockModels[ MAX_ROCK_MODELS ] =
{
"models/props/cliffside/inhibitor_rocks/inhibitor_rock12.mdl",
"models/props/cliffside/inhibitor_rocks/inhibitor_rock13.mdl",
"models/props/cliffside/inhibitor_rocks/inhibitor_rock14.mdl",
"models/props/cliffside/inhibitor_rocks/inhibitor_rock19.mdl",
"models/props/cliffside/inhibitor_rocks/inhibitor_rock20.mdl",
"models/props/cliffside/inhibitor_rocks/inhibitor_rock21.mdl",
};
//-----------------------------------------------------------------------------
// Purpose: A falling rock entity
//-----------------------------------------------------------------------------
class CFallingRock : public CBaseAnimating
{
DECLARE_CLASS( CFallingRock, CBaseAnimating );
public:
DECLARE_DATADESC();
CFallingRock( void );
virtual void Spawn( void );
virtual void VPhysicsUpdate( IPhysicsObject *pPhysics );
static CFallingRock *Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, const AngularImpulse &vecRotationSpeed );
void RockTouch( CBaseEntity *pOther );
public:
};
BEGIN_DATADESC( CFallingRock )
// functions
DEFINE_FUNCTION( RockTouch ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( fallingrock, CFallingRock );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CFallingRock::CFallingRock( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFallingRock::Spawn( void )
{
SetModel( sRockModels[ random->RandomInt(0,MAX_ROCK_MODELS-1) ] );
SetMoveType( MOVETYPE_NONE );
m_takedamage = DAMAGE_NO;
// Create the object in the physics system
VPhysicsInitNormal( SOLID_BBOX, 0, false );
UTIL_SetSize( this, Vector(-4,-4,-4), Vector(4,4,4) );
SetTouch( RockTouch );
SetThink( SUB_Remove );
SetNextThink( gpGlobals->curtime + random->RandomFloat( 20.0, 30.0 ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFallingRock::VPhysicsUpdate( IPhysicsObject *pPhysics )
{
BaseClass::VPhysicsUpdate( pPhysics );
}
//-----------------------------------------------------------------------------
// Purpose: Create a falling rock
//-----------------------------------------------------------------------------
CFallingRock *CFallingRock::Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, const AngularImpulse &vecRotationSpeed )
{
CFallingRock *pRock = (CFallingRock*)CreateEntityByName("fallingrock");
UTIL_SetOrigin( pRock, vecOrigin );
pRock->SetLocalAngles( vecAngles );
pRock->Spawn();
IPhysicsObject *pPhysicsObject = pRock->VPhysicsGetObject();
if ( pPhysicsObject )
{
pPhysicsObject->AddVelocity( &vecVelocity, &vecRotationSpeed );
}
return pRock;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFallingRock::RockTouch( CBaseEntity *pOther )
{
}
//-----------------------------------------------------------------------------
// Purpose: A falling rock spawner entity
//-----------------------------------------------------------------------------
class CEnv_FallingRocks : public CPointEntity
{
DECLARE_CLASS( CEnv_FallingRocks, CPointEntity );
public:
DECLARE_DATADESC();
virtual void Spawn( void );
virtual void Precache( void );
void RockThink( void );
void InputSpawnRock( inputdata_t &inputdata );
public:
float m_flFallStrength;
float m_flRotationSpeed;
float m_flMinSpawnTime;
float m_flMaxSpawnTime;
COutputEvent m_pOutputRockSpawned;
};
BEGIN_DATADESC( CEnv_FallingRocks )
// Fields
DEFINE_KEYFIELD( m_flFallStrength, FIELD_FLOAT, "FallSpeed"),
DEFINE_KEYFIELD( m_flRotationSpeed, FIELD_FLOAT, "RotationSpeed"),
DEFINE_KEYFIELD( m_flMinSpawnTime, FIELD_FLOAT, "MinSpawnTime"),
DEFINE_KEYFIELD( m_flMaxSpawnTime, FIELD_FLOAT, "MaxSpawnTime"),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "SpawnRock", InputSpawnRock ),
// Outputs
DEFINE_OUTPUT( m_pOutputRockSpawned, "OnRockSpawned" ),
// Functions
DEFINE_FUNCTION( RockThink ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( env_fallingrocks, CEnv_FallingRocks );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnv_FallingRocks::Spawn( void )
{
Precache();
SetThink( RockThink );
SetNextThink( gpGlobals->curtime + random->RandomFloat( m_flMinSpawnTime, m_flMaxSpawnTime ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnv_FallingRocks::Precache( void )
{
for (int i = 0; i < MAX_ROCK_MODELS; i++ )
{
PrecacheModel( sRockModels[i] );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEnv_FallingRocks::RockThink( void )
{
// Spawn a rock
// Make it aim a little around the angle supplied
QAngle angFire = GetAbsAngles();
angFire.y += random->RandomFloat( -10, 10 );
Vector vecForward;
AngleVectors( angFire, &vecForward );
CFallingRock::Create( GetAbsOrigin(), GetAbsAngles(), (vecForward * m_flFallStrength), AngularImpulse(0,0,m_flRotationSpeed) );
// Fire our output
m_pOutputRockSpawned.FireOutput( NULL,this );
SetNextThink( gpGlobals->curtime + random->RandomFloat( m_flMinSpawnTime, m_flMaxSpawnTime ) );
}
//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CEnv_FallingRocks::InputSpawnRock( inputdata_t &inputdata )
{
RockThink();
}

View File

@@ -0,0 +1,618 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "Env_Meteor.h"
#include "entitylist.h"
#include "vphysics_interface.h"
#include "tier1/strtools.h"
#include "mapdata_shared.h"
#include "sharedinterface.h"
#include "skycamera.h"
#include "ispatialpartition.h"
#include "gameinterface.h"
#include "props.h"
#include "tf_func_resource.h"
#include "resource_chunk.h"
#include "ndebugoverlay.h"
//=============================================================================
//
// Enumerator for swept bbox collision.
//
class CCollideList : public IEntityEnumerator
{
public:
CCollideList( Ray_t *pRay, CBaseEntity* pIgnoreEntity, int nContentsMask ) :
m_Entities( 0, 32 ), m_pIgnoreEntity( pIgnoreEntity ),
m_nContentsMask( nContentsMask ), m_pRay(pRay) {}
virtual bool EnumEntity( IHandleEntity *pHandleEntity )
{
trace_t tr;
enginetrace->ClipRayToEntity( *m_pRay, m_nContentsMask, pHandleEntity, &tr );
if (( tr.fraction < 1.0f ) || (tr.startsolid) || (tr.allsolid))
{
CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
m_Entities.AddToTail( pEntity );
}
return true;
}
CUtlVector<CBaseEntity*> m_Entities;
private:
CBaseEntity *m_pIgnoreEntity;
int m_nContentsMask;
Ray_t *m_pRay;
};
//=============================================================================
//
// Meteor Factory Functions
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMeteorFactory::CreateMeteor( int nID, int iType,
const Vector &vecPosition, const Vector &vecDirection,
float flSpeed, float flStartTime, float flDamageRadius,
const Vector &vecTriggerMins, const Vector &vecTriggerMaxs )
{
CEnvMeteor::Create( nID, iType, vecPosition, vecDirection, flSpeed, flStartTime, flDamageRadius,
vecTriggerMins, vecTriggerMaxs );
}
//=============================================================================
//
// Meteor Spawner Functions
//
void SendProxy_MeteorTargetPositions( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
{
CEnvMeteorSpawnerShared *pMeteorSpawner = ( CEnvMeteorSpawnerShared* )pData;
pOut->m_Vector[0] = pMeteorSpawner->m_aTargets[iElement].m_vecPosition.x;
pOut->m_Vector[1] = pMeteorSpawner->m_aTargets[iElement].m_vecPosition.y;
pOut->m_Vector[2] = pMeteorSpawner->m_aTargets[iElement].m_vecPosition.z;
}
void SendProxy_MeteorTargetRadii( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
{
CEnvMeteorSpawnerShared *pMeteorSpawner = ( CEnvMeteorSpawnerShared* )pData;
pOut->m_Float = pMeteorSpawner->m_aTargets[iElement].m_flRadius;
}
int SendProxyArrayLength_MeteorTargets( const void *pStruct, int objectID )
{
CEnvMeteorSpawnerShared *pMeteorSpawner = ( CEnvMeteorSpawnerShared* )pStruct;
return pMeteorSpawner->m_aTargets.Count();
}
// Link the name "env_meteorspawner" to the CMeteorSpawner class. This
// links the WC entity with the game code.
LINK_ENTITY_TO_CLASS( env_meteorspawner, CEnvMeteorSpawner );
BEGIN_DATADESC( CEnvMeteorSpawner )
// Key Fields.
DEFINE_KEYFIELD( m_SpawnerShared.m_iMeteorType, FIELD_INTEGER, "MeteorType" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_bSkybox, FIELD_INTEGER, "SpawnInSkybox" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_flMinSpawnTime, FIELD_FLOAT, "SpawnIntervalMin" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_flMaxSpawnTime, FIELD_FLOAT, "SpawnIntervalMax" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_nMinSpawnCount, FIELD_INTEGER, "SpawnCountMin" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_nMaxSpawnCount, FIELD_INTEGER, "SpawnCountMax" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_flMinSpeed, FIELD_FLOAT, "MeteorSpeedMin" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_flMaxSpeed, FIELD_FLOAT, "MeteorSpeedMax" ),
DEFINE_KEYFIELD( m_SpawnerShared.m_flMeteorDamageRadius, FIELD_FLOAT, "MeteorDamageRadius" ),
DEFINE_KEYFIELD( m_fDisabled, FIELD_BOOLEAN, "StartDisabled" ),
// Function Pointers.
DEFINE_FUNCTION( MeteorSpawnerThink ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
END_DATADESC()
BEGIN_SEND_TABLE_NOBASE( CEnvMeteorSpawnerShared, DT_EnvMeteorSpawnerShared )
// Setup (read from) Worldcraft.
SendPropInt ( SENDINFO( m_iMeteorType ), 8, SPROP_UNSIGNED ),
SendPropInt ( SENDINFO( m_bSkybox ), 4, SPROP_UNSIGNED ),
SendPropFloat ( SENDINFO( m_flMinSpawnTime ), 0, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO( m_flMaxSpawnTime ), 0, SPROP_NOSCALE ),
SendPropInt ( SENDINFO( m_nMinSpawnCount ), 16, SPROP_UNSIGNED ),
SendPropInt ( SENDINFO( m_nMaxSpawnCount ), 16, SPROP_UNSIGNED ),
SendPropFloat ( SENDINFO( m_flMinSpeed ), 0, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO( m_flMaxSpeed ), 0, SPROP_NOSCALE ),
// Setup through Init.
SendPropFloat ( SENDINFO( m_flStartTime ), -1, SPROP_NOSCALE ),
SendPropInt ( SENDINFO( m_nRandomSeed ), -1, SPROP_UNSIGNED ),
SendPropVector ( SENDINFO( m_vecMinBounds ), -1, SPROP_NOSCALE ),
SendPropVector ( SENDINFO( m_vecMaxBounds ), -1, SPROP_NOSCALE ),
SendPropVector ( SENDINFO( m_vecTriggerMins ), -1, SPROP_NOSCALE ),
SendPropVector ( SENDINFO( m_vecTriggerMaxs ), -1, SPROP_NOSCALE ),
// Target List
SendPropArray2( SendProxyArrayLength_MeteorTargets,
SendPropVector( "meteortargetposition_array_element", 0, 0, 0, SPROP_NOSCALE, 0, 0, SendProxy_MeteorTargetPositions ),
16, 0, "meteortargetposition_array" ),
SendPropArray2( SendProxyArrayLength_MeteorTargets,
SendPropFloat( "meteortargetradius_array_element", 0, 0, 0, SPROP_NOSCALE, 0, 0, SendProxy_MeteorTargetRadii ),
16, 0, "meteortargetradius_array" )
END_SEND_TABLE()
// This table encodes the CBaseEntity data.
IMPLEMENT_SERVERCLASS_ST_NOBASE( CEnvMeteorSpawner, DT_EnvMeteorSpawner )
SendPropDataTable ( SENDINFO_DT( m_SpawnerShared ), &REFERENCE_SEND_TABLE( DT_EnvMeteorSpawnerShared ) ),
SendPropInt ( SENDINFO( m_fDisabled ), 1, SPROP_UNSIGNED ),
END_SEND_TABLE()
// Meteor Models
char *strResourceMeteorModels[2] =
{
"models/props/common/meteorites/meteor04.mdl",
"models/props/common/meteorites/meteor05.mdl",
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CEnvMeteorSpawner::CEnvMeteorSpawner()
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::Spawn( void )
{
// Pre-cache.
Precache();
// Server-side is not visible -- for collision only.
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
AddEffects( EF_NODRAW );
// Set the "brush model" size and link into the world.
SetModel( STRING( GetModelName() ) );
// Set the think function and time.
if ( !m_fDisabled )
{
SetThink( MeteorSpawnerThink );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::InputEnable( inputdata_t &inputdata )
{
m_fDisabled = false;
m_SpawnerShared.m_flStartTime = gpGlobals->curtime;
m_SpawnerShared.m_flNextSpawnTime = m_SpawnerShared.m_flStartTime + m_SpawnerShared.m_flMaxSpawnTime;
// Probably should set this as a message begin, etc..... will get to this later!!
//
// CEntityMessageFilter filter( this, "CEnvMeteorSpawner" );
// MessageBegin( filter, 0 );
// WRITE_LONG( m_SpawnerShared.m_flStartTime );
// WRITE_LONG( m_SpawnerShared.m_flNextSpawnTime );
// MessageEnd();
// Set the think function and time.
SetThink( MeteorSpawnerThink );
SetNextThink( gpGlobals->curtime + m_SpawnerShared.m_flNextSpawnTime );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::InputDisable( inputdata_t &inputdata )
{
m_fDisabled = true;
SetThink( NULL );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::Get3DSkyboxWorldBounds( Vector &vecTriggerMins,
Vector &vecTriggerMaxs )
{
CBaseEntity *pEntity = gEntList.FindEntityByClassname( NULL, "trigger_skybox2world" );
if ( pEntity && pEntity->edict() )
{
pEntity->CollisionProp()->WorldSpaceAABB( &vecTriggerMins, &vecTriggerMaxs );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::Precache( void )
{
// Precache the meteor models!
for ( int iType = 0; iType < 2; iType++ )
{
PrecacheModel( strResourceMeteorModels[iType] );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::MeteorSpawnerThink( void )
{
SetNextThink( gpGlobals->curtime + m_SpawnerShared.MeteorThink( gpGlobals->curtime ) );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CEnvMeteorSpawner::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
if ( m_SpawnerShared.m_bSkybox )
return FL_EDICT_ALWAYS;
return BaseClass::ShouldTransmit( pInfo );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorSpawner::Activate( void )
{
// Parse the entity list looking for targets!
int nEntityCount = engine->GetEntityCount();
for ( int iEntity = 0; iEntity < nEntityCount; ++iEntity )
{
edict_t *pEdict = engine->PEntityOfEntIndex( iEntity );
if ( !pEdict || pEdict->IsFree() )
continue;
CBaseEntity *pEntity = GetContainingEntity( pEdict );
if ( !pEntity )
continue;
if ( pEntity->GetFlags()& FL_STATICPROP )
continue;
if ( !Q_strcmp( pEntity->GetClassname(), "env_meteortarget" ) )
{
CEnvMeteorTarget *pMeteorTarget = static_cast<CEnvMeteorTarget*>( pEntity );
if ( pMeteorTarget && pMeteorTarget->m_target != NULL_STRING )
{
if ( !Q_strcmp( STRING( pMeteorTarget->m_target ), STRING( GetEntityName() ) ) )
{
m_SpawnerShared.AddToTargetList( pMeteorTarget->GetLocalOrigin(), pMeteorTarget->m_flRadius );
}
}
}
}
// Get 3d skybox world trigger bounds.
Vector vecTriggerMins, vecTriggerMaxs;
Get3DSkyboxWorldBounds( vecTriggerMins, vecTriggerMaxs );
// Initialize the spawner.
float flTime = gpGlobals->curtime;
m_SpawnerShared.Init( &m_Factory, 0/* seed */, flTime,
WorldAlignMins(), WorldAlignMaxs(), vecTriggerMins, vecTriggerMaxs );
// Setup next think.
if ( !m_fDisabled )
{
SetNextThink( gpGlobals->curtime + m_SpawnerShared.m_flNextSpawnTime );
}
}
//=============================================================================
//
// Meteor Target Functions
//
LINK_ENTITY_TO_CLASS( env_meteortarget, CEnvMeteorTarget );
BEGIN_DATADESC( CEnvMeteorTarget )
// Key Fields.
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "EffectRadius" ),
END_DATADESC()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CEnvMeteorTarget::CEnvMeteorTarget()
{
m_iTargetID = -1;
m_flRadius = 1.0f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteorTarget::Spawn( void )
{
BaseClass::Spawn();
}
//=============================================================================
//
// Meteor Functions
//
//
// NOTE: The server-side meteor code has not really been tested. I do not
// trust that is works correctly and/or cleans itself up nicely!
//
LINK_ENTITY_TO_CLASS( env_meteor, CEnvMeteor );
BEGIN_DATADESC( CEnvMeteor )
// Function Pointers.
DEFINE_FUNCTION( MeteorSkyboxThink ),
DEFINE_FUNCTION( MeteorWorldThink ),
END_DATADESC()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CEnvMeteor::CEnvMeteor()
{
m_vecMin.Init( -10.0f, -10.0f, -10.0f );
m_vecMax.Init( 10.0f, 10.0f, 10.0f );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CEnvMeteor *CEnvMeteor::Create( int nID, int iMeteorType,
const Vector &vecOrigin, const Vector &vecDirection,
float flSpeed, float flStartTime, float flDamageRadius,
const Vector &vecTriggerMins, const Vector &vecTriggerMaxs )
{
CEnvMeteor *pMeteor = ( CEnvMeteor* )CreateEntityByName( "env_meteor" );
if ( pMeteor )
{
pMeteor->m_Meteor.Init( nID, flStartTime, METEOR_PASSIVE_TIME, vecOrigin, vecDirection, flSpeed,
flDamageRadius, vecTriggerMins, vecTriggerMaxs );
// If the meteor will never enter the world, then don't bother with a server-side version.
if ( pMeteor->m_Meteor.m_flWorldEnterTime == METEOR_INVALID_TIME )
{
UTIL_Remove( pMeteor );
}
// Handle forward simulation.
if ( ( pMeteor->m_Meteor.m_flStartTime + METEOR_MAX_LIFETIME ) < gpGlobals->curtime )
{
UTIL_Remove( pMeteor );
}
pMeteor->Spawn();
pMeteor->SetNextThink( gpGlobals->curtime + pMeteor->m_Meteor.m_flWorldEnterTime );
}
return pMeteor;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CEnvMeteor::Spawn( void )
{
// Pass data.
BaseClass::Spawn();
int iModel = modelinfo->GetModelIndex( "models/props/common/meteorites/meteor04.mdl" );
if ( iModel > 0 )
{
const model_t *pModel = modelinfo->GetModel( iModel );
modelinfo->GetModelBounds( pModel, m_vecMin, m_vecMax );
}
// Assumes we start life in a skybox!
SetThink( MeteorSkyboxThink );
m_bPrevInSkybox = true;
}
//-----------------------------------------------------------------------------
// Purpose: This think function should be called at the time when the meteor
// will be leaving the skybox and entering the world.
//-----------------------------------------------------------------------------
void CEnvMeteor::MeteorSkyboxThink( void )
{
SetThink( MeteorWorldThink );
SetNextThink( gpGlobals->curtime + 0.2f );
}
//-----------------------------------------------------------------------------
// Purpose: This think function simulates (moves/collides) the meteor while in
// the world.
//-----------------------------------------------------------------------------
void CEnvMeteor::MeteorWorldThink( void )
{
// Get the current time.
float flTime = gpGlobals->curtime;
// Convert if need be!
if ( m_bPrevInSkybox )
{
m_Meteor.ConvertFromSkyboxToWorld();
UTIL_SetOrigin( this, m_Meteor.m_vecStartPosition );
m_bPrevInSkybox = false;
}
// Update meteor position for swept collision test.
Vector vecEndPosition;
m_Meteor.GetPositionAtTime( flTime, vecEndPosition );
// Debugging!!
// NDebugOverlay::Box( GetAbsOrigin(), m_vecMin * 0.5f, m_vecMax * 0.5f, 255, 255, 0, 0, 5 );
// NDebugOverlay::Box( vecEndPosition, m_vecMin, m_vecMax, 255, 0, 0, 0, 5 );
Ray_t ray;
ray.Init( GetAbsOrigin(), vecEndPosition, m_vecMin, m_vecMax );
CCollideList collideList( &ray, this, MASK_SOLID );
enginetrace->EnumerateEntities( ray, false, &collideList );
// Now get each entity and react accordinly!
for( int iEntity = collideList.m_Entities.Count(); --iEntity >= 0; )
{
CBaseEntity *pEntity = collideList.m_Entities[iEntity];
if ( pEntity )
{
Vector vecForceDir = m_Meteor.m_vecDirection;
// Check for a physics object and apply force!
IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
if ( pPhysObject )
{
// float flMass = pPhysObject->GetMass();
// Send it flying!!!
vecForceDir *= 5000000000000.0f;
pPhysObject->ApplyForceCenter( vecForceDir );
}
if ( pEntity->m_takedamage )
{
CTakeDamageInfo info( this, this, 200.0f, DMG_CLUB );
CalculateExplosiveDamageForce( &info, vecForceDir, pEntity->GetAbsOrigin() );
pEntity->TakeDamage( info );
}
}
}
trace_t trace;
UTIL_TraceHull( GetAbsOrigin(), vecEndPosition, m_vecMin, m_vecMax,
MASK_NPCWORLDSTATIC, this, COLLISION_GROUP_NONE, &trace );
if( ( trace.fraction < 1.0f ) && !( trace.surface.flags & SURF_SKY ) )
{
CBaseEntity *pEntity = trace.m_pEnt;
if ( pEntity )
{
// Hit the world? The meteor is destroyed!
if ( pEntity->GetSolid() == SOLID_BSP )
{
#if 0
// Suppress resources for now!!
// Create a random number or resource chunks.
int nChunkCount = random->RandomInt( 0, 4 );
for( int iChunk = 0; iChunk < nChunkCount; ++iChunk )
{
// Generate a random velocity vector.
Vector vVelocity = Vector( random->RandomFloat( -20,20 ), random->RandomFloat( -20,20 ), random->RandomFloat( 100,150 ) );
CResourceChunk::Create( false, GetAbsOrigin(), vVelocity );
}
#endif
// Splash damage!
Vector vecImpactPoint;
vecImpactPoint = GetAbsOrigin() + ( ( vecEndPosition - GetAbsOrigin() ) * trace.fraction );
// Debugging!!
// NDebugOverlay::Box( vecImpactPoint, m_vecMin, m_vecMax, 0, 255, 0, 0, 5 );
//Iterate on all entities in the vicinity.
for ( CEntitySphereQuery sphere( vecImpactPoint, m_Meteor.GetDamageRadius() ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
{
// Get distance to object and use it as a scale value.
Vector vecSegment;
vecSegment = pEntity->GetAbsOrigin() - vecImpactPoint;
float flDistance = vecSegment.Length();
float flScale = flDistance / ( m_Meteor.GetDamageRadius() * 0.75f );
if ( flScale > 1.0f )
{
flScale = 1.0f;
}
Vector vecForceDir = m_Meteor.m_vecDirection;
// Check for a physics object and apply force!
IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
if ( pPhysObject )
{
// float flMass = pPhysObject->GetMass();
// Send it flying!!!
vecForceDir *= 5000000000000.0f * flScale;
pPhysObject->ApplyForceCenter( vecForceDir );
}
if ( pEntity->m_takedamage )
{
CTakeDamageInfo info( this, this, 300.0f * flScale, DMG_CLUB );
CalculateExplosiveDamageForce( &info, vecForceDir, pEntity->GetAbsOrigin() );
pEntity->TakeDamage( info );
}
}
UTIL_Remove( this );
return;
}
}
}
// Always move full movement.
UTIL_SetOrigin( this, vecEndPosition );
SetNextThink( gpGlobals->curtime + 0.2f );
// Check for death.
if ( flTime >= m_Meteor.m_flWorldExitTime )
{
UTIL_Remove( this );
return;
}
}
//=============================================================================
//
// Shooting Star Spawner Functionality.
//
// Link the name "env_meteorspawner" to the CMeteorSpawner class. This
// links the WC entity with the game code.
LINK_ENTITY_TO_CLASS( env_shootingstarspawner, CShootingStarSpawner );
BEGIN_DATADESC( CShootingStarSpawner )
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_flSpawnInterval, FIELD_FLOAT, "SpawnInterval" ),
DEFINE_KEYFIELD_NOT_SAVED( m_bSkybox, FIELD_INTEGER, "SpawnInSkybox" ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CShootingStarSpawner, DT_ShootingStarSpawner )
SendPropFloat( SENDINFO( m_flSpawnInterval ), -1, SPROP_NOSCALE ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CShootingStarSpawner::CShootingStarSpawner()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CShootingStarSpawner::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
// Always send shooting star spawners if they are in the skybox!
if ( m_bSkybox )
return FL_EDICT_ALWAYS ;
return BaseClass::ShouldTransmit( pInfo );
}

View File

@@ -0,0 +1,146 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ENV_METEOR_H
#define ENV_METEOR_H
#pragma once
#include "BaseEntity.h"
#include "BaseAnimating.h"
#include "Env_Meteor_Shared.h"
#include "utlvector.h"
//=============================================================================
//
// Server-side Meteor Factory Class
//
class CMeteorFactory : public IMeteorFactory
{
public:
void CreateMeteor( int nID, int iType, const Vector &vecPosition,
const Vector &vecDirection, float flSpeed, float flStartTime,
float flDamageRadius,
const Vector &vecTriggerMins, const Vector &vecTriggerMaxs );
};
//=============================================================================
//
// Meteor Spawner Class
//
class CEnvMeteorSpawner : public CBaseEntity
{
public:
DECLARE_CLASS( CEnvMeteorSpawner, CBaseEntity );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
CEnvMeteorSpawner();
void Spawn( void );
void Precache( void );
void MeteorSpawnerThink( void );
int UpdateTransmitState() { return SetTransmitState( FL_EDICT_FULLCHECK ); }
int ShouldTransmit( const CCheckTransmitInfo *pInfo );
void Activate( void );
private:
// Inputs
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
void Get3DSkyboxWorldBounds( Vector &vecTriggerMins, Vector &vecTriggerMaxs );
CMeteorFactory m_Factory;
CNetworkVarEmbedded( CEnvMeteorSpawnerShared, m_SpawnerShared );
CNetworkVar( bool, m_fDisabled ); // Spawner active (trigger). NOTE: uses an f to remain consistent
// with entity input system
};
//=============================================================================
//
// Meteor Target Class
//
class CEnvMeteorTarget : public CBaseEntity
{
public:
DECLARE_CLASS( CEnvMeteorTarget, CBaseEntity );
DECLARE_DATADESC();
CEnvMeteorTarget();
void Spawn( void );
int m_iTargetID;
float m_flRadius;
};
//=============================================================================
//
// Meteor Class
//
class CEnvMeteor : public CBaseAnimating
{
DECLARE_CLASS( CEnvMeteor, CBaseAnimating );
public:
DECLARE_DATADESC();
//-------------------------------------------------------------------------
// Initialization
//-------------------------------------------------------------------------
CEnvMeteor();
static CEnvMeteor *Create( int nID, int iMeteorType, const Vector &vecOrigin,
const Vector &vecDirection, float flSpeed, float flStartTime,
float flDamageRadius,
const Vector &vecTriggerMins, const Vector &vecTriggerMaxs );
void Spawn( void );
//-------------------------------------------------------------------------
// Think(s)
//-------------------------------------------------------------------------
void MeteorSkyboxThink( void );
void MeteorWorldThink( void );
private:
CEnvMeteorShared m_Meteor;
bool m_bPrevInSkybox;
Vector m_vecMin, m_vecMax;
};
//=============================================================================
//
// Shooting Star Spawner Class
//
class CShootingStarSpawner : public CBaseEntity
{
DECLARE_CLASS( CShootingStarSpawner, CBaseEntity );
public:
CShootingStarSpawner();
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_FULLCHECK ); }
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
public:
CNetworkVar( float, m_flSpawnInterval ); // How often do I spawn shooting stars?
bool m_bSkybox; // Is the spawner in the skybox?
};
#endif // ENV_METEOR_H

View File

@@ -0,0 +1,301 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "fire_damage_mgr.h"
#include "entity_burn_effect.h"
#include "gasoline_blob.h"
#include "tf_obj.h"
#include "ai_basenpc.h"
#include "tf_gamerules.h"
#define FIRE_DAMAGE_APPLY_INTERVAL 0.5 // Apply the damage at this interval.
#define FIRE_DECAY_END_VALUE 0.00001
// No more damage from fire can be applied to a player per second.
#define MAX_FIRE_DAMAGE_PER_SECOND 15
// The fire heat uses exponential decay. It goes from MAX_FIRE_DAMAGE_PER_SECOND to
// FIRE_DECAY_END_VALUE in FIRE_DECAY_SECONDS.
#define FIRE_DECAY_SECONDS 3
ConVar fire_damageall( "fire_damageall", "0", 0, "Enable fire damaging team members." );
bool CFireDamageMgr::Init()
{
m_flApplyDamageCountdown = FIRE_DAMAGE_APPLY_INTERVAL;
// Fire decays exponentially: B = A * e^(-kt)
// So we set B=FIRE_DECAY_END_VALUE, A=flMaxDamagePerSecond, and t=flFireDecaySeconds, then solve for K.
m_flMaxDamagePerSecond = MAX_FIRE_DAMAGE_PER_SECOND;
m_flDecayConstant = -log( FIRE_DECAY_END_VALUE / m_flMaxDamagePerSecond ) / FIRE_DECAY_SECONDS;
return true;
}
void CFireDamageMgr::AddDamage( CBaseEntity *pTarget, CBaseEntity *pAttacker, float flDamageAccel, bool bMakeBurnEffect )
{
FOR_EACH_LL( m_DamageEnts, iDamageEnt )
{
CDamageEnt *pEnt = &m_DamageEnts[iDamageEnt];
if ( pEnt->m_hEnt != pTarget )
continue;
for ( int i=0; i < pEnt->m_nAttackers; i++ )
{
if ( pEnt->m_Attackers[i].m_hAttacker == pAttacker )
{
pEnt->m_Attackers[i].m_flVelocity += flDamageAccel * gpGlobals->frametime;
return;
}
}
if ( pEnt->m_nAttackers < CDamageEnt::MAX_ATTACKERS )
{
// Add a new attacker.
pEnt->m_Attackers[pEnt->m_nAttackers].Init( pAttacker, flDamageAccel * gpGlobals->frametime );
++pEnt->m_nAttackers;
return;
}
else
{
// No room for more attackers.
Warning( "CFireDamageMgr: ran out of attackers\n" );
return;
}
}
// Add a new CDamageEnt.
int iNew = m_DamageEnts.AddToTail();
CDamageEnt *pEnt = &m_DamageEnts[iNew];
pEnt->m_hEnt = pTarget;
pEnt->m_bWasAlive = pTarget->IsAlive();
pEnt->m_nAttackers = 1;
pEnt->m_Attackers[0].Init( pAttacker, flDamageAccel * gpGlobals->frametime );
if ( bMakeBurnEffect )
pEnt->m_pBurnEffect = CEntityBurnEffect::Create( pTarget );
else
pEnt->m_pBurnEffect = NULL;
}
void CFireDamageMgr::RemoveDamageEnt( int iEnt )
{
UTIL_Remove( m_DamageEnts[iEnt].m_pBurnEffect );
m_DamageEnts.Remove( iEnt );
}
void CFireDamageMgr::FrameUpdatePostEntityThink()
{
VPROF( "CFireDamageMgr::FrameUpdatePostEntityThink" );
float frametime = gpGlobals->frametime;
// Update the damage countdown.
m_flApplyDamageCountdown -= gpGlobals->frametime;
bool bApplyDamageThisFrame = false;
if ( m_flApplyDamageCountdown <= 0 )
{
bApplyDamageThisFrame = true;
m_flApplyDamageCountdown += FIRE_DAMAGE_APPLY_INTERVAL;
}
// (-kt)
// Figure out how much all the damage decays this frame: e
float flFrameDecay = pow( 2.718281828459045235360, -m_flDecayConstant * frametime );
int iNext;
for ( int iCur = m_DamageEnts.Head(); iCur != m_DamageEnts.InvalidIndex(); iCur = iNext )
{
iNext = m_DamageEnts.Next( iCur );
CDamageEnt *pEnt = &m_DamageEnts[iCur];
// If the entity was dead and is now alive, stop damage to them so their new body doesn't burn.
if ( !pEnt->m_hEnt.Get() || ( !pEnt->m_bWasAlive && pEnt->m_hEnt->IsAlive() ) )
{
RemoveDamageEnt( iCur );
pEnt = NULL;
continue;
}
pEnt->m_bWasAlive = pEnt->m_hEnt->IsAlive();
// Sum up each attacker's velocity.
float flTotalVelocity = 0;
for ( int i=0; i < pEnt->m_nAttackers; i++ )
flTotalVelocity += pEnt->m_Attackers[i].m_flVelocity;
// Figure out each attacker's contribution.
float flContributionPercent[CDamageEnt::MAX_ATTACKERS];
for ( i=0; i < pEnt->m_nAttackers; i++ )
flContributionPercent[i] = pEnt->m_Attackers[i].m_flVelocity / flTotalVelocity;
// Decay each attacker's velocity.
flTotalVelocity *= flFrameDecay;
// Uniformly scale each attacker's velocity down so the sum total doesn't exceed our maximum.
float flPercentScale = 1;
if ( flTotalVelocity > m_flMaxDamagePerSecond )
flPercentScale = m_flMaxDamagePerSecond / flTotalVelocity;
for ( i=0; i < pEnt->m_nAttackers; i++ )
{
CDamageAttacker *pAttacker = &pEnt->m_Attackers[i];
pAttacker->m_flVelocity *= flFrameDecay * flPercentScale;
bool bEntsValid = (pEnt->m_Attackers[i].m_hAttacker.Get() != NULL);
if ( !bEntsValid ||
pEnt->m_Attackers[i].m_flVelocity <= 0.001 )
{
if ( bEntsValid )
ApplyCollectedDamage( pEnt, i ); // Apply the last-remaining damage from this guy.
Q_memmove( &pEnt->m_Attackers[i], &pEnt->m_Attackers[i+1], sizeof( pEnt->m_Attackers[0] ) * (pEnt->m_nAttackers-i-1) );
Q_memmove( &flContributionPercent[i], &flContributionPercent[i+1], sizeof( flContributionPercent[0] ) * (pEnt->m_nAttackers-i-1) );
--pEnt->m_nAttackers;
if ( pEnt->m_nAttackers == 0 )
{
// This ent isn't being damaged anymore.
RemoveDamageEnt( iCur );
break;
}
--i;
}
// Update their current damage sum and maybe apply the damage.
pAttacker->m_flDamageSum += pAttacker->m_flVelocity * frametime;
if ( bApplyDamageThisFrame )
{
ApplyCollectedDamage( pEnt, i );
}
}
}
}
float GetFireDamageScale( CBaseEntity *pEnt )
{
// Objects have a lot more health and we want them to take damage faster.
if ( dynamic_cast< CBaseObject* >( pEnt ) )
return 4;
else
return 1;
}
void CFireDamageMgr::ApplyCollectedDamage( CFireDamageMgr::CDamageEnt *pEnt, int iAttacker )
{
CDamageAttacker *pAttacker = &pEnt->m_Attackers[iAttacker];
CTakeDamageInfo info( NULL, pAttacker->m_hAttacker, pAttacker->m_flDamageSum * GetFireDamageScale( pEnt->m_hEnt ), DMG_BURN );
pEnt->m_hEnt->TakeDamage( info );
pAttacker->m_flDamageSum = 0;
}
// ------------------------------------------------------------------------------------------------ //
// Global functions.
// ------------------------------------------------------------------------------------------------ //
bool IsBurnableEnt( CBaseEntity *pEntity, int iIgnoreTeam )
{
if ( pEntity->m_takedamage == DAMAGE_NO )
return false;
CGasolineBlob *pBlob = dynamic_cast< CGasolineBlob* >( pEntity );
if ( pBlob )
{
return !pBlob->IsLit();
}
if ( pEntity->GetTeamNumber() == iIgnoreTeam && !fire_damageall.GetInt() )
{
// Don't damage anyone on the pyro's team (including the pyro himself).
return false;
}
// Now only allow specific types of objects to be damaged.
if ( dynamic_cast< CBasePlayer* >( pEntity ) ||
dynamic_cast< CAI_BaseNPC* >( pEntity ) ||
dynamic_cast< CBaseObject* >( pEntity ) )
{
return true;
}
return false;
}
int FindBurnableEntsInSphere(
CBaseEntity **ents,
float *dists,
int nMaxEnts,
const Vector &vecCenter,
float flSearchRadius,
CBaseEntity *pOwner )
{
Assert( nMaxEnts > 0 );
int nOutEnts = 0;
CBaseEntity *pEntity;
for ( CEntitySphereQuery sphere( vecCenter, flSearchRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
{
if ( !IsBurnableEnt( pEntity, pOwner->GetTeamNumber() ) )
continue;
// Make sure it's not blocked.
trace_t tr;
Vector vCenter = pEntity->WorldSpaceCenter();
UTIL_TraceLine ( vecCenter, vCenter, MASK_SHOT & (~CONTENTS_HITBOX), NULL, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
continue;
if ( TFGameRules()->IsTraceBlockedByWorldOrShield( vecCenter, vCenter, pOwner, DMG_BURN | DMG_PROBE, &tr ) )
continue;
// Make sure it's in range.
const Vector &mins = pEntity->WorldAlignMins();
const Vector &maxs = pEntity->WorldAlignMaxs();
float approxTargetRadius = ( Vector( maxs.x, maxs.y, 0 ) - Vector( mins.x, mins.y, 0 )).Length() * 0.5f;
float flDistFromCenter = ( vecCenter - tr.endpos ).Length() - approxTargetRadius;
ents[nOutEnts] = pEntity;
dists[nOutEnts] = flDistFromCenter;
nOutEnts++;
if ( nOutEnts >= nMaxEnts )
return nOutEnts;
}
return nOutEnts;
}
CFireDamageMgr g_FireDamageMgr;
CFireDamageMgr* GetFireDamageMgr()
{
return &g_FireDamageMgr;
}

View File

@@ -0,0 +1,121 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef FIRE_DAMAGE_MGR_H
#define FIRE_DAMAGE_MGR_H
#ifdef _WIN32
#pragma once
#endif
#include "igamesystem.h"
#include "utllinkedlist.h"
#include "ehandle.h"
class CEntityBurnEffect;
// ------------------------------------------------------------------------------------------ //
// CFireDamageMgr.
//
// This class manages fire damage being applied to entities. It uses velocity, acceleration,
// and decay to model fire damage building up, and it puts a cap on the maximum amount of damage
// an entity can take from fire during a given frame.
// ------------------------------------------------------------------------------------------ //
class CFireDamageMgr : public CAutoGameSystem
{
// Overrides.
public:
virtual bool Init();
virtual void FrameUpdatePostEntityThink();
public:
// Apply fire damage to an entity. flDamageAccel is in units per second, so in the absence of
// decay, it equals velocity increase per second.
//
// NOTE: the damage acceleration should always be greater than the decay per second, or no damage
// will be applied since it will decay faster than
void AddDamage( CBaseEntity *pTarget, CBaseEntity *pAttacker, float flDamageAccel, bool bMakeBurnEffect );
private:
class CDamageAttacker
{
public:
void Init( CBaseEntity *pAttacker, float flVelocity )
{
m_hAttacker = pAttacker;
m_flVelocity = flVelocity;
m_flDamageSum = 0;
}
EHANDLE m_hAttacker;
float m_flVelocity; // Current damage velocity.
float m_flDamageSum; // Damage is summed up and applied a couple times per second instead of
// each frame since fractional damage is rounded to 1.
};
class CDamageEnt
{
public:
enum
{
MAX_ATTACKERS = 4
};
bool m_bWasAlive;
EHANDLE m_hEnt;
CHandle<CEntityBurnEffect> m_pBurnEffect;
// Each attacker gets credit for a portion of
CDamageAttacker m_Attackers[MAX_ATTACKERS];
int m_nAttackers;
};
private:
void ApplyCollectedDamage( CFireDamageMgr::CDamageEnt *pEnt, int iAttacker );
void RemoveDamageEnt( int iEnt );
private:
CUtlLinkedList<CDamageEnt,int> m_DamageEnts;
float m_flMaxDamagePerSecond;
float m_flDecayConstant;
// This counts down to zero so we only apply fire damage every so often.
float m_flApplyDamageCountdown;
};
// Returns true if the entity is burnable by the specified team.
bool IsBurnableEnt( CBaseEntity *pEntity, int iTeam );
// This is used by the flamethrower and the burning gasoline blobs to find entities to burn.
int FindBurnableEntsInSphere(
CBaseEntity **ents,
float *dists,
int nMaxEnts,
const Vector &vecCenter,
float flSearchRadius,
CBaseEntity *pOwner );
CFireDamageMgr* GetFireDamageMgr();
#endif // FIRE_DAMAGE_MGR_H

View File

@@ -0,0 +1,279 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "gasoline_blob.h"
#include "gasoline_shared.h"
#include "utllinkedlist.h"
#include "fire_damage_mgr.h"
#include "tf_gamerules.h"
// Flamethrower blobs wait a bit before they cause damage so they don't hurt the guy
// shooting them.
#define BLOB_DAMAGE_WAIT_TIME 1.0
// At what heat level does an unlit blob ignite?
#define IGNITION_HEAT 0.1
#define FIRE_DAMAGE_SEARCH_DISTANCE 200 // It searches within this sphere for entities to damage.
#define FIRE_DAMAGE_DISTANCE 90 // This is how far fire can damage an entity from.
ConVar fire_enable( "fire_enable", "1", 0, "Enable or disable fire." );
// ------------------------------------------------------------------------------------------ //
// CGasolineBlob implementation.
// ------------------------------------------------------------------------------------------ //
IMPLEMENT_SERVERCLASS_ST_NOBASE( CGasolineBlob, DT_GasolineBlob )
SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD ),
SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)),
SendPropFloat( SENDINFO(m_flLitStartTime), -1, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flCreateTime), -1, SPROP_NOSCALE ),
SendPropFloat( SENDINFO(m_flMaxLifetime), -1, SPROP_NOSCALE ),
SendPropInt( SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0 ),
SendPropInt( SENDINFO( m_BlobFlags ), NUM_BLOB_FLAGS, SPROP_UNSIGNED ),
SendPropVector( SENDINFO( m_vSurfaceNormal ), 0, SPROP_NORMAL ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( gasoline_blob, CGasolineBlob );
CUtlLinkedList<CGasolineBlob*, int> g_GasolineBlobs;
CGasolineBlob* CGasolineBlob::Create(
CBaseEntity *pOwner,
const Vector &vOrigin,
const Vector &vStartVelocity,
bool bUseGravity,
float flAirLifetime,
float flLifetime )
{
CGasolineBlob *pBlob = (CGasolineBlob*)CreateEntityByName( "gasoline_blob" );
if ( !pBlob )
return NULL;
// The "constructor".
pBlob->SetLocalOrigin( vOrigin );
pBlob->SetAbsVelocity( vStartVelocity );
pBlob->SetThink( &CGasolineBlob::Think );
pBlob->SetNextThink( gpGlobals->curtime );
pBlob->SetCollisionBounds( Vector( -GASOLINE_BLOB_RADIUS, -GASOLINE_BLOB_RADIUS, -GASOLINE_BLOB_RADIUS ), Vector( GASOLINE_BLOB_RADIUS, GASOLINE_BLOB_RADIUS, GASOLINE_BLOB_RADIUS ) );
pBlob->SetMoveType( MOVETYPE_NONE );
pBlob->SetSolid( SOLID_BBOX );
pBlob->AddSolidFlags( FSOLID_NOT_SOLID );
pBlob->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
pBlob->m_BlobFlags = 0;
pBlob->m_HeatLevel = 0;
pBlob->m_hOwner = pOwner;
pBlob->m_flCreateTime = gpGlobals->curtime;
pBlob->m_flMaxLifetime = flLifetime;
pBlob->m_takedamage = DAMAGE_YES;
pBlob->m_flLitStartTime = 0;
pBlob->ChangeTeam( pOwner->GetTeamNumber() );
if ( bUseGravity )
pBlob->m_BlobFlags |= BLOBFLAG_USE_GRAVITY;
pBlob->m_flAirLifetime = flAirLifetime;
pBlob->m_flTimeInAir = 0;
pBlob->SetNextThink( gpGlobals->curtime );
g_GasolineBlobs.AddToTail( pBlob );
return pBlob;
}
CGasolineBlob::~CGasolineBlob()
{
g_GasolineBlobs.Remove( g_GasolineBlobs.Find( this ) );
}
void CGasolineBlob::AddAutoBurnBlob( CGasolineBlob *pBlob )
{
int index = m_AutoBurnBlobs.AddToTail();
m_AutoBurnBlobs[index] = pBlob;
}
int CGasolineBlob::OnTakeDamage( const CTakeDamageInfo &info )
{
m_HeatLevel += info.GetDamage();
if ( m_HeatLevel >= IGNITION_HEAT )
SetLit( true );
return 0;
}
void CGasolineBlob::SetLit( bool bLit )
{
if ( bLit != IsLit() )
{
if ( bLit )
{
m_BlobFlags |= BLOBFLAG_LIT;
m_flLitStartTime = gpGlobals->curtime;
}
else
{
m_BlobFlags &= ~BLOBFLAG_LIT;
}
}
}
bool CGasolineBlob::IsLit() const
{
return (m_BlobFlags & BLOBFLAG_LIT) != 0;
}
bool CGasolineBlob::IsStopped() const
{
return (m_BlobFlags & BLOBFLAG_STOPPED) != 0;
}
void CGasolineBlob::AutoBurn_R( CGasolineBlob *pParent )
{
SetLit( true );
for ( int i=0; i < m_AutoBurnBlobs.Count(); i++ )
{
CGasolineBlob *pTestBlob = m_AutoBurnBlobs[i];
if ( pTestBlob )
{
if ( pTestBlob != pParent )
pTestBlob->AutoBurn_R( this );
}
else
{
m_AutoBurnBlobs.Remove( i );
--i;
}
}
}
void CGasolineBlob::Think()
{
if ( !fire_enable.GetInt() )
{
UTIL_Remove( this );
return;
}
// Decay quickly while in the air.
if ( !IsStopped() )
{
m_flTimeInAir += gpGlobals->frametime;
if ( m_flTimeInAir >= m_flAirLifetime )
{
UTIL_Remove( this );
return;
}
}
float flLifetime = gpGlobals->curtime - m_flCreateTime;
if ( flLifetime >= m_flMaxLifetime )
{
UTIL_Remove( this );
return;
}
if ( IsLit() )
{
// Have we burnt out?
float litPercent = 1 - (flLifetime / m_flMaxLifetime);
if ( litPercent <= 0 )
{
UTIL_Remove( this );
return;
}
// Look for nearby entities to burn.
CBaseEntity *ents[512];
float dists[512];
int nEnts = FindBurnableEntsInSphere( ents, dists, ARRAYSIZE( ents ), GetAbsOrigin(), FIRE_DAMAGE_SEARCH_DISTANCE, m_hOwner );
for ( int i=0; i < nEnts; i++ )
{
float flDistFromBorder = MAX( 0, FIRE_DAMAGE_DISTANCE - dists[i] );
if ( flDistFromBorder <= 0 )
continue;
float flDamage = litPercent * flDistFromBorder / FIRE_DAMAGE_DISTANCE * FIRE_DAMAGE_PER_SEC;
GetFireDamageMgr()->AddDamage( ents[i], m_hOwner, flDamage, !IsGasolineBlob( ents[i] ) );
}
// Ignite our "auto burn" blobs.
AutoBurn_R( NULL );
}
// Figure out where we want to go.
if ( !IsStopped() )
{
// Apply gravity.
Vector vecNewVelocity = GetAbsVelocity();
if ( m_BlobFlags & BLOBFLAG_USE_GRAVITY )
{
vecNewVelocity.z -= 800 * gpGlobals->frametime;
SetAbsVelocity( vecNewVelocity );
}
Vector vNewPos = GetAbsOrigin() + vecNewVelocity * gpGlobals->frametime;
// Can we go there?
trace_t trace;
UTIL_TraceLine( GetAbsOrigin(), vNewPos, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &trace );
bool bStopped = (trace.fraction != 1);
if ( !bStopped )
{
// Trace against shields.
if ( TFGameRules()->IsTraceBlockedByWorldOrShield( GetAbsOrigin(), vNewPos, m_hOwner, DMG_BURN, &trace ) )
{
// Blobs just fizzle out when they hit a shield.
UTIL_Remove( this );
}
}
if( bStopped )
{
SetLocalOrigin( trace.endpos + trace.plane.normal * 2 );
// Ok, we hit something. Stop moving.
m_BlobFlags |= BLOBFLAG_STOPPED;
m_vSurfaceNormal = trace.plane.normal;
}
else
{
SetLocalOrigin( vNewPos );
}
}
SetNextThink( gpGlobals->curtime );
}
bool IsGasolineBlob( CBaseEntity *pEnt )
{
return FClassnameIs( pEnt, "gasoline_blob" );
}

View File

@@ -0,0 +1,93 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef GASOLINE_BLOB_H
#define GASOLINE_BLOB_H
#ifdef _WIN32
#pragma once
#endif
#include "baseentity.h"
class CGasolineBlob : public CBaseEntity
{
public:
DECLARE_CLASS( CGasolineBlob, CBaseEntity );
DECLARE_SERVERCLASS();
// Create a gasoline blob.
// flAirLifetime specifies how long it takes to fizzle out in the air.
static CGasolineBlob* Create(
CBaseEntity *pOwner,
const Vector &vOrigin,
const Vector &vStartVelocity,
bool bUseGravity,
float flAirLifetime,
float flLifetime );
virtual ~CGasolineBlob();
// A lit blob will always apply at least 25% damage to its "auto burn" blob.
//
// This is used when laying down gasoline blobs in a line. Since it's fairly easy to accidentally
// lay down blobs that won't damage each other because they're too far away, the line of blobs
// can be linked together using this.
void AddAutoBurnBlob( CGasolineBlob *pBlob );
// Overrides.
public:
virtual int OnTakeDamage( const CTakeDamageInfo &info );
// Implementation.
public:
bool IsLit() const;
bool IsStopped() const;
void SetLit( bool bLit );
void Think();
void AutoBurn_R( CGasolineBlob *pParent );
private:
typedef CHandle<CGasolineBlob> CGasolineBlobHandle;
CUtlVector<CGasolineBlobHandle> m_AutoBurnBlobs;
float m_flTimeInAir; // How long we've been in the air.
float m_flAirLifetime; // How long we're allowed to exist in the air.
CNetworkVar( float, m_flLitStartTime ); // What time did the blob become lit at?
CNetworkVar( int, m_BlobFlags ); // Combination of BLOBFLAG_ defines.
// This is set at the start and is used to know the percentage of lifetime left.
CNetworkVar( float, m_flMaxLifetime );
// When the blob was created.
CNetworkVar( float, m_flCreateTime );
CNetworkVector( m_vSurfaceNormal ); // This is sent to the client so it can spread the fire out.
EHANDLE m_hOwner;
float m_HeatLevel; // This rises when other flames are nearby until we ignite.
};
// Returns true if the entity is a gasoline blob.
bool IsGasolineBlob( CBaseEntity *pEnt );
#endif // GASOLINE_BLOB_H

View File

@@ -0,0 +1,587 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "tier1/strtools.h"
#include "baseentity.h"
#include "tf_shareddefs.h"
#include "info_act.h"
// Global pointer to the current act
CHandle<CInfoAct> g_hCurrentAct;
BEGIN_DATADESC( CInfoAct )
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStart ),
DEFINE_INPUTFUNC( FIELD_VOID, "FinishWinNone", InputFinishWinNone ),
DEFINE_INPUTFUNC( FIELD_VOID, "FinishWin1", InputFinishWin1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "FinishWin2", InputFinishWin2 ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "AddTime", InputAddTime ),
// outputs
DEFINE_OUTPUT( m_OnStarted, "OnStarted" ),
DEFINE_OUTPUT( m_OnFinishedTeamNone, "OnFinishedWinNone" ),
DEFINE_OUTPUT( m_OnFinishedTeam1, "OnFinishedWin1" ),
DEFINE_OUTPUT( m_OnFinishedTeam2, "OnFinishedWin2" ),
DEFINE_OUTPUT( m_OnTimerExpired, "OnTimerExpired" ),
DEFINE_OUTPUT( m_Respawn1Team1Events[CInfoAct::RESPAWN_TIMER_90_REMAINING], "OnRespawn1Team1_90sec" ),
DEFINE_OUTPUT( m_Respawn1Team1Events[CInfoAct::RESPAWN_TIMER_60_REMAINING], "OnRespawn1Team1_60sec" ),
DEFINE_OUTPUT( m_Respawn1Team1Events[CInfoAct::RESPAWN_TIMER_45_REMAINING], "OnRespawn1Team1_45sec" ),
DEFINE_OUTPUT( m_Respawn1Team1Events[CInfoAct::RESPAWN_TIMER_30_REMAINING], "OnRespawn1Team1_30sec" ),
DEFINE_OUTPUT( m_Respawn1Team1Events[CInfoAct::RESPAWN_TIMER_10_REMAINING], "OnRespawn1Team1_10sec" ),
DEFINE_OUTPUT( m_Respawn1Team1Events[CInfoAct::RESPAWN_TIMER_0_REMAINING], "OnRespawn1Team1" ),
DEFINE_OUTPUT( m_Respawn1Team1TimeRemaining, "Respawn1Team1TimeRemaining" ),
DEFINE_OUTPUT( m_Respawn2Team1Events[CInfoAct::RESPAWN_TIMER_90_REMAINING], "OnRespawn2Team1_90sec" ),
DEFINE_OUTPUT( m_Respawn2Team1Events[CInfoAct::RESPAWN_TIMER_60_REMAINING], "OnRespawn2Team1_60sec" ),
DEFINE_OUTPUT( m_Respawn2Team1Events[CInfoAct::RESPAWN_TIMER_45_REMAINING], "OnRespawn2Team1_45sec" ),
DEFINE_OUTPUT( m_Respawn2Team1Events[CInfoAct::RESPAWN_TIMER_30_REMAINING], "OnRespawn2Team1_30sec" ),
DEFINE_OUTPUT( m_Respawn2Team1Events[CInfoAct::RESPAWN_TIMER_10_REMAINING], "OnRespawn2Team1_10sec" ),
DEFINE_OUTPUT( m_Respawn2Team1Events[CInfoAct::RESPAWN_TIMER_0_REMAINING], "OnRespawn2Team1" ),
DEFINE_OUTPUT( m_Respawn2Team1TimeRemaining, "Respawn2Team1TimeRemaining" ),
DEFINE_OUTPUT( m_Respawn1Team2Events[CInfoAct::RESPAWN_TIMER_90_REMAINING], "OnRespawn1Team2_90sec" ),
DEFINE_OUTPUT( m_Respawn1Team2Events[CInfoAct::RESPAWN_TIMER_60_REMAINING], "OnRespawn1Team2_60sec" ),
DEFINE_OUTPUT( m_Respawn1Team2Events[CInfoAct::RESPAWN_TIMER_45_REMAINING], "OnRespawn1Team2_45sec" ),
DEFINE_OUTPUT( m_Respawn1Team2Events[CInfoAct::RESPAWN_TIMER_30_REMAINING], "OnRespawn1Team2_30sec" ),
DEFINE_OUTPUT( m_Respawn1Team2Events[CInfoAct::RESPAWN_TIMER_10_REMAINING], "OnRespawn1Team2_10sec" ),
DEFINE_OUTPUT( m_Respawn1Team2Events[CInfoAct::RESPAWN_TIMER_0_REMAINING], "OnRespawn1Team2" ),
DEFINE_OUTPUT( m_Respawn1Team2TimeRemaining, "Respawn1Team2TimeRemaining" ),
DEFINE_OUTPUT( m_Respawn2Team2Events[CInfoAct::RESPAWN_TIMER_90_REMAINING], "OnRespawn2Team2_90sec" ),
DEFINE_OUTPUT( m_Respawn2Team2Events[CInfoAct::RESPAWN_TIMER_60_REMAINING], "OnRespawn2Team2_60sec" ),
DEFINE_OUTPUT( m_Respawn2Team2Events[CInfoAct::RESPAWN_TIMER_45_REMAINING], "OnRespawn2Team2_45sec" ),
DEFINE_OUTPUT( m_Respawn2Team2Events[CInfoAct::RESPAWN_TIMER_30_REMAINING], "OnRespawn2Team2_30sec" ),
DEFINE_OUTPUT( m_Respawn2Team2Events[CInfoAct::RESPAWN_TIMER_10_REMAINING], "OnRespawn2Team2_10sec" ),
DEFINE_OUTPUT( m_Respawn2Team2Events[CInfoAct::RESPAWN_TIMER_0_REMAINING], "OnRespawn2Team2" ),
DEFINE_OUTPUT( m_Respawn2Team2TimeRemaining, "Respawn2Team2TimeRemaining" ),
DEFINE_OUTPUT( m_Team1RespawnDelayDone, "OnTeam1RespawnDelayDone" ),
DEFINE_OUTPUT( m_Team2RespawnDelayDone, "OnTeam2RespawnDelayDone" ),
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iActNumber, FIELD_INTEGER, "ActNumber" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flActTimeLimit, FIELD_FLOAT, "ActTimeLimit" ),
DEFINE_KEYFIELD_NOT_SAVED( m_iszIntermissionCamera, FIELD_STRING, "IntermissionCamera" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nRespawn1Team1Time, FIELD_INTEGER, "Respawn1Team1Time" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nRespawn2Team1Time, FIELD_INTEGER, "Respawn2Team1Time" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nRespawn1Team2Time, FIELD_INTEGER, "Respawn1Team2Time" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nRespawn2Team2Time, FIELD_INTEGER, "Respawn2Team2Time" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nRespawnTeam1Delay, FIELD_INTEGER, "RespawnTeam1InitialDelay" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nRespawnTeam2Delay, FIELD_INTEGER, "RespawnTeam2InitialDelay" ),
// functions
DEFINE_FUNCTION( ActThink ),
DEFINE_FUNCTION( ActThinkEndActOverlayTime ),
DEFINE_FUNCTION( RespawnTimerThink ),
DEFINE_FUNCTION( Team1RespawnDelayThink ),
DEFINE_FUNCTION( Team2RespawnDelayThink ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST(CInfoAct, DT_InfoAct)
SendPropInt(SENDINFO(m_iActNumber), 5 ),
SendPropInt(SENDINFO(m_spawnflags), SF_ACT_BITS, SPROP_UNSIGNED ),
SendPropFloat(SENDINFO(m_flActTimeLimit), 12 ),
SendPropInt(SENDINFO(m_nRespawn1Team1Time), 8 ),
SendPropInt(SENDINFO(m_nRespawn2Team1Time), 8 ),
SendPropInt(SENDINFO(m_nRespawn1Team2Time), 8 ),
SendPropInt(SENDINFO(m_nRespawn2Team2Time), 8 ),
SendPropInt(SENDINFO(m_nRespawnTeam1Delay), 8 ),
SendPropInt(SENDINFO(m_nRespawnTeam2Delay), 8 ),
END_SEND_TABLE();
LINK_ENTITY_TO_CLASS( info_act, CInfoAct );
#define RESPAWN_TIMER_CONTEXT "RespawnTimerThink"
#define RESPAWN_TEAM_1_DELAY_CONTEXT "RespawnTeam1DelayThink"
#define RESPAWN_TEAM_2_DELAY_CONTEXT "RespawnTeam2DelayThink"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CInfoAct::CInfoAct( void )
{
// No act == -1
m_iActNumber = -1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CInfoAct::UpdateTransmitState()
{
return SetTransmitState( FL_EDICT_ALWAYS );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoAct::Spawn( void )
{
m_flActStartedAt = 0;
m_iWinners = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Set up respawn timers
//-----------------------------------------------------------------------------
void CInfoAct::SetUpRespawnTimers()
{
// NOTE: Need to add the second there so the respawn timers don't immediately trigger
SetContextThink( RespawnTimerThink, gpGlobals->curtime + 1.0f, RESPAWN_TIMER_CONTEXT );
if (m_nRespawnTeam1Delay != 0)
{
SetContextThink( Team1RespawnDelayThink, gpGlobals->curtime + m_nRespawnTeam1Delay, RESPAWN_TEAM_1_DELAY_CONTEXT );
}
else
{
m_Team1RespawnDelayDone.FireOutput( this, this );
}
if (m_nRespawnTeam2Delay != 0)
{
SetContextThink( Team2RespawnDelayThink, gpGlobals->curtime + m_nRespawnTeam2Delay, RESPAWN_TEAM_2_DELAY_CONTEXT );
}
else
{
m_Team2RespawnDelayDone.FireOutput( this, this );
}
}
void CInfoAct::ShutdownRespawnTimers()
{
SetContextThink( NULL, 0, RESPAWN_TIMER_CONTEXT );
SetContextThink( NULL, 0, RESPAWN_TEAM_1_DELAY_CONTEXT );
SetContextThink( NULL, 0, RESPAWN_TEAM_2_DELAY_CONTEXT );
}
//-----------------------------------------------------------------------------
// Respawn delay
//-----------------------------------------------------------------------------
void CInfoAct::Team1RespawnDelayThink()
{
m_Team1RespawnDelayDone.FireOutput( this, this );
SetContextThink( NULL, 0, RESPAWN_TEAM_1_DELAY_CONTEXT );
}
void CInfoAct::Team2RespawnDelayThink()
{
m_Team2RespawnDelayDone.FireOutput( this, this );
SetContextThink( NULL, 0, RESPAWN_TEAM_2_DELAY_CONTEXT );
}
//-----------------------------------------------------------------------------
// Computes the time remaining
//-----------------------------------------------------------------------------
int CInfoAct::ComputeTimeRemaining( int nPeriod, int nDelay )
{
if (nPeriod <= 0)
return -1;
int nTimeDelta = (int)(gpGlobals->curtime - m_flActStartedAt);
Assert( nTimeDelta >= 0 );
nTimeDelta -= nDelay;
// This case takes care of the initial spawn delay time...
if (nTimeDelta <= 0)
{
return nPeriod - nTimeDelta;
}
int nFactor = nTimeDelta / nPeriod;
int nTimeRemainder = nTimeDelta - nFactor * nPeriod;
if (nTimeRemainder == 0)
return 0;
return nPeriod - nTimeRemainder;
}
//-----------------------------------------------------------------------------
// Fires respawn events
//-----------------------------------------------------------------------------
void CInfoAct::FireRespawnEvents( int nTimeRemaining, COutputEvent *pRespawnEvents, COutputInt &respawnTime )
{
if (nTimeRemaining < 0)
return;
switch (nTimeRemaining)
{
case 90:
pRespawnEvents[RESPAWN_TIMER_90_REMAINING].FireOutput( this, this );
break;
case 60:
pRespawnEvents[RESPAWN_TIMER_60_REMAINING].FireOutput( this, this );
break;
case 45:
pRespawnEvents[RESPAWN_TIMER_45_REMAINING].FireOutput( this, this );
break;
case 30:
pRespawnEvents[RESPAWN_TIMER_30_REMAINING].FireOutput( this, this );
break;
case 10:
pRespawnEvents[RESPAWN_TIMER_10_REMAINING].FireOutput( this, this );
break;
case 0:
pRespawnEvents[RESPAWN_TIMER_0_REMAINING].FireOutput( this, this );
break;
default:
break;
}
respawnTime.Set( nTimeRemaining, this, this );
}
//-----------------------------------------------------------------------------
// Respawn timers
//-----------------------------------------------------------------------------
void CInfoAct::RespawnTimerThink()
{
int nTimeRemaining = ComputeTimeRemaining( m_nRespawn1Team1Time, m_nRespawnTeam1Delay );
FireRespawnEvents( nTimeRemaining, m_Respawn1Team1Events, m_Respawn1Team1TimeRemaining );
nTimeRemaining = ComputeTimeRemaining( m_nRespawn2Team1Time, m_nRespawnTeam1Delay );
FireRespawnEvents( nTimeRemaining, m_Respawn2Team1Events, m_Respawn2Team1TimeRemaining );
nTimeRemaining = ComputeTimeRemaining( m_nRespawn1Team2Time, m_nRespawnTeam2Delay );
FireRespawnEvents( nTimeRemaining, m_Respawn1Team2Events, m_Respawn1Team2TimeRemaining );
nTimeRemaining = ComputeTimeRemaining( m_nRespawn2Team2Time, m_nRespawnTeam2Delay );
FireRespawnEvents( nTimeRemaining, m_Respawn2Team2Events, m_Respawn2Team2TimeRemaining );
SetNextThink( gpGlobals->curtime + 1.0f, RESPAWN_TIMER_CONTEXT );
}
//-----------------------------------------------------------------------------
// Purpose: The act has started
//-----------------------------------------------------------------------------
void CInfoAct::StartAct( void )
{
// FIXME: Should this change?
// Don't allow two simultaneous acts
if (g_hCurrentAct)
{
g_hCurrentAct->FinishAct( );
}
// Set the global act to this
g_hCurrentAct = this;
m_flActStartedAt = gpGlobals->curtime;
m_OnStarted.FireOutput( this, this );
// Do we have a timelimit?
if ( m_flActTimeLimit )
{
SetNextThink( gpGlobals->curtime + m_flActTimeLimit );
SetThink( ActThink );
}
SetUpRespawnTimers();
// Tell all the clients
CReliableBroadcastRecipientFilter filter;
UserMessageBegin( filter, "ActBegin" );
WRITE_BYTE( (byte)m_iActNumber );
WRITE_FLOAT( m_flActStartedAt );
MessageEnd();
// If we're not an intermission, clean up
if ( !HasSpawnFlags( SF_ACT_INTERMISSION ) )
{
CleanupOnActStart();
}
// Cycle through all players and start the act
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)UTIL_PlayerByIndex( i );
if ( pPlayer )
{
// Am I an intermission?
if ( HasSpawnFlags( SF_ACT_INTERMISSION ) )
{
StartIntermission( pPlayer );
}
else
{
StartActOverlayTime( pPlayer );
}
}
}
// Think again soon, to remove player locks
if ( !HasSpawnFlags(SF_ACT_INTERMISSION) )
{
SetNextThink( gpGlobals->curtime + MIN_ACT_OVERLAY_TIME );
SetThink( ActThinkEndActOverlayTime );
}
}
//-----------------------------------------------------------------------------
// Purpose: Update a client who joined during the middle of an act
//-----------------------------------------------------------------------------
void CInfoAct::UpdateClient( CBaseTFPlayer *pPlayer )
{
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "ActBegin" );
WRITE_BYTE( (byte)m_iActNumber );
WRITE_FLOAT( m_flActStartedAt );
MessageEnd();
}
//-----------------------------------------------------------------------------
// Purpose: The act has finished
//-----------------------------------------------------------------------------
void CInfoAct::FinishAct( )
{
if ( g_hCurrentAct.Get() != this )
{
DevWarning( 2, "Attempted to finish an act which wasn't started!\n" );
return;
}
ShutdownRespawnTimers();
switch( m_iWinners)
{
case 0:
m_OnFinishedTeamNone.FireOutput( this, this );
break;
case 1:
m_OnFinishedTeam1.FireOutput( this, this );
break;
case 2:
m_OnFinishedTeam2.FireOutput( this, this );
break;
default:
Assert(0);
break;
}
g_hCurrentAct = NULL;
// Tell all the clients
CReliableBroadcastRecipientFilter filter;
UserMessageBegin( filter, "ActEnd" );
WRITE_BYTE( m_iWinners );
MessageEnd();
// Am I an intermission?
if ( HasSpawnFlags( SF_ACT_INTERMISSION ) )
{
// Cycle through all players and end the intermission for them
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)UTIL_PlayerByIndex( i );
if ( pPlayer )
{
EndIntermission( pPlayer );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoAct::ActThink( void )
{
m_OnTimerExpired.FireOutput( this,this );
}
//-----------------------------------------------------------------------------
// Purpose: Force the players not to move to give them time to read the act overlays
//-----------------------------------------------------------------------------
void CInfoAct::StartActOverlayTime( CBaseTFPlayer *pPlayer )
{
// Lock the player in place
pPlayer->CleanupOnActStart();
pPlayer->LockPlayerInPlace();
if ( pPlayer->GetActiveWeapon() )
{
pPlayer->GetActiveWeapon()->Holster();
}
pPlayer->m_Local.m_iHideHUD |= (HIDEHUD_WEAPONSELECTION | HIDEHUD_HEALTH);
pPlayer->GetLocalData()->m_bForceMapOverview = true;
}
//-----------------------------------------------------------------------------
// Purpose: Release the players after overlay time has finished
//-----------------------------------------------------------------------------
void CInfoAct::EndActOverlayTime( CBaseTFPlayer *pPlayer )
{
// Release the player
pPlayer->UnlockPlayer();
if ( pPlayer->GetActiveWeapon() )
{
pPlayer->GetActiveWeapon()->Deploy();
}
pPlayer->m_Local.m_iHideHUD &= ~(HIDEHUD_WEAPONSELECTION | HIDEHUD_HEALTH);
pPlayer->GetLocalData()->m_bForceMapOverview = false;
}
//-----------------------------------------------------------------------------
// Purpose: Unlock the players after an act has started
//-----------------------------------------------------------------------------
void CInfoAct::ActThinkEndActOverlayTime( void )
{
// Cycle through all players and end the intermission for them
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)UTIL_PlayerByIndex( i );
if ( pPlayer )
{
EndActOverlayTime( pPlayer );
}
}
// Think again when the act ends, if we have a timelimit
if ( m_flActTimeLimit )
{
SetNextThink( gpGlobals->curtime + m_flActTimeLimit - MIN_ACT_OVERLAY_TIME );
SetThink( ActThink );
}
}
//-----------------------------------------------------------------------------
// Purpose: Clean up entities before a new act starts
//-----------------------------------------------------------------------------
void CInfoAct::CleanupOnActStart( void )
{
// Remove all resource chunks
CBaseEntity *pEntity = NULL;
while ((pEntity = gEntList.FindEntityByClassname( pEntity, "resource_chunk" )) != NULL)
{
UTIL_Remove( pEntity );
}
}
//-----------------------------------------------------------------------------
// Purpose: Intermission handling
//-----------------------------------------------------------------------------
void CInfoAct::StartIntermission( CBaseTFPlayer *pPlayer )
{
// Do we have a camera point?
if ( m_iszIntermissionCamera != NULL_STRING )
{
CBaseEntity *pCamera = gEntList.FindEntityByName( NULL, STRING(m_iszIntermissionCamera) );
if ( pCamera )
{
// Move the player to the camera point
pPlayer->SetViewEntity( pCamera );
pPlayer->m_Local.m_iHideHUD |= (HIDEHUD_WEAPONSELECTION | HIDEHUD_HEALTH | HIDEHUD_MISCSTATUS);
}
}
// Lock the player in place
pPlayer->LockPlayerInPlace();
}
//-----------------------------------------------------------------------------
// Purpose: Intermission handling
//-----------------------------------------------------------------------------
void CInfoAct::EndIntermission( CBaseTFPlayer *pPlayer )
{
// Force the player to respawn
pPlayer->UnlockPlayer();
pPlayer->SetViewEntity( pPlayer );
pPlayer->ForceRespawn();
pPlayer->m_Local.m_iHideHUD &= ~(HIDEHUD_WEAPONSELECTION | HIDEHUD_HEALTH | HIDEHUD_MISCSTATUS);
}
//-----------------------------------------------------------------------------
// Purpose: Force the act to start
//-----------------------------------------------------------------------------
void CInfoAct::InputStart( inputdata_t &inputdata )
{
StartAct();
}
//-----------------------------------------------------------------------------
// Purpose: Force the act to finish, with team 1 as the winners
//-----------------------------------------------------------------------------
void CInfoAct::InputFinishWinNone( inputdata_t &inputdata )
{
m_iWinners = 0;
FinishAct();
}
//-----------------------------------------------------------------------------
// Purpose: Force the act to finish, with team 1 as the winners
//-----------------------------------------------------------------------------
void CInfoAct::InputFinishWin1( inputdata_t &inputdata )
{
m_iWinners = 1;
FinishAct();
}
//-----------------------------------------------------------------------------
// Purpose: Force the act to finish, with team 2 as the winners
//-----------------------------------------------------------------------------
void CInfoAct::InputFinishWin2( inputdata_t &inputdata )
{
m_iWinners = 2;
FinishAct();
}
//-----------------------------------------------------------------------------
// Purpose: Add time to the act's time
//-----------------------------------------------------------------------------
void CInfoAct::InputAddTime( inputdata_t &inputdata )
{
float flNewTime = inputdata.value.Float();
// Think again when the act ends, if we have a timelimit
if ( flNewTime )
{
m_flActTimeLimit += flNewTime;
SetNextThink( GetNextThink() + flNewTime );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CInfoAct::IsAWaitingAct( void )
{
return HasSpawnFlags(SF_ACT_WAITINGFORGAMESTART);
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the current act (if any) is a waiting act.
//-----------------------------------------------------------------------------
bool CurrentActIsAWaitingAct( void )
{
if ( g_hCurrentAct )
return g_hCurrentAct->IsAWaitingAct();
return false;
}

138
game/server/tf2/info_act.h Normal file
View File

@@ -0,0 +1,138 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef INFO_ACT_H
#define INFO_ACT_H
#ifdef _WIN32
#pragma once
#endif
class CBaseTFPlayer;
//-----------------------------------------------------------------------------
// Purpose: Map entity that defines an act
//-----------------------------------------------------------------------------
class CInfoAct : public CBaseEntity
{
DECLARE_CLASS( CInfoAct, CBaseEntity );
public:
CInfoAct();
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
int UpdateTransmitState();
void Spawn( void );
void StartAct( void );
void UpdateClient( CBaseTFPlayer *pPlayer );
void FinishAct( void );
void ActThink( void );
int ActNumber() const;
void CleanupOnActStart( void );
bool IsAWaitingAct( void );
// Act player locking
void StartActOverlayTime( CBaseTFPlayer *pPlayer );
void EndActOverlayTime( CBaseTFPlayer *pPlayer );
void ActThinkEndActOverlayTime( void );
// Intermissions
void StartIntermission( CBaseTFPlayer *pPlayer );
void EndIntermission( CBaseTFPlayer *pPlayer );
int GetMCVTimer( void ) { return m_nRespawn2Team2Time; }
private:
enum
{
RESPAWN_TIMER_90_REMAINING = 0,
RESPAWN_TIMER_60_REMAINING,
RESPAWN_TIMER_45_REMAINING,
RESPAWN_TIMER_30_REMAINING,
RESPAWN_TIMER_10_REMAINING,
RESPAWN_TIMER_0_REMAINING,
RESPAWN_TIMER_EVENT_COUNT
};
void SetUpRespawnTimers();
void ShutdownRespawnTimers();
// Inputs
void InputStart( inputdata_t &inputdata );
void InputFinishWinNone( inputdata_t &inputdata );
void InputFinishWin1( inputdata_t &inputdata );
void InputFinishWin2( inputdata_t &inputdata );
void InputAddTime( inputdata_t &inputdata );
// Respawn timers
void RespawnTimerThink();
// Respawn delay
void Team1RespawnDelayThink();
void Team2RespawnDelayThink();
// Computes the time remaining
int ComputeTimeRemaining( int nPeriod, int nDelay );
// Fires respawn events
void FireRespawnEvents( int nTimeRemaining, COutputEvent *pRespawnEvents, COutputInt &respawnTime );
// Outputs
COutputEvent m_OnStarted;
COutputEvent m_OnFinishedTeamNone;
COutputEvent m_OnFinishedTeam1;
COutputEvent m_OnFinishedTeam2;
COutputEvent m_OnTimerExpired;
COutputEvent m_Team1RespawnDelayDone;
COutputEvent m_Team2RespawnDelayDone;
COutputInt m_Respawn1Team1TimeRemaining;
COutputInt m_Respawn2Team1TimeRemaining;
COutputInt m_Respawn1Team2TimeRemaining;
COutputInt m_Respawn2Team2TimeRemaining;
// A whole buncha respawn timer events
COutputEvent m_Respawn1Team1Events[RESPAWN_TIMER_EVENT_COUNT];
COutputEvent m_Respawn2Team1Events[RESPAWN_TIMER_EVENT_COUNT];
COutputEvent m_Respawn1Team2Events[RESPAWN_TIMER_EVENT_COUNT];
COutputEvent m_Respawn2Team2Events[RESPAWN_TIMER_EVENT_COUNT];
// Respawn timer periods
CNetworkVar( int, m_nRespawn1Team1Time );
CNetworkVar( int, m_nRespawn1Team2Time );
CNetworkVar( int, m_nRespawn2Team1Time );
CNetworkVar( int, m_nRespawn2Team2Time );
CNetworkVar( int, m_nRespawnTeam1Delay );
CNetworkVar( int, m_nRespawnTeam2Delay );
// Data
CNetworkVar( int, m_iActNumber );
CNetworkVar( float, m_flActTimeLimit );
int m_iWinners;
// Acts
float m_flActStartedAt;
// Intermissions
string_t m_iszIntermissionCamera;
};
inline int CInfoAct::ActNumber() const
{
return m_iActNumber;
}
extern CHandle<CInfoAct> g_hCurrentAct;
bool CurrentActIsAWaitingAct( void );
#endif // INFO_ACT_H

View File

@@ -0,0 +1,72 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
#include "tf_stats.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that gives resources to players passed into it
//-----------------------------------------------------------------------------
class CInfoAddResources : public CBaseEntity
{
DECLARE_CLASS( CInfoAddResources, CBaseEntity );
public:
DECLARE_DATADESC();
void Spawn( void );
// Inputs
void InputPlayer( inputdata_t &inputdata );
public:
// Outputs
COutputEvent m_OnAdded;
// Data
int m_iResourceAmount;
};
BEGIN_DATADESC( CInfoAddResources )
// inputs
DEFINE_INPUTFUNC( FIELD_EHANDLE, "Player", InputPlayer ),
// outputs
DEFINE_OUTPUT( m_OnAdded, "OnAdded" ),
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iResourceAmount, FIELD_INTEGER, "ResourceAmount" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_add_resources, CInfoAddResources );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoAddResources::Spawn( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoAddResources::InputPlayer( inputdata_t &inputdata )
{
CBaseEntity *pEntity = (inputdata.value.Entity()).Get();
if ( pEntity && pEntity->IsPlayer() )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
pPlayer->AddBankResources( m_iResourceAmount );
TFStats()->IncrementPlayerStat( pPlayer, TF_PLAYER_STAT_RESOURCES_ACQUIRED, m_iResourceAmount );
}
m_OnAdded.FireOutput( inputdata.pActivator, this );
}

View File

@@ -0,0 +1,217 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Map entity that allows players to build objects on it
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
#include "info_buildpoint.h"
#include "tf_gamerules.h"
// Spawnflags
const int SF_BUILDPOINT_ALLOW_ALL_GUNS = 0x01; // Allow all manned guns to be built on this point
const int SF_BUILDPOINT_ALLOW_VEHICLES = 0x02; // Allow all vehicles to be built on this point
BEGIN_DATADESC( CInfoBuildPoint )
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iszAllowedObject, FIELD_STRING, "AllowedObject" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_buildpoint, CInfoBuildPoint );
// List of buildpoints
CUtlVector<CInfoBuildPoint*> g_MapDefinedBuildPoints;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoBuildPoint::Spawn( void )
{
g_MapDefinedBuildPoints.AddToTail( this );
m_iAllowedObjectType = -1;
if ( m_iszAllowedObject != NULL_STRING )
{
for ( int i = 0; i < OBJ_LAST; i++ )
{
if ( !Q_strcmp( STRING(m_iszAllowedObject), GetObjectInfo(i)->m_pClassName ) )
{
m_iAllowedObjectType = i;
break;
}
}
}
BaseClass::Spawn();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoBuildPoint::UpdateOnRemove( void )
{
g_MapDefinedBuildPoints.FindAndRemove( this );
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose: Tell me how many build points you have
//-----------------------------------------------------------------------------
int CInfoBuildPoint::GetNumBuildPoints( void ) const
{
return 1;
}
//-----------------------------------------------------------------------------
// Purpose: Give me the origin & angles of the specified build point
//-----------------------------------------------------------------------------
bool CInfoBuildPoint::GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles )
{
ASSERT( iPoint <= GetNumBuildPoints() );
vecOrigin = GetAbsOrigin();
vecAngles = GetAbsAngles();
return true;
}
int CInfoBuildPoint::GetBuildPointAttachmentIndex( int iPoint ) const
{
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Can I build the specified object on the specified build point?
//-----------------------------------------------------------------------------
bool CInfoBuildPoint::CanBuildObjectOnBuildPoint( int iPoint, int iObjectType )
{
ASSERT( iPoint <= GetNumBuildPoints() );
if ( m_hObjectBuiltOnMe )
return false;
// Manned guns?
if ( m_spawnflags & SF_BUILDPOINT_ALLOW_ALL_GUNS )
{
if ( (iObjectType == OBJ_MANNED_PLASMAGUN) ||
(iObjectType == OBJ_MANNED_MISSILELAUNCHER) ||
(iObjectType == OBJ_MANNED_SHIELD) ||
(iObjectType == OBJ_SENTRYGUN_PLASMA) )
return true;
}
// Vehicles
if ( m_spawnflags & SF_BUILDPOINT_ALLOW_VEHICLES )
{
if ( IsObjectAVehicle(iObjectType) )
return true;
}
// Check our unique
if ( m_iAllowedObjectType >= 0 && m_iAllowedObjectType == iObjectType )
return true;
return false;
}
//-----------------------------------------------------------------------------
// Purpose: I've finished building the specified object on the specified build point
//-----------------------------------------------------------------------------
void CInfoBuildPoint::SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject )
{
ASSERT( iPoint <= GetNumBuildPoints() );
m_hObjectBuiltOnMe = pObject;
}
//-----------------------------------------------------------------------------
// Purpose: Get the number of objects build on this entity
//-----------------------------------------------------------------------------
int CInfoBuildPoint::GetNumObjectsOnMe( void )
{
if ( m_hObjectBuiltOnMe )
return 1;
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Get the first object that's built on me
//-----------------------------------------------------------------------------
CBaseEntity *CInfoBuildPoint::GetFirstObjectOnMe( void )
{
return m_hObjectBuiltOnMe;
}
//-----------------------------------------------------------------------------
// Purpose: Get the first object of type, return NULL if no such type available
//-----------------------------------------------------------------------------
CBaseObject *CInfoBuildPoint::GetObjectOfTypeOnMe( int iObjectType )
{
if ( m_hObjectBuiltOnMe )
{
if ( m_hObjectBuiltOnMe->ObjectType() == iObjectType )
return m_hObjectBuiltOnMe;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Remove all objects built on me
//-----------------------------------------------------------------------------
void CInfoBuildPoint::RemoveAllObjects( void )
{
UTIL_Remove( m_hObjectBuiltOnMe );
}
//-----------------------------------------------------------------------------
// Purpose: Return the maximum distance that this entity's build points can be snapped to
//-----------------------------------------------------------------------------
float CInfoBuildPoint::GetMaxSnapDistance( int iPoint )
{
return 64;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if it's possible that build points on this entity may move in local space (i.e. due to animation)
//-----------------------------------------------------------------------------
bool CInfoBuildPoint::ShouldCheckForMovement( void )
{
return false;
}
//-----------------------------------------------------------------------------
// Purpose: I've finished building the specified object on the specified build point
//-----------------------------------------------------------------------------
int CInfoBuildPoint::FindObjectOnBuildPoint( CBaseObject *pObject )
{
return 1;
}
//-----------------------------------------------------------------------------
// Purpose: Returns an exit point for a vehicle built on a build point...
//-----------------------------------------------------------------------------
void CInfoBuildPoint::GetExitPoint( CBaseEntity *pPlayer, int iPoint, Vector *pAbsOrigin, QAngle *pAbsAngles )
{
// FIXME: In future, we may well want to use specific exit attachments here...
GetBuildPoint( iPoint, *pAbsOrigin, *pAbsAngles );
// Move back along the forward direction a bit...
Vector vecForward;
AngleVectors( *pAbsAngles, &vecForward );
*pAbsOrigin -= vecForward * 60;
// Now select a good spot to drop onto
Vector vNewPos;
if ( !EntityPlacementTest(pPlayer, *pAbsOrigin, vNewPos, true) )
{
Warning("Can't find valid place to exit object.\n");
return;
}
*pAbsOrigin = vNewPos;
}

View File

@@ -0,0 +1,76 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Map entity that allows players to build objects on it
//
//=============================================================================//
#ifndef INFO_BUILDPOINT_H
#define INFO_BUILDPOINT_H
#ifdef _WIN32
#pragma once
#endif
#include "ihasbuildpoints.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that allows players to build objects on it
//-----------------------------------------------------------------------------
class CInfoBuildPoint : public CBaseEntity, public IHasBuildPoints
{
DECLARE_CLASS( CInfoBuildPoint, CBaseEntity );
public:
DECLARE_DATADESC();
void Spawn( void );
void UpdateOnRemove( void );
// IHasBuildPoints
public:
// Tell me how many build points you have
virtual int GetNumBuildPoints( void ) const;
// Give me the origin & angles of the specified build point
virtual bool GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles );
virtual int GetBuildPointAttachmentIndex( int iPoint ) const;
// Can I build the specified object on the specified build point?
virtual bool CanBuildObjectOnBuildPoint( int iPoint, int iObjectType );
// I've finished building the specified object on the specified build point
virtual void SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject );
// Get the number of objects build on this entity
virtual int GetNumObjectsOnMe( void );
// Get the first object that's built on me
virtual CBaseEntity *GetFirstObjectOnMe( void );
// Get the first object of type, return NULL if no such type available
virtual CBaseObject *GetObjectOfTypeOnMe( int iObjectType );
// Remove all objects built on me
virtual void RemoveAllObjects( void );
// Return the maximum distance that this entity's build points can be snapped to
virtual float GetMaxSnapDistance( int iPoint );
// Return true if it's possible that build points on this entity may move in local space (i.e. due to animation)
virtual bool ShouldCheckForMovement( void );
// I've finished building the specified object on the specified build point
virtual int FindObjectOnBuildPoint( CBaseObject *pObject );
// Returns an exit point for a vehicle built on a build point...
virtual void GetExitPoint( CBaseEntity *pPlayer, int iPoint, Vector *pAbsOrigin, QAngle *pAbsAngles );
private:
string_t m_iszAllowedObject;
int m_iAllowedObjectType;
CHandle<CBaseObject> m_hObjectBuiltOnMe;
};
extern CUtlVector<CInfoBuildPoint*> g_MapDefinedBuildPoints;
#endif // INFO_BUILDPOINT_H

View File

@@ -0,0 +1,107 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Map entity that adds a custom technology to the techtree
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "techtree.h"
#include "info_customtech.h"
#include "tier1/strtools.h"
BEGIN_DATADESC( CInfoCustomTechnology )
// outputs
DEFINE_OUTPUT( m_flTechPercentage, "TechPercentage" ),
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iszTech , FIELD_STRING, "TechToWatch" ),
DEFINE_KEYFIELD_NOT_SAVED( m_iszTechTreeFile , FIELD_STRING, "NewTechFile" ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CInfoCustomTechnology, DT_InfoCustomTechnology )
SendPropString( SENDINFO( m_szTechTreeFile ) ),
END_SEND_TABLE();
LINK_ENTITY_TO_CLASS( info_customtech, CInfoCustomTechnology );
//-----------------------------------------------------------------------------
// Purpose: Always transmit
//-----------------------------------------------------------------------------
int CInfoCustomTechnology::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
// Don't need to transmit ones that don't add new techs
if ( !m_iszTechTreeFile )
return FL_EDICT_DONTSEND;
// Only transmit to members of my team
CBaseEntity* pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
if ( InSameTeam( pRecipientEntity ) )
{
//Msg( "SENDING\n" );
return FL_EDICT_ALWAYS;
}
return FL_EDICT_DONTSEND;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoCustomTechnology::Spawn( void )
{
m_flTechPercentage.Set( 0, this, this );
memset( m_szTechTreeFile.GetForModify(), 0, sizeof(m_szTechTreeFile) );
}
//-----------------------------------------------------------------------------
// Purpose: Add myself to the technology tree
//-----------------------------------------------------------------------------
void CInfoCustomTechnology::Activate( void )
{
BaseClass::Activate();
if ( !GetTeamNumber() )
{
Msg( "ERROR: info_customtech without a specified team.\n" );
UTIL_Remove( this );
return;
}
// Get the Team's Technology Tree
CTFTeam *pTeam = (CTFTeam *)GetTeam();
if ( pTeam )
{
CTechnologyTree *pTechTree = pTeam->GetTechnologyTree();
if ( pTechTree )
{
// Am I supposed to add some new technologies to the tech tree?
if ( m_iszTechTreeFile != NULL_STRING )
{
pTechTree->AddTechnologyFile( filesystem, GetTeamNumber(), (char*)STRING(m_iszTechTreeFile ) );
// Tell our clients about the technology
Q_strncpy( m_szTechTreeFile.GetForModify(), STRING(m_iszTechTreeFile), sizeof(m_szTechTreeFile) );
}
// Find the technology in the techtree
CBaseTechnology *pTechnology = pTechTree->GetTechnology( STRING(m_iszTech) );
// Now hook the technology up to me
if ( pTechnology )
{
pTechnology->RegisterWatcher( this );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Update the amount of our technology that's owned
//-----------------------------------------------------------------------------
void CInfoCustomTechnology::UpdateTechPercentage( float flPercentage )
{
m_flTechPercentage.Set( flPercentage, this, this );
}

View File

@@ -0,0 +1,43 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef INFO_CUSTOMTECH_H
#define INFO_CUSTOMTECH_H
#ifdef _WIN32
#pragma once
#endif
#include "entityoutput.h"
#include "baseentity.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that adds a custom technology to the techtree
//-----------------------------------------------------------------------------
class CInfoCustomTechnology : public CPointEntity
{
DECLARE_CLASS( CInfoCustomTechnology, CPointEntity );
public:
void Spawn( void );
void Activate( void );
void UpdateTechPercentage( float flPercentage );
virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_FULLCHECK ); };
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
public:
COutputFloat m_flTechPercentage; // Percentage of the tech that's owned
string_t m_iszTech;
string_t m_iszTechTreeFile;
// Sent via datatable
CNetworkString( m_szTechTreeFile, 128 );
};
#endif // INFO_CUSTOMTECH_H

View File

@@ -0,0 +1,222 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
#include "engine/IEngineSound.h"
#include "triggers.h"
// Spawnflags
#define SF_PLAYSOUND_USE_THIS_ORIGIN 0x0001
//-----------------------------------------------------------------------------
// Purpose: Map entity that plays sounds to players
//-----------------------------------------------------------------------------
class CInfoInputPlaySound : public CBaseEntity
{
DECLARE_CLASS( CInfoInputPlaySound, CBaseEntity );
public:
DECLARE_DATADESC();
virtual void Spawn( void );
virtual void Precache( void );
virtual void Activate( void );
// Inputs
void InputPlaySoundToAll( inputdata_t &inputdata );
void InputPlaySoundToTeam1( inputdata_t &inputdata );
void InputPlaySoundToTeam2( inputdata_t &inputdata );
void InputPlaySoundToPlayer( inputdata_t &inputdata );
void InputSetSound( inputdata_t &inputdata );
// Sound playing
void PlaySoundToPlayer( CBaseTFPlayer *pPlayer );
void PlaySoundToTeam( CTFTeam *pTeam );
private:
string_t m_iszSound;
float m_flVolume;
float m_flAttenuation;
string_t m_iszTestVolumeName;
EHANDLE m_hTestVolume;
};
BEGIN_DATADESC( CInfoInputPlaySound )
// variables
DEFINE_KEYFIELD( m_iszSound, FIELD_SOUNDNAME, "Sound" ),
DEFINE_KEYFIELD( m_flVolume, FIELD_FLOAT, "Volume" ),
DEFINE_KEYFIELD( m_flAttenuation, FIELD_FLOAT, "Attenuation" ),
DEFINE_KEYFIELD( m_iszTestVolumeName, FIELD_STRING, "TestVolume" ),
// inputs
DEFINE_INPUTFUNC( FIELD_STRING, "SetSound", InputSetSound ),
DEFINE_INPUTFUNC( FIELD_VOID, "PlaySoundToAll", InputPlaySoundToAll ),
DEFINE_INPUTFUNC( FIELD_VOID, "PlaySoundToTeam1", InputPlaySoundToTeam1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "PlaySoundToTeam2", InputPlaySoundToTeam2 ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "PlaySoundToPlayer", InputPlaySoundToPlayer ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_input_playsound, CInfoInputPlaySound );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::Spawn( void )
{
m_hTestVolume = NULL;
Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::Precache( void )
{
if ( m_iszSound != NULL_STRING )
{
PrecacheScriptSound( STRING(m_iszSound) );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::Activate( void )
{
BaseClass::Activate();
// Find our test volume, if we have one
if ( m_iszTestVolumeName != NULL_STRING )
{
m_hTestVolume = gEntList.FindEntityByName( NULL, STRING(m_iszTestVolumeName) );
if ( !m_hTestVolume )
{
Msg("ERROR: Could not find test volume %s for info_input_playsound.\n", STRING(m_iszTestVolumeName) );
}
else
{
// Make sure it's a trigger
CBaseTrigger *pTrigger = dynamic_cast<CBaseTrigger*>((CBaseEntity*)m_hTestVolume);
if ( !pTrigger )
{
Msg("ERROR: info_input_playsound specifies a volume %s, but it's not a trigger.\n", STRING(m_iszTestVolumeName) );
m_hTestVolume = NULL;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Play sound to all players
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::InputPlaySoundToAll( inputdata_t &inputdata )
{
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
if ( pPlayer )
{
PlaySoundToPlayer( pPlayer );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Play sound to all players on team 1
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::InputPlaySoundToTeam1( inputdata_t &inputdata )
{
PlaySoundToTeam( GetGlobalTFTeam(1) );
}
//-----------------------------------------------------------------------------
// Purpose: Play sound to all players on team 2
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::InputPlaySoundToTeam2( inputdata_t &inputdata )
{
PlaySoundToTeam( GetGlobalTFTeam(2) );
}
//-----------------------------------------------------------------------------
// Purpose: Play sound to a specific player
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::InputPlaySoundToPlayer( inputdata_t &inputdata )
{
CBaseEntity *pEntity = (inputdata.value.Entity()).Get();
if ( pEntity && pEntity->IsPlayer() )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
PlaySoundToPlayer( pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the sound to play
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::InputSetSound( inputdata_t &inputdata )
{
m_iszSound = MAKE_STRING( inputdata.value.String() );
}
//-----------------------------------------------------------------------------
// Purpose: Play sound to a team
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::PlaySoundToTeam( CTFTeam *pTeam )
{
for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
{
PlaySoundToPlayer( (CBaseTFPlayer*)pTeam->GetPlayer(i) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Play sound to a player
//-----------------------------------------------------------------------------
void CInfoInputPlaySound::PlaySoundToPlayer( CBaseTFPlayer *pPlayer )
{
// First, if we have a test volume, make sure the player's within it
if ( m_hTestVolume )
{
CBaseTrigger *pTrigger = (CBaseTrigger *)(CBaseEntity*)m_hTestVolume;
if ( !pTrigger->IsTouching( pPlayer ) )
return;
}
// Check to see if we're supposed to play it from this entity's location
if ( HasSpawnFlags( SF_PLAYSOUND_USE_THIS_ORIGIN ) )
{
CPASAttenuationFilter filter;
filter.AddRecipient( pPlayer );
filter.Filter( GetAbsOrigin(), m_flAttenuation );
EmitSound_t ep;
ep.m_nChannel = CHAN_VOICE;
ep.m_pSoundName = STRING(m_iszSound);
ep.m_flVolume = m_flVolume;
ep.m_SoundLevel = ATTN_TO_SNDLVL( m_flAttenuation );
ep.m_pOrigin = &(GetAbsOrigin());
EmitSound( filter, entindex(), ep );
}
else
{
CSingleUserRecipientFilter filter( pPlayer );
EmitSound_t ep;
ep.m_nChannel = CHAN_VOICE;
ep.m_pSoundName = STRING(m_iszSound);
ep.m_flVolume = m_flVolume;
ep.m_SoundLevel = ATTN_TO_SNDLVL( m_flAttenuation );
EmitSound( filter, pPlayer->entindex(), ep );
}
}

View File

@@ -0,0 +1,124 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that resets player's banks
//-----------------------------------------------------------------------------
class CInfoInputResetBanks : public CBaseEntity
{
DECLARE_CLASS( CInfoInputResetBanks, CBaseEntity );
public:
DECLARE_DATADESC();
// Inputs
void InputResetAll( inputdata_t &inputdata );
void InputResetTeam1( inputdata_t &inputdata );
void InputResetTeam2( inputdata_t &inputdata );
void InputResetPlayer( inputdata_t &inputdata );
void InputSetResetAmount( inputdata_t &inputdata );
// Resetting
void ResetPlayersBank( CBaseTFPlayer *pPlayer );
void ResetTeamsBanks( CTFTeam *pTeam );
private:
int m_iResetAmount;
};
BEGIN_DATADESC( CInfoInputResetBanks )
// variables
DEFINE_KEYFIELD( m_iResetAmount, FIELD_INTEGER, "ResetAmount" ),
// inputs
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetResetAmount", InputSetResetAmount ),
DEFINE_INPUTFUNC( FIELD_VOID, "ResetAll", InputResetAll ),
DEFINE_INPUTFUNC( FIELD_VOID, "ResetTeam1", InputResetTeam1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "ResetTeam2", InputResetTeam2 ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "ResetPlayer", InputResetPlayer ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_input_resetbanks, CInfoInputResetBanks );
//-----------------------------------------------------------------------------
// Purpose: Reset all the player's resource banks
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::InputResetAll( inputdata_t &inputdata )
{
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
if ( pPlayer )
{
ResetPlayersBank( pPlayer );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Reset all of team 1's player's resource banks
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::InputResetTeam1( inputdata_t &inputdata )
{
ResetTeamsBanks( GetGlobalTFTeam(1) );
}
//-----------------------------------------------------------------------------
// Purpose: Reset all of team 2's player's resource banks
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::InputResetTeam2( inputdata_t &inputdata )
{
ResetTeamsBanks( GetGlobalTFTeam(2) );
}
//-----------------------------------------------------------------------------
// Purpose: Reset a specific player's resource banks
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::InputResetPlayer( inputdata_t &inputdata )
{
CBaseEntity *pEntity = (inputdata.value.Entity()).Get();
if ( pEntity && pEntity->IsPlayer() )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
ResetPlayersBank( pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the reset amount
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::InputSetResetAmount( inputdata_t &inputdata )
{
m_iResetAmount = inputdata.value.Int();
Assert( m_iResetAmount >= 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Reset the team's resource banks
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::ResetTeamsBanks( CTFTeam *pTeam )
{
pTeam->SetRecentBankSet( m_iResetAmount );
for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
{
ResetPlayersBank( (CBaseTFPlayer*)pTeam->GetPlayer(i) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Reset the player's resource bank
//-----------------------------------------------------------------------------
void CInfoInputResetBanks::ResetPlayersBank( CBaseTFPlayer *pPlayer )
{
pPlayer->SetBankResources( m_iResetAmount );
}

View File

@@ -0,0 +1,106 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that resets player's objects
//-----------------------------------------------------------------------------
class CInfoInputResetObjects : public CBaseEntity
{
DECLARE_CLASS( CInfoInputResetObjects, CBaseEntity );
public:
DECLARE_DATADESC();
// Inputs
void InputResetAll( inputdata_t &inputdata );
void InputResetTeam1( inputdata_t &inputdata );
void InputResetTeam2( inputdata_t &inputdata );
void InputResetPlayer( inputdata_t &inputdata );
// Resetting
void ResetPlayersObjects( CBaseTFPlayer *pPlayer );
void ResetTeamsObjects( CTFTeam *pTeam );
};
BEGIN_DATADESC( CInfoInputResetObjects )
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "ResetAll", InputResetAll ),
DEFINE_INPUTFUNC( FIELD_VOID, "ResetTeam1", InputResetTeam1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "ResetTeam2", InputResetTeam2 ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "ResetPlayer", InputResetPlayer ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_input_resetobjects, CInfoInputResetObjects );
//-----------------------------------------------------------------------------
// Purpose: Reset all the player's objects
//-----------------------------------------------------------------------------
void CInfoInputResetObjects::InputResetAll( inputdata_t &inputdata )
{
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
if ( pPlayer )
{
ResetPlayersObjects( pPlayer );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Reset all of team 1's player's objects
//-----------------------------------------------------------------------------
void CInfoInputResetObjects::InputResetTeam1( inputdata_t &inputdata )
{
ResetTeamsObjects( GetGlobalTFTeam(1) );
}
//-----------------------------------------------------------------------------
// Purpose: Reset all of team 2's player's objects
//-----------------------------------------------------------------------------
void CInfoInputResetObjects::InputResetTeam2( inputdata_t &inputdata )
{
ResetTeamsObjects( GetGlobalTFTeam(2) );
}
//-----------------------------------------------------------------------------
// Purpose: Reset a specific player's objects
//-----------------------------------------------------------------------------
void CInfoInputResetObjects::InputResetPlayer( inputdata_t &inputdata )
{
CBaseEntity *pEntity = (inputdata.value.Entity()).Get();
if ( pEntity && pEntity->IsPlayer() )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
ResetPlayersObjects( pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose: Reset the team's objects
//-----------------------------------------------------------------------------
void CInfoInputResetObjects::ResetTeamsObjects( CTFTeam *pTeam )
{
for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
{
ResetPlayersObjects( (CBaseTFPlayer*)pTeam->GetPlayer(i) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Reset the player's objects
//-----------------------------------------------------------------------------
void CInfoInputResetObjects::ResetPlayersObjects( CBaseTFPlayer *pPlayer )
{
pPlayer->RemoveAllObjects( true, 0, true );
}

View File

@@ -0,0 +1,121 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
// Spawnflags
const int SF_RESPAWNPLAYERS_RESETALL = 0x01; // Respawned players have their inventory completely reset
const int SF_RESPAWNPLAYERS_RESETAMMO = 0x02; // Respawned players have their ammo counts reset
//-----------------------------------------------------------------------------
// Purpose: Map entity that respawns players
//-----------------------------------------------------------------------------
class CInfoInputRespawnPlayers : public CBaseEntity
{
DECLARE_CLASS( CInfoInputRespawnPlayers, CBaseEntity );
public:
DECLARE_DATADESC();
// Inputs
void InputRespawnAll( inputdata_t &inputdata );
void InputRespawnTeam1( inputdata_t &inputdata );
void InputRespawnTeam2( inputdata_t &inputdata );
void InputRespawnPlayer( inputdata_t &inputdata );
// Respawning
void RespawnPlayer( CBaseTFPlayer *pPlayer );
void RespawnTeam( CTFTeam *pTeam );
};
BEGIN_DATADESC( CInfoInputRespawnPlayers )
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "RespawnAll", InputRespawnAll ),
DEFINE_INPUTFUNC( FIELD_VOID, "RespawnTeam1", InputRespawnTeam1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "RespawnTeam2", InputRespawnTeam2 ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "RespawnPlayer", InputRespawnPlayer ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_input_respawnplayers, CInfoInputRespawnPlayers );
//-----------------------------------------------------------------------------
// Purpose: Respawn all the players
//-----------------------------------------------------------------------------
void CInfoInputRespawnPlayers::InputRespawnAll( inputdata_t &inputdata )
{
for ( int i = 0; i < MAX_TF_TEAMS; i++ )
{
RespawnTeam( GetGlobalTFTeam(i+1) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Respawn all of team 1's players
//-----------------------------------------------------------------------------
void CInfoInputRespawnPlayers::InputRespawnTeam1( inputdata_t &inputdata )
{
RespawnTeam( GetGlobalTFTeam(1) );
}
//-----------------------------------------------------------------------------
// Purpose: Respawn all of team 2's players
//-----------------------------------------------------------------------------
void CInfoInputRespawnPlayers::InputRespawnTeam2( inputdata_t &inputdata )
{
RespawnTeam( GetGlobalTFTeam(2) );
}
//-----------------------------------------------------------------------------
// Purpose: Respawn a specific player
//-----------------------------------------------------------------------------
void CInfoInputRespawnPlayers::InputRespawnPlayer( inputdata_t &inputdata )
{
CBaseEntity *pEntity = (inputdata.value.Entity()).Get();
if ( pEntity && pEntity->IsPlayer() )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
RespawnPlayer( pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose: Respawn the team
//-----------------------------------------------------------------------------
void CInfoInputRespawnPlayers::RespawnTeam( CTFTeam *pTeam )
{
Assert( pTeam );
if ( !pTeam )
return;
// Respawn all the players
for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
{
RespawnPlayer( (CBaseTFPlayer*)pTeam->GetPlayer(i) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Respawn the player
//-----------------------------------------------------------------------------
void CInfoInputRespawnPlayers::RespawnPlayer( CBaseTFPlayer *pPlayer )
{
// Reset ammo if the spawnflag's set
if ( HasSpawnFlags( SF_RESPAWNPLAYERS_RESETALL ) )
{
pPlayer->ResupplyAmmo( 1.0, RESUPPLY_ALL_FROM_STATION );
}
else if ( HasSpawnFlags( SF_RESPAWNPLAYERS_RESETAMMO ) )
{
pPlayer->ResupplyAmmo( 1.0, RESUPPLY_RESPAWN );
}
pPlayer->ForceRespawn();
}

View File

@@ -0,0 +1,116 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that makes a pulse on the minimap
//-----------------------------------------------------------------------------
class CInfoMinimapPulse : public CBaseEntity
{
DECLARE_CLASS( CInfoMinimapPulse, CBaseEntity );
public:
DECLARE_DATADESC();
void Spawn( void );
// Inputs
void InputPulseForAll( inputdata_t &inputdata );
void InputPulseForTeam1( inputdata_t &inputdata );
void InputPulseForTeam2( inputdata_t &inputdata );
void InputPulseForPlayer( inputdata_t &inputdata );
// Pulsing
void PulseForPlayer( CBaseTFPlayer *pPlayer );
void PulseForTeam( CTFTeam *pTeam );
};
BEGIN_DATADESC( CInfoMinimapPulse )
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "PulseForAll", InputPulseForAll ),
DEFINE_INPUTFUNC( FIELD_VOID, "PulseForTeam1", InputPulseForTeam1 ),
DEFINE_INPUTFUNC( FIELD_VOID, "PulseForTeam2", InputPulseForTeam2 ),
DEFINE_INPUTFUNC( FIELD_EHANDLE, "PulseForPlayer", InputPulseForPlayer ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_minimappulse, CInfoMinimapPulse );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::Spawn( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Pulse for all the players
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::InputPulseForAll( inputdata_t &inputdata )
{
for ( int i = 0; i < MAX_TF_TEAMS; i++ )
{
PulseForTeam( GetGlobalTFTeam(i) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Pulse for all of team 1's players
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::InputPulseForTeam1( inputdata_t &inputdata )
{
PulseForTeam( GetGlobalTFTeam(1) );
}
//-----------------------------------------------------------------------------
// Purpose: Pulse for all of team 2's players
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::InputPulseForTeam2( inputdata_t &inputdata )
{
PulseForTeam( GetGlobalTFTeam(2) );
}
//-----------------------------------------------------------------------------
// Purpose: Pulse for a specific player
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::InputPulseForPlayer( inputdata_t &inputdata )
{
CBaseEntity *pEntity = (inputdata.value.Entity()).Get();
if ( pEntity && pEntity->IsPlayer() )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
PulseForPlayer( pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose: Pulse for the team
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::PulseForTeam( CTFTeam *pTeam )
{
// Pulse all the players
for ( int i = 0; i < pTeam->GetNumPlayers(); i++ )
{
PulseForPlayer( (CBaseTFPlayer*)pTeam->GetPlayer(i) );
}
}
//-----------------------------------------------------------------------------
// Purpose: Make a minimap pulse for the player
//-----------------------------------------------------------------------------
void CInfoMinimapPulse::PulseForPlayer( CBaseTFPlayer *pPlayer )
{
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
UserMessageBegin( user, "MinimapPulse" );
WRITE_VEC3COORD( GetAbsOrigin() );
MessageEnd();
}

View File

@@ -0,0 +1,71 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "tf_team.h"
#include "baseentity.h"
//-----------------------------------------------------------------------------
// Purpose: Map entity that fires its output with all the players in a team
//-----------------------------------------------------------------------------
class CInfoOutputTeam : public CBaseEntity
{
DECLARE_CLASS( CInfoOutputTeam, CBaseEntity );
public:
DECLARE_DATADESC();
void Spawn( void );
// Inputs
void InputFire( inputdata_t &inputdata );
public:
// Outputs
COutputEHANDLE m_Player;
};
BEGIN_DATADESC( CInfoOutputTeam )
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "Fire", InputFire ),
// outputs
DEFINE_OUTPUT( m_Player, "Player" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_output_team, CInfoOutputTeam );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoOutputTeam::Spawn( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoOutputTeam::InputFire( inputdata_t &inputdata )
{
// Loop through all the players on the team and fire our output with each of them.
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
if ( pPlayer )
{
// If we don't belong to a team, loop through all players
if ( GetTeamNumber() == 0 || pPlayer->GetTeamNumber() == GetTeamNumber() )
{
EHANDLE hHandle;
hHandle = pPlayer;
m_Player.Set( hHandle, inputdata.pActivator, this );
}
}
}
}

View File

@@ -0,0 +1,139 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A team's resource processor back at their base
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_team.h"
#include "info_resourceprocessor.h"
#include "tf_player.h"
#include "npc_minicarrier.h"
#include "tf_gamerules.h"
BEGIN_DATADESC( CResourceProcessor )
// functions
DEFINE_FUNCTION( ProcessorTouch ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST(CResourceProcessor, DT_ResourceProcessor)
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( info_resourceprocessor, CResourceProcessor);
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CResourceProcessor::Spawn( void )
{
Precache();
SetSolid( SOLID_SLIDEBOX );
SetMoveType( MOVETYPE_NONE );
AddFlag( FL_NOTARGET );
UTIL_SetSize( pev, Vector(-32,-32,0), Vector(32,32, 128) );
SetModel( "models/objects/obj_resourceprocessor.mdl" );
SetTouch( ProcessorTouch );
m_flHackSpawnHeight = 256;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CResourceProcessor::Precache( void )
{
PrecacheModel( "models/objects/obj_resourceprocessor.mdl" );
UTIL_PrecacheOther( "npc_minicarrier" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CResourceProcessor::Activate( void )
{
BaseClass::Activate();
if ( GetTeamNumber() < 0 || GetTeamNumber() >= GetNumberOfTeams() )
{
Warning( "Warning, info_resourceprocessor with invalid Team Number set.\n" );
UTIL_Remove( this );
return;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CResourceProcessor::ProcessorTouch( CBaseEntity *pOther )
{
// Players
if ( pOther->IsPlayer() )
{
// Ignore touches from enemy players
if ( !InSameTeam( pOther ) )
return;
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pOther;
// Remove all the player's resources and add them to the team's stash
for ( int i = 0; i < MAX_RESOURCE_TYPES; i++ )
{
int iCount = pPlayer->GetResourceChunkCount(i, false);
if ( iCount )
{
pPlayer->RemoveResourceChunks( i, iCount, false );
AddResources( i, iCount * CHUNK_RESOURCE_VALUE );
}
// Now remove processed versions too
iCount = pPlayer->GetResourceChunkCount(i, true);
if ( iCount )
{
pPlayer->RemoveResourceChunks( i, iCount, true );
AddResources( i, iCount * PROCESSED_CHUNK_RESOURCE_VALUE );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Add resources to the processor, and hence the team's bank
//-----------------------------------------------------------------------------
void CResourceProcessor::AddResources( int iResourceType, float flResources )
{
if ( !GetTeam() )
return;
((CTFTeam *)GetTeam())->AddResources( iResourceType, flResources );
((CTFTeam *)GetTeam())->ResourceLoadDeposited();
}
//-----------------------------------------------------------------------------
// Purpose: Spawn a minicarrier
//-----------------------------------------------------------------------------
void CResourceProcessor::SpawnMiniCarrier( void )
{
CNPC_MiniCarrier *pMiniCarrier = (CNPC_MiniCarrier*)CreateEntityByName( "npc_minicarrier" );
pMiniCarrier->Spawn();
pMiniCarrier->ChangeTeam( m_iTeamNumber );
// Find a clear spot near me & spawn in it
if ( !EntityPlacementTest( pMiniCarrier, GetAbsOrigin() + Vector(0,0,m_flHackSpawnHeight),
pMiniCarrier->GetAbsOrigin(), false ) )
{
Warning( "Failed to find empty space to spawn a minicarrier.\n" );
( ( CTFTeam * )GetTeam() )->RemoveRobot( pMiniCarrier );
UTIL_Remove( pMiniCarrier );
return;
}
m_flHackSpawnHeight += 64;
engine->SetOrigin( pMiniCarrier->pev, pMiniCarrier->GetOrigin() );
pMiniCarrier->SetHomeProcessor( this );
}

View File

@@ -0,0 +1,270 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "EntityOutput.h"
#include "EntityList.h"
#include "info_vehicle_bay.h"
#include "tf_obj.h"
#include "tf_player.h"
#include "info_act.h"
extern ConVar tf_fastbuild;
BEGIN_DATADESC( CInfoVehicleBay )
END_DATADESC()
LINK_ENTITY_TO_CLASS( info_vehicle_bay, CInfoVehicleBay );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoVehicleBay::Spawn( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Vehicle bays have 1 build point
//-----------------------------------------------------------------------------
int CInfoVehicleBay::GetNumBuildPoints( void ) const
{
return 1;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the specified object type can be built on this point
//-----------------------------------------------------------------------------
bool CInfoVehicleBay::CanBuildObjectOnBuildPoint( int iPoint, int iObjectType )
{
ASSERT( iPoint <= GetNumBuildPoints() );
// Don't allow building if there's another vehicle in the way
// Only vehicles can be built here
return ( iObjectType >= OBJ_BATTERING_RAM );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CInfoVehicleBay::GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles )
{
ASSERT( iPoint <= GetNumBuildPoints() );
vecOrigin = GetAbsOrigin();
vecAngles = GetAbsAngles();
return true;
}
int CInfoVehicleBay::GetBuildPointAttachmentIndex( int iPoint ) const
{
return 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoVehicleBay::SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CInfoVehicleBay::GetNumObjectsOnMe( void )
{
return 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseEntity *CInfoVehicleBay::GetFirstObjectOnMe( void )
{
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseObject *CInfoVehicleBay::GetObjectOfTypeOnMe( int iObjectType )
{
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CInfoVehicleBay::FindObjectOnBuildPoint( CBaseObject *pObject )
{
return -1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoVehicleBay::GetExitPoint( CBaseEntity *pPlayer, int iPoint, Vector *pAbsOrigin, QAngle *pAbsAngles )
{
Assert(0);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CInfoVehicleBay::RemoveAllObjects( void )
{
}
//===================================================================================================================
// Vehicle Bay VGui Screen
//===================================================================================================================
BEGIN_DATADESC( CVGuiScreenVehicleBay )
// outputs
DEFINE_OUTPUT( m_OnStartedBuild, "OnStartedBuild" ),
DEFINE_OUTPUT( m_OnFinishedBuild, "OnFinishedBuild" ),
DEFINE_OUTPUT( m_OnReadyToBuildAgain, "OnReadyToBuildAgain" ),
// functions
DEFINE_FUNCTION( BayThink ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( vgui_screen_vehicle_bay, CVGuiScreenVehicleBay );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVGuiScreenVehicleBay::Activate( void )
{
BaseClass::Activate();
// Make sure we have a buildpoint specified
CBaseEntity *pBuildPoint = gEntList.FindEntityByName( NULL, m_target );
if ( !pBuildPoint )
{
Msg("ERROR: vgui_screen_vehicle_bay with no buildpoint as its target.\n" );
UTIL_Remove( this );
return;
}
Vector vecOrigin = pBuildPoint->GetAbsOrigin();
QAngle vecAngles = pBuildPoint->GetAbsAngles();
SetBuildPoint( vecOrigin, vecAngles );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVGuiScreenVehicleBay::SetBuildPoint( Vector &vecOrigin, QAngle &vecAngles )
{
m_vecBuildPointOrigin = vecOrigin;
m_vecBuildPointAngles = vecAngles;
m_bBayIsClear = false;
// Start checking to see when I'm clear again
SetThink( BayThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVGuiScreenVehicleBay::BuildVehicle( CBaseTFPlayer *pPlayer, int iObjectType )
{
if ( !IsObjectAVehicle(iObjectType) )
return;
Assert( m_vecBuildPointOrigin != vec3_origin );
// Can't build if the game hasn't started
if ( !tf_fastbuild.GetInt() && CurrentActIsAWaitingAct() )
{
ClientPrint( pPlayer, HUD_PRINTCENTER, "Can't build until the game's started.\n" );
return;
}
// Try and spawn the object
CBaseEntity *pEntity = CreateEntityByName( GetObjectInfo(iObjectType)->m_pClassName );
if ( !pEntity )
return;
if ( !m_bBayIsClear )
{
ClientPrint( pPlayer, HUD_PRINTCENTER, "Vehicle bay isn't clear.\n" );
return;
}
pEntity->SetAbsOrigin( m_vecBuildPointOrigin );
pEntity->SetAbsAngles( m_vecBuildPointAngles );
CBaseObject *pObject = dynamic_cast<CBaseObject*>(pEntity);
if ( pObject )
pObject->AdjustInitialBuildAngles();
pEntity->Spawn();
// If it's an object, finish setting it up
if ( !pObject )
return;
pObject->StartPlacement( pPlayer );
pObject->SetVehicleBay( this );
// StartBuilding will return false if the player couldn't afford the vehicle
if ( !pObject->StartBuilding( pPlayer ) )
return;
// Fire our started-building output
m_OnStartedBuild.FireOutput( pPlayer, this );
m_bBayIsClear = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVGuiScreenVehicleBay::FinishedBuildVehicle( CBaseObject *pObject )
{
m_OnFinishedBuild.FireOutput( pObject->GetBuilder(), this );
// Start checking to see when I'm clear again
SetThink( BayThink );
SetNextThink( gpGlobals->curtime + 0.3 );
}
//-----------------------------------------------------------------------------
// Purpose: Check to see if we're clear enough to allow another vehicle to be built here
//-----------------------------------------------------------------------------
void CVGuiScreenVehicleBay::BayThink( void )
{
// Get a list of entities around our buildpoint
CBaseEntity *pListOfNearbyEntities[100];
int iNumberOfNearbyEntities = UTIL_EntitiesInSphere( pListOfNearbyEntities, 100, m_vecBuildPointOrigin, 128, 0 );
for ( int i = 0; i < iNumberOfNearbyEntities; i++ )
{
CBaseEntity *pEntity = pListOfNearbyEntities[i];
if ( pEntity->IsSolid( ) )
{
// Ignore shields..
if ( pEntity->GetCollisionGroup() == TFCOLLISION_GROUP_SHIELD )
continue;
// Ignore func brushes
if ( pEntity->GetMoveType() == MOVETYPE_PUSH )
continue;
//NDebugOverlay::EntityBounds( pEntity, 0,255,0,8, 0.1 );
// Check again soon
SetNextThink( gpGlobals->curtime + 1 );
return;
}
}
// We're clear
m_bBayIsClear = true;
SetThink( NULL );
m_OnReadyToBuildAgain.FireOutput( this, this );
}

View File

@@ -0,0 +1,72 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef INFO_VEHICLE_BAY_H
#define INFO_VEHICLE_BAY_H
#ifdef _WIN32
#pragma once
#endif
#include "ihasbuildpoints.h"
#include "vguiscreen.h"
//-----------------------------------------------------------------------------
// Purpose: Entity that provides a place to build a vehicle
//-----------------------------------------------------------------------------
class CInfoVehicleBay : public CBaseEntity, public IHasBuildPoints
{
DECLARE_CLASS( CInfoVehicleBay, CBaseEntity );
public:
DECLARE_DATADESC();
void Spawn( void );
// IHasBuildPoints
public:
virtual int GetNumBuildPoints( void ) const;
virtual bool CanBuildObjectOnBuildPoint( int iPoint, int iObjectType );
virtual bool GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles );
virtual int GetBuildPointAttachmentIndex( int iPoint ) const;
virtual void SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject );
virtual int GetNumObjectsOnMe( void );
virtual CBaseEntity *GetFirstObjectOnMe( void );
virtual CBaseObject *GetObjectOfTypeOnMe( int iObjectType );
virtual int FindObjectOnBuildPoint( CBaseObject *pObject );
virtual void GetExitPoint( CBaseEntity *pPlayer, int iPoint, Vector *pAbsOrigin, QAngle *pAbsAngles );
virtual void RemoveAllObjects( void );
virtual float GetMaxSnapDistance( int iPoint ) { return 128; }
virtual bool ShouldCheckForMovement( void ) { return false; }
};
//-----------------------------------------------------------------------------
// Purpose: Vgui screen for vehicle buying
//-----------------------------------------------------------------------------
class CVGuiScreenVehicleBay : public CVGuiScreen
{
DECLARE_CLASS( CVGuiScreenVehicleBay, CVGuiScreen );
public:
DECLARE_DATADESC();
virtual void Activate( void );
void BuildVehicle( CBaseTFPlayer *pPlayer, int iObjectType );
void FinishedBuildVehicle( CBaseObject *pObject );
void SetBuildPoint( Vector &vecOrigin, QAngle &vecAngles );
void BayThink( void );
private:
bool m_bBayIsClear;
Vector m_vecBuildPointOrigin;
QAngle m_vecBuildPointAngles;
// Outputs
COutputEvent m_OnStartedBuild;
COutputEvent m_OnFinishedBuild;
COutputEvent m_OnReadyToBuildAgain;
};
#endif // INFO_VEHICLE_BAY_H

View File

@@ -0,0 +1,115 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "mapdata_shared.h"
#include "sharedinterface.h"
#include "baseentity.h"
#include "world.h"
#include "player.h"
class CMapData_Server : public IMapData
{
public:
// World data queries.
void GetMapBounds( Vector &vecMins, Vector &vecMaxs );
void GetMapOrigin( Vector &vecOrigin );
void GetMapSize( Vector &vecSize );
// 3D Skybox data queries.
void Get3DSkyboxOrigin( Vector &vecOrigin );
float Get3DSkyboxScale( void );
};
static CMapData_Server g_MapData;
IMapData *g_pMapData = &g_MapData;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapData_Server::GetMapBounds( Vector &vecMins, Vector &vecMaxs )
{
CWorld *pWorld = static_cast<CWorld*>( GetWorldEntity() );
if ( pWorld )
{
// Get the world bounds.
pWorld->GetWorldBounds( vecMins, vecMaxs );
// Backward compatability...
if ( ( vecMins.LengthSqr() == 0.0f ) && ( vecMaxs.LengthSqr() == 0.0f ) )
{
vecMins.Init( -6500.0f, -6500.0f, -6500.0f );
vecMaxs.Init( 6500.0f, 6500.0f, 6500.0f );
}
}
else
{
Assert( 0 );
vecMins.Init( 0.0f, 0.0f, 0.0f );
vecMaxs.Init( 1.0f, 1.0f, 1.0f );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapData_Server::GetMapOrigin( Vector &vecOrigin )
{
Vector vecMins, vecMaxs;
GetMapBounds( vecMins, vecMaxs );
VectorAdd( vecMins, vecMaxs, vecOrigin );
VectorMultiply( vecOrigin, 0.5f, vecOrigin );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapData_Server::GetMapSize( Vector &vecSize )
{
Vector vecMins, vecMaxs;
GetMapBounds( vecMins, vecMaxs );
VectorSubtract( vecMaxs, vecMins, vecSize );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapData_Server::Get3DSkyboxOrigin( Vector &vecOrigin )
{
// NOTE: If the player hasn't been created yet -- this doesn't work!!!
// We need to pass the data along in the map - requires a tool change.
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( pPlayer )
{
CPlayerLocalData *pLocalData = &pPlayer->m_Local;
VectorCopy( pLocalData->m_skybox3d.origin, vecOrigin );
}
else
{
// Debugging!
Assert( 0 );
vecOrigin.Init();
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CMapData_Server::Get3DSkyboxScale( void )
{
// NOTE: If the player hasn't been created yet -- this doesn't work!!!
// We need to pass the data along in the map - requires a tool change.
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( pPlayer )
{
CPlayerLocalData *pLocalData = &pPlayer->m_Local;
return pLocalData->m_skybox3d.scale;
}
else
{
// Debugging!
Assert( 0 );
return 1.0f;
}
}

View File

@@ -0,0 +1,262 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Ugly menus for prototyping
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "player.h"
#include "tf_player.h"
#include "menu_base.h"
#include "tf_team.h"
#include "baseviewmodel.h"
#include "tf_gamerules.h"
#include "tf_class_infiltrator.h"
#include "tier1/strtools.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Global list of menus
CMenu *gMenus[MENU_LAST];
//-----------------------------------------------------------------------------
// Purpose: Initialize the Global Menu structure
//-----------------------------------------------------------------------------
void InitializeMenus( void )
{
gMenus[MENU_DEFAULT] = NULL;
gMenus[MENU_TEAM] = new CMenuTeam();
gMenus[MENU_CLASS] = new CMenuClass();
}
void DestroyMenus( void )
{
delete gMenus[MENU_DEFAULT];
delete gMenus[MENU_TEAM];
delete gMenus[MENU_CLASS];
}
//-----------------------------------------------------------------------------
// Purpose: Base Menu Handling
//-----------------------------------------------------------------------------
CMenu::CMenu()
{
memset( m_szMenuString, 0, sizeof(m_szMenuString) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMenu::Display( CBaseTFPlayer *pViewer, int allowed, int display_time )
{
RecalculateMenu( pViewer );
// Only display if the menu's not identical to the one the player's already seeing
if ( pViewer->m_MenuUpdateTime > gpGlobals->curtime )
{
if ( (allowed == pViewer->m_MenuSelectionBuffer) && FStrEq( m_szMenuString, pViewer->m_MenuStringBuffer) )
return;
}
pViewer->m_MenuUpdateTime = gpGlobals->curtime + 10;
pViewer->m_MenuSelectionBuffer = allowed;
const char *msg_portion = m_szMenuString;
Q_strncpy( pViewer->m_MenuStringBuffer, m_szMenuString, MENU_STRING_BUFFER_SIZE );
CSingleUserRecipientFilter user( pViewer );
user.MakeReliable();
while ( strlen(msg_portion) >= MENU_MSG_TEXTCHUNK_SIZE )
{
// split the string
char sbuf[MENU_MSG_TEXTCHUNK_SIZE+1];
Q_strncpy( sbuf, msg_portion, MENU_MSG_TEXTCHUNK_SIZE+1 );
msg_portion += MENU_MSG_TEXTCHUNK_SIZE;
// send the string portion
UserMessageBegin( user, "ShowMenu" );
WRITE_WORD( allowed );
WRITE_CHAR( display_time ); // display time (-1 means unlimited)
WRITE_BYTE( TRUE ); // there is more message to come
WRITE_STRING( sbuf );
MessageEnd();
}
// send the remaining string
UserMessageBegin( user, "ShowMenu" );
WRITE_WORD( allowed );
WRITE_CHAR( display_time ); // display time (-1 means unlimited)
WRITE_BYTE( FALSE ); // there is no more message to come
WRITE_STRING( (char*)msg_portion );
MessageEnd();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMenu::RecalculateMenu( CBaseTFPlayer *pViewer )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMenu::Input( CBaseTFPlayer *pViewer, int iInput )
{
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Team Menu
//-----------------------------------------------------------------------------
CMenuTeam::CMenuTeam()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMenuTeam::RecalculateMenu( CBaseTFPlayer *pViewer )
{
Q_strncpy( m_szMenuString, "Pick a Team: \n\n\n->1. Humans\n\n->2. Aliens\n", sizeof(m_szMenuString) );
// Allow aborting if they have a class
if ( pViewer->GetTeam() )
{
Q_strncat( m_szMenuString, "\n\n->9. Don't change team.\n", sizeof(m_szMenuString), COPY_ALL_CHARACTERS );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle input for the Team menu
//-----------------------------------------------------------------------------
bool CMenuTeam::Input( CBaseTFPlayer *pViewer, int iInput )
{
// Allow aborting if they have a team
if ( pViewer->GetTeam() && iInput == 9 )
{
pViewer->MenuReset();
return true;
}
if (iInput < 0 || iInput >= GetNumberOfTeams())
return false;
// Ignore changeteam requests to their current team
if ( pViewer->GetTeam() )
{
if ( iInput == pViewer->GetTeam()->GetTeamNumber() )
{
pViewer->MenuReset();
return true;
}
}
// Add the player to the team and then bring up the Class Menu
pViewer->ChangeTeam( iInput );
// Clear out the class
if ( pViewer->GetPlayerClass() )
{
// Remove all the player's items
pViewer->RemoveAllItems( false );
pViewer->HideViewModels();
pViewer->ClearPlayerClass();
}
pViewer->ForceRespawn();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Class Menu
//-----------------------------------------------------------------------------
CMenuClass::CMenuClass()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMenuClass::RecalculateMenu( CBaseTFPlayer *pViewer )
{
if ( !pViewer->GetTeam() )
return;
Q_snprintf( m_szMenuString,sizeof(m_szMenuString), "You are on Team %s\n\n\n\n\n\n\nPick your Class: \n\n\n", pViewer->GetTeam()->GetName() );
int iClassNum = 1;
// Check technology for each class
for ( int i = 0; i < TFCLASS_CLASS_COUNT; i++ )
{
char sClass[256];
if ( !( pViewer->IsClassAvailable( (TFClass)i ) ) )
continue;
int iNumber = pViewer->GetTFTeam()->GetNumOfClass( (TFClass)i );
if ( !iNumber )
{
Q_snprintf( sClass, sizeof(sClass), "->%d. %s\n\n", iClassNum, GetTFClassInfo( i )->m_pClassName );
}
else
{
Q_snprintf( sClass, sizeof(sClass), "->%d. %s (%d in your team)\n\n", iClassNum, GetTFClassInfo( i )->m_pClassName, iNumber );
}
Q_strncat( m_szMenuString, sClass,sizeof(m_szMenuString), COPY_ALL_CHARACTERS );
iClassNum++;
}
// Allow aborting if they have a class
if ( pViewer->GetPlayerClass() )
{
Q_strncat( m_szMenuString, "\n\n->9. Don't change class.\n", sizeof(m_szMenuString), COPY_ALL_CHARACTERS );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle input for the Class menu
//-----------------------------------------------------------------------------
bool CMenuClass::Input( CBaseTFPlayer *pViewer, int iInput )
{
int iClassNum = 0;
// Allow aborting if they have a class
if ( pViewer->GetPlayerClass() && iInput == 9 )
{
pViewer->MenuReset();
return true;
}
// Get the class number
for ( int i = 1; iInput && i < TFCLASS_CLASS_COUNT; i++ )
{
if ( !( pViewer->IsClassAvailable( (TFClass)i ) ) )
continue;
iInput--;
iClassNum = i;
}
// Ignore changeclass requests to their current class
if ( pViewer->GetPlayerClass() )
{
if ( (TFClass)iClassNum == pViewer->PlayerClass() )
{
pViewer->MenuReset();
return true;
}
}
if ( !pViewer->IsClassAvailable( (TFClass)iClassNum ) )
return false;
pViewer->ChangeClass( (TFClass)iClassNum );
pViewer->m_pCurrentMenu = NULL;
return true;
}

View File

@@ -0,0 +1,77 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef MENU_BASE_H
#define MENU_BASE_H
#pragma once
class CBasePlayer;
class CMenu;
enum
{
MENU_DEFAULT = 0,
MENU_TEAM,
MENU_CLASS,
// Insert new Menus here
MENU_LAST, // Total Number of menus
};
// Global list of menus
extern CMenu *gMenus[];
//-----------------------------------------------------------------------------
// Purpose: Base Menu Class
//-----------------------------------------------------------------------------
class CMenu
{
public:
CMenu();
virtual void RecalculateMenu( CBaseTFPlayer *pViewer );
virtual void Display( CBaseTFPlayer *pViewer, int allowed = 0xFFFF, int display_time = -1 );
virtual bool Input( CBaseTFPlayer *pViewer, int iInput );
protected:
char m_szMenuString[1024];
};
//-----------------------------------------------------------------------------
// Purpose: Team Menu
//-----------------------------------------------------------------------------
class CMenuTeam : public CMenu
{
public:
CMenuTeam();
virtual void RecalculateMenu( CBaseTFPlayer *pViewer );
virtual bool Input( CBaseTFPlayer *pViewer, int iInput );
};
//-----------------------------------------------------------------------------
// Purpose: Class Menu
//-----------------------------------------------------------------------------
class CMenuClass : public CMenu
{
public:
CMenuClass();
virtual void RecalculateMenu( CBaseTFPlayer *pViewer );
virtual bool Input( CBaseTFPlayer *pViewer, int iInput );
};
#endif // MENU_BASE_H

View File

@@ -0,0 +1,257 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "mortar_round.h"
#include "engine/IEngineSound.h"
#include "tf_gamerules.h"
#include "tf_team.h"
// Damage CVars
ConVar weapon_mortar_shell_damage( "weapon_mortar_shell_damage","0", FCVAR_NONE, "Mortar's standard shell maximum damage" );
ConVar weapon_mortar_shell_radius( "weapon_mortar_shell_radius","0", FCVAR_NONE, "Mortar's standard shell splash radius" );
ConVar weapon_mortar_starburst_damage( "weapon_mortar_starburst_damage","0", FCVAR_NONE, "Mortar's starburst maximum damage" );
ConVar weapon_mortar_starburst_radius( "weapon_mortar_starburst_radius","0", FCVAR_NONE, "Mortar's starburst splash radius" );
ConVar weapon_mortar_cluster_shells( "weapon_mortar_cluster_shells","0", FCVAR_NONE, "Number of shells a mortar cluster round bursts into" );
//=====================================================================================================
// MORTAR ROUND
//=====================================================================================================
BEGIN_DATADESC( CMortarRound )
// Function Pointers
DEFINE_FUNCTION( MissileTouch ),
DEFINE_FUNCTION( FallThink ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( mortar_round, CMortarRound );
PRECACHE_WEAPON_REGISTER(mortar_round);
CMortarRound::CMortarRound()
{
m_pSmokeTrail = NULL;
m_pLauncher = NULL;
m_iRoundType = MA_SHELL;
UseClientSideAnimation();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMortarRound::Precache( void )
{
PrecacheModel( "models/weapons/w_grenade.mdl" );
PrecacheScriptSound( "MortarRound.StopSound" );
PrecacheScriptSound( "MortarRound.IncomingSound" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMortarRound::Spawn( void )
{
Precache();
SetMoveType( MOVETYPE_FLYGRAVITY );
SetSolid( SOLID_BBOX );
SetModel( "models/weapons/w_grenade.mdl" );
UTIL_SetSize( this, vec3_origin, vec3_origin );
SetTouch( MissileTouch );
SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
// Trail smoke
m_pSmokeTrail = SmokeTrail::CreateSmokeTrail();
if ( m_pSmokeTrail )
{
m_pSmokeTrail->m_SpawnRate = 90;
m_pSmokeTrail->m_ParticleLifetime = 1.5;
m_pSmokeTrail->m_StartColor.Init(0.0, 0.0, 0.0);
m_pSmokeTrail->m_EndColor.Init( 0.5,0.5,0.5 );
m_pSmokeTrail->m_StartSize = 10;
m_pSmokeTrail->m_EndSize = 50;
m_pSmokeTrail->m_SpawnRadius = 1;
m_pSmokeTrail->m_MinSpeed = 15;
m_pSmokeTrail->m_MaxSpeed = 25;
m_pSmokeTrail->SetLifetime(15);
m_pSmokeTrail->FollowEntity( this );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMortarRound::MissileTouch( CBaseEntity *pOther )
{
CMortarRound *pRound = dynamic_cast< CMortarRound* >(pOther);
if ( pRound )
return;
// Stop emitting smoke/sound
m_pSmokeTrail->SetEmit(false);
EmitSound( "MortarRound.StopSound" );
// Create an explosion.
if ( m_iRoundType == MA_STARBURST )
{
// Small explosion, blind people in the area
float flBlind = 0;
// Shift it up a bit for the explosion
SetLocalOrigin( GetAbsOrigin() + Vector(0,0,32) );
CPASFilter filter( GetAbsOrigin() );
te->Explosion( filter, 0.0, &GetAbsOrigin(), g_sModelIndexFireball, 3.0, 15, TE_EXPLFLAG_NONE, 512, 100 );
RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), weapon_mortar_starburst_damage.GetFloat(), DMG_BLAST ), GetAbsOrigin(), weapon_mortar_starburst_radius.GetFloat(), CLASS_NONE, NULL );
// Blind all players nearby
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
if ( pPlayer )
{
Vector vecSrc = pPlayer->EyePosition();
Vector vecToTarget = (vecSrc - GetAbsOrigin());
float flLength = VectorNormalize( vecToTarget );
// If the player's looking at the grenade, blind him a lot
if ( flLength < 2048 )
{
Vector forward, right, up;
AngleVectors( pPlayer->pl.v_angle, &forward, &right, &up );
float flDot = DotProduct( vecToTarget, forward );
// Msg( "Dot: %f\n", flDot );
// Make sure it's in front of the player
if ( flDot < 0.0f )
{
flDot = fabs( DotProduct(vecToTarget, right ) ) + fabs( DotProduct(vecToTarget, up ) );
// Open LOS?
trace_t tr;
UTIL_TraceLine( vecSrc, GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
if ( ( tr.fraction == 1.0f ) || ( tr.m_pEnt == pPlayer ) )
{
flBlind = flDot;
}
else
{
// Blocked blind is only half as effective
flBlind = flDot * 0.5;
}
}
else if ( flLength < 512 )
{
// Otherwise, if the player's near the grenade blind him a little
flBlind = 0.2;
}
// Flash the screen red
color32 white = {255,255,255, 255};
white.a = MIN( (flBlind * 255), 255 );
UTIL_ScreenFade( pPlayer, white, 0.3, 5.0, 0 );
}
}
}
UTIL_Remove( this );
}
else
{
// Large explosion
// Shift it up a bit for the explosion
SetLocalOrigin( GetAbsOrigin() + Vector(0,0,64) );
CPASFilter filter( GetAbsOrigin() );
te->Explosion( filter, 0.0, &GetAbsOrigin(), g_sModelIndexFireball, 10.0, 15, TE_EXPLFLAG_NONE, 512, 300 );
RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), weapon_mortar_shell_damage.GetFloat(), DMG_BLAST ), GetAbsOrigin(), weapon_mortar_shell_radius.GetFloat(), CLASS_NONE, NULL );
UTIL_Remove( this );
}
}
//-----------------------------------------------------------------------------
// Purpose: Play a fall sound when the mortar round begins to fall
//-----------------------------------------------------------------------------
void CMortarRound::FallThink( void )
{
if ( m_pLauncher )
{
CRecipientFilter filter;
filter.AddAllPlayers();
EmitSound( filter, entindex(), "MortarRound.IncomingSound" );
// Cluster bombs split up in the air
if ( m_iRoundType == MA_CLUSTER )
{
Vector forward, right;
QAngle angles;
VectorAngles( GetAbsVelocity(), angles );
SetLocalAngles( angles );
AngleVectors( GetLocalAngles(), &forward, &right, NULL );
for ( int i = 0; i < weapon_mortar_cluster_shells.GetInt(); i++ )
{
Vector vecVelocity = GetAbsVelocity();
vecVelocity += forward * random->RandomFloat( -200, 200 );
vecVelocity += right * random->RandomFloat( -200, 200 );
CMortarRound *pRound = CMortarRound::Create( GetAbsOrigin(), vecVelocity, GetOwnerEntity() ? GetOwnerEntity()->edict() : NULL );
pRound->SetLauncher( m_pLauncher );
pRound->ChangeTeam( GetTeamNumber() );
pRound->m_iRoundType = MA_SHELL;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Keep a pointer to the Launcher
//-----------------------------------------------------------------------------
void CMortarRound::SetLauncher( CVehicleMortar *pLauncher )
{
m_pLauncher = pLauncher;
}
//-----------------------------------------------------------------------------
// Purpose: Set the point at which we should start playing the fall sound
//-----------------------------------------------------------------------------
void CMortarRound::SetFallTime( float flFallTime )
{
SetThink( FallThink );
SetNextThink( gpGlobals->curtime + flFallTime );
}
//-----------------------------------------------------------------------------
// Purpose: Set the round's type
//-----------------------------------------------------------------------------
void CMortarRound::SetRoundType( int iRoundType )
{
m_iRoundType = iRoundType;
}
//-----------------------------------------------------------------------------
// Purpose: Create a missile
//-----------------------------------------------------------------------------
CMortarRound* CMortarRound::Create( const Vector &vecOrigin, const Vector &vecVelocity, edict_t *pentOwner = NULL )
{
CMortarRound *pGrenade = (CMortarRound*)CreateEntityByName("mortar_round");
UTIL_SetOrigin( pGrenade, vecOrigin );
pGrenade->SetOwnerEntity( Instance( pentOwner ) );
pGrenade->Spawn();
pGrenade->SetAbsVelocity( vecVelocity );
QAngle angles;
VectorAngles( vecVelocity, angles );
pGrenade->SetLocalAngles( angles );
return pGrenade;
}

View File

@@ -0,0 +1,48 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef MORTAR_ROUND_H
#define MORTAR_ROUND_H
#ifdef _WIN32
#pragma once
#endif
#include "tf_vehicle_mortar.h"
#include "smoke_trail.h"
class CMortarRound : public CBaseAnimating
{
public:
DECLARE_CLASS( CMortarRound, CBaseAnimating );
CMortarRound();
DECLARE_DATADESC();
public:
virtual void Spawn( void );
virtual void Precache( void );
virtual void MissileTouch( CBaseEntity *pOther );
virtual void FallThink( void );
virtual void SetLauncher( CVehicleMortar *pLauncher );
virtual void SetFallTime( float flFallTime );
virtual void SetRoundType( int iRoundType );
// Damage type accessors
virtual int GetDamageType() const { return DMG_BLAST; }
static CMortarRound* CMortarRound::Create( const Vector &vecOrigin, const Vector &vecVelocity, edict_t *pentOwner );
SmokeTrail *m_pSmokeTrail;
CHandle<CVehicleMortar> m_pLauncher;
int m_iRoundType;
};
#endif // MORTAR_ROUND_H

View File

@@ -0,0 +1,577 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The builder bug
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "AI_Task.h"
#include "AI_Default.h"
#include "AI_Schedule.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Navigator.h"
#include "activitylist.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "tf_player.h"
#include "EntityList.h"
#include "ndebugoverlay.h"
#include "shake.h"
#include "monstermaker.h"
#include "decals.h"
#include "vstdlib/random.h"
#include "tf_obj.h"
#include "engine/IEngineSound.h"
#include "IEffects.h"
#include "npc_bug_builder.h"
#include "npc_bug_hole.h"
ConVar npc_bug_builder_health( "npc_bug_builder_health", "100" );
BEGIN_DATADESC( CNPC_Bug_Builder )
DEFINE_FIELD( m_flIdleDelay, FIELD_FLOAT ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( npc_bug_builder, CNPC_Bug_Builder );
IMPLEMENT_CUSTOM_AI( npc_bug_builder, CNPC_Bug_Builder );
// Dawdling details
// Max & Min distances for dawdle forward movement
#define DAWDLE_MIN_DIST 64
#define DAWDLE_MAX_DIST 1024
//==================================================
// Bug Conditions
//==================================================
enum BugConditions
{
COND_BBUG_RETURN_TO_BUGHOLE = LAST_SHARED_CONDITION,
};
//==================================================
// Bug Schedules
//==================================================
enum BugSchedules
{
SCHED_BBUG_FLEE_ENEMY = LAST_SHARED_SCHEDULE,
SCHED_BBUG_RETURN_TO_BUGHOLE,
SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE,
SCHED_BBUG_DAWDLE,
};
//==================================================
// Bug Tasks
//==================================================
enum BugTasks
{
TASK_BBUG_GET_PATH_TO_FLEE = LAST_SHARED_TASK,
TASK_BBUG_GET_PATH_TO_BUGHOLE,
TASK_BBUG_HOLE_REMOVE,
TASK_BBUG_GET_PATH_TO_DAWDLE,
TASK_BBUG_FACE_DAWDLE,
};
//==================================================
// Bug Activities
//==================================================
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CNPC_Bug_Builder::CNPC_Bug_Builder( void )
{
m_flFieldOfView = 0.5f;
m_flIdleDelay = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose: Setup our schedules and tasks, etc.
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::InitCustomSchedules( void )
{
INIT_CUSTOM_AI( CNPC_Bug_Builder );
// Schedules
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_FLEE_ENEMY );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE );
ADD_CUSTOM_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_DAWDLE );
// Conditions
ADD_CUSTOM_CONDITION( CNPC_Bug_Builder, COND_BBUG_RETURN_TO_BUGHOLE );
// Tasks
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_GET_PATH_TO_FLEE );
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_GET_PATH_TO_BUGHOLE );
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_HOLE_REMOVE );
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_GET_PATH_TO_DAWDLE );
ADD_CUSTOM_TASK( CNPC_Bug_Builder, TASK_BBUG_FACE_DAWDLE );
// Activities
//ADD_CUSTOM_ACTIVITY( CNPC_Bug_Builder, ACT_BUG_WARRIOR_DISTRACT );
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_FLEE_ENEMY );
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE );
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE );
AI_LOAD_SCHEDULE( CNPC_Bug_Builder, SCHED_BBUG_DAWDLE );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::Spawn( void )
{
Precache();
SetModel( BUG_BUILDER_MODEL );
SetHullType(HULL_TINY);
SetHullSizeNormal();
SetDefaultEyeOffset();
SetViewOffset( (WorldAlignMins() + WorldAlignMaxs()) * 0.5 ); // See from my center
SetDistLook( 1024.0 );
m_flNextDawdle = 0;
SetNavType(NAV_GROUND);
m_NPCState = NPC_STATE_NONE;
SetBloodColor( BLOOD_COLOR_YELLOW );
m_iHealth = npc_bug_builder_health.GetFloat();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
NPCInit();
BaseClass::Spawn();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::Precache( void )
{
PrecacheModel( BUG_BUILDER_MODEL );
PrecacheScriptSound( "NPC_Bug_Builder.Idle" );
PrecacheScriptSound( "NPC_Bug_Builder.Pain" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CNPC_Bug_Builder::SelectSchedule( void )
{
// If I'm not in idle anymore, don't idle
if ( m_NPCState != NPC_STATE_IDLE )
{
m_flNextDawdle = 0;
}
switch ( m_NPCState )
{
case NPC_STATE_IDLE:
{
// BugHole might be requesting help
if ( HasCondition( COND_BBUG_RETURN_TO_BUGHOLE ) )
return SCHED_BBUG_RETURN_TO_BUGHOLE;
// Setup to dawdle a bit from now
if ( !m_flNextDawdle )
{
m_flNextDawdle = gpGlobals->curtime + random->RandomFloat( 3.0, 5.0 );
}
else if ( m_flNextDawdle < gpGlobals->curtime )
{
m_flNextDawdle = 0;
return SCHED_BBUG_DAWDLE;
}
// When I take damage, I flee
if ( HasCondition( COND_LIGHT_DAMAGE | COND_HEAVY_DAMAGE ) )
return SCHED_BBUG_FLEE_ENEMY;
// Return to my bughole
//return SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE;
break;
}
case NPC_STATE_ALERT:
{
// BugHole might be requesting help
if ( HasCondition( COND_BBUG_RETURN_TO_BUGHOLE ) )
return SCHED_BBUG_RETURN_TO_BUGHOLE;
// When I take damage, I flee
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
return SCHED_BBUG_FLEE_ENEMY;
break;
}
case NPC_STATE_COMBAT:
{
// Did I lose my enemy?
if ( HasCondition ( COND_LOST_ENEMY ) || HasCondition ( COND_ENEMY_UNREACHABLE ) )
{
SetEnemy( NULL );
SetState(NPC_STATE_IDLE);
return BaseClass::SelectSchedule();
}
// When I take damage, I flee
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
return SCHED_BBUG_FLEE_ENEMY;
}
break;
}
return BaseClass::SelectSchedule();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::StartTask( const Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_BBUG_GET_PATH_TO_FLEE:
{
// Always tell our bughole that we're under attack
if ( m_hMyBugHole )
{
m_hMyBugHole->IncomingFleeingBug( this );
}
// If we have no squad, or we couldn't get a path to our squadmate, move to our bughole
if ( m_hMyBugHole )
{
SetTarget( m_hMyBugHole );
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN );
if ( GetNavigator()->SetGoal( goal ) )
{
TaskComplete();
return;
}
}
TaskComplete();
}
break;
case TASK_BBUG_GET_PATH_TO_BUGHOLE:
{
// Get a path back to my bughole
// If we have no squad, or we couldn't get a path to our squadmate, look for a bughole
if ( m_hMyBugHole )
{
SetTarget( m_hMyBugHole );
AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, ACT_RUN );
if ( GetNavigator()->SetGoal( goal ) )
{
TaskComplete();
return;
}
}
TaskFail( "Couldn't get to bughole." );
}
break;
case TASK_BBUG_HOLE_REMOVE:
{
TaskComplete();
// Crawl inside the bughole and remove myself
AddEffects( EF_NODRAW );
AddSolidFlags( FSOLID_NOT_SOLID );
Event_Killed( CTakeDamageInfo( this, this, 200, DMG_CRUSH ) );
// Tell the bughole
if ( m_hMyBugHole )
{
m_hMyBugHole->BugReturned();
}
}
break;
case TASK_BBUG_GET_PATH_TO_DAWDLE:
{
// Get a dawdle point ahead of us
Vector vecForward, vecTarget;
AngleVectors( GetAbsAngles(), &vecForward );
VectorMA( GetAbsOrigin(), random->RandomFloat( DAWDLE_MIN_DIST, DAWDLE_MAX_DIST ), vecForward, vecTarget );
// See how far we could move ahead
trace_t tr;
UTIL_TraceEntity( this, GetAbsOrigin(), vecTarget, MASK_SOLID, &tr);
float flDistance = tr.fraction * (vecTarget - GetAbsOrigin()).Length();
if ( flDistance >= DAWDLE_MIN_DIST )
{
AI_NavGoal_t goal( tr.endpos );
GetNavigator()->SetGoal( goal );
}
TaskComplete();
}
break;
case TASK_BBUG_FACE_DAWDLE:
{
// Turn a random amount to the right
float flYaw = GetMotor()->GetIdealYaw();
flYaw = flYaw + random->RandomFloat( 45, 135 );
GetMotor()->SetIdealYaw( UTIL_AngleMod(flYaw) );
SetTurnActivity();
break;
}
break;
default:
BaseClass::StartTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::RunTask( const Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_BBUG_FACE_DAWDLE:
{
GetMotor()->UpdateYaw();
if ( FacingIdeal() )
{
TaskComplete();
}
break;
}
default:
BaseClass::RunTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_Bug_Builder::FValidateHintType(CAI_Hint *pHint)
{
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pVictim -
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::Event_Killed( const CTakeDamageInfo &info )
{
BaseClass::Event_Killed( info );
// Remove myself in a minute
if ( !ShouldFadeOnDeath() )
{
SetThink( SUB_Remove );
SetNextThink( gpGlobals->curtime + 20 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEvent -
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::HandleAnimEvent( animevent_t *pEvent )
{
BaseClass::HandleAnimEvent( pEvent );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CNPC_Bug_Builder::MaxYawSpeed( void )
{
return 2.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::IdleSound( void )
{
EmitSound( "NPC_Bug_Builder.Idle" );
m_flIdleDelay = gpGlobals->curtime + 4.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::PainSound( const CTakeDamageInfo &info )
{
EmitSound( "NPC_Bug_Builder.Pain" );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNPC_Bug_Builder::ShouldPlayIdleSound( void )
{
//Only do idles in the right states
if ( ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT ) )
return false;
//Gagged monsters don't talk
if ( HasSpawnFlags( SF_NPC_GAG ) )
return false;
//Don't cut off another sound or play again too soon
if ( m_flIdleDelay > gpGlobals->curtime )
return false;
//Randomize it a bit
if ( random->RandomInt( 0, 20 ) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::SetBugHole( CMaker_BugHole *pBugHole )
{
m_hMyBugHole = pBugHole;
}
//-----------------------------------------------------------------------------
// Purpose: BugHole is calling me home to defend it
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::ReturnToBugHole( void )
{
SetCondition( COND_BBUG_RETURN_TO_BUGHOLE );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Bug_Builder::AlertSound( void )
{
if ( GetEnemy() )
{
//FIXME: We need a better solution for inner-squad alerts!!
//SOUND_DANGER is designed to frighten NPC's away. Need a different SOUND_ type.
CSoundEnt::InsertSound( SOUND_DANGER, GetEnemy()->GetAbsOrigin(), 1024, 0.5f, this );
}
}
//-----------------------------------------------------------------------------
// Purpose: Overridden for team handling
//-----------------------------------------------------------------------------
Disposition_t CNPC_Bug_Builder::IRelationType( CBaseEntity *pTarget )
{
// Builders ignore everything
return D_NU;
}
//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------
//=========================================================
// Dawdle around
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_BBUG_DAWDLE,
" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 32"
" TASK_BBUG_GET_PATH_TO_DAWDLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_BBUG_FACE_DAWDLE 0"
" "
" Interrupts"
" COND_LIGHT_DAMAGE"
" COND_HEAVY_DAMAGE"
);
//=========================================================
// Flee from our enemy
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_BBUG_FLEE_ENEMY,
" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_BBUG_GET_PATH_TO_FLEE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_TURN_RIGHT 180"
" "
" Interrupts"
" COND_ENEMY_DEAD"
" COND_LOST_ENEMY"
);
//=========================================================
// Retreat to a bughole
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_BBUG_RETURN_TO_BUGHOLE,
" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_BBUG_GET_PATH_TO_BUGHOLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" "
" Interrupts"
" COND_NEW_ENEMY"
" COND_HEAR_COMBAT"
" COND_HEAR_DANGER"
);
//=========================================================
// Return to a bughole and remove myself
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_BBUG_RETURN_TO_BUGHOLE_AND_REMOVE,
" Tasks"
" TASK_WAIT 5" // Wait for 5-10 seconds to see if anything happens
" TASK_WAIT_RANDOM 5"
" TASK_SET_TOLERANCE_DISTANCE 128"
" TASK_BBUG_GET_PATH_TO_BUGHOLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_BBUG_HOLE_REMOVE 0"
" "
" Interrupts"
" COND_NEW_ENEMY"
" COND_HEAR_COMBAT"
" COND_HEAR_DANGER"
);

View File

@@ -0,0 +1,69 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef NPC_BUG_BUILDER_H
#define NPC_BUG_BUILDER_H
#ifdef _WIN32
#pragma once
#endif
#include "AI_BaseNPC.h"
#define BUG_BUILDER_MODEL "models/npcs/bugs/bug_builder.mdl"
class CMaker_BugHole;
//-----------------------------------------------------------------------------
// Purpose: BUILDER BUG
//-----------------------------------------------------------------------------
class CNPC_Bug_Builder : public CAI_BaseNPC
{
DECLARE_CLASS( CNPC_Bug_Builder, CAI_BaseNPC );
public:
CNPC_Bug_Builder( void );
virtual void Spawn( void );
virtual void Precache( void );
virtual int SelectSchedule( void );
virtual void StartTask( const Task_t *pTask );
virtual void RunTask( const Task_t *pTask );
virtual float MaxYawSpeed( void );
virtual void HandleAnimEvent( animevent_t *pEvent );
virtual void IdleSound( void );
virtual void PainSound( const CTakeDamageInfo &info );
virtual void AlertSound( void );
virtual bool FValidateHintType(CAI_Hint *pHint);
virtual bool ShouldPlayIdleSound( void );
virtual Class_T Classify( void ) { return CLASS_ANTLION; }
virtual int GetSoundInterests( void ) { return 0; }
DECLARE_DATADESC();
// BugHole handling
void SetBugHole( CMaker_BugHole *pBugHole );
void ReturnToBugHole( void );
private:
virtual Disposition_t IRelationType( CBaseEntity *pTarget );
void Event_Killed( const CTakeDamageInfo &info );
float m_flIdleDelay;
float m_flNextDawdle;
// BugHole handling
CHandle< CMaker_BugHole > m_hMyBugHole;
DEFINE_CUSTOM_AI;
};
#endif // NPC_BUG_BUILDER_H

View File

@@ -0,0 +1,392 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Bug hole
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "AI_Task.h"
#include "AI_Default.h"
#include "AI_Schedule.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "activitylist.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "tf_player.h"
#include "EntityList.h"
#include "ndebugoverlay.h"
#include "shake.h"
#include "monstermaker.h"
#include "decals.h"
#include "vstdlib/random.h"
#include "tf_obj.h"
#include "engine/IEngineSound.h"
#include "IEffects.h"
#include "npc_bug_warrior.h"
#include "npc_bug_builder.h"
#include "npc_bug_hole.h"
LINK_ENTITY_TO_CLASS( npc_bughole, CMaker_BugHole );
IMPLEMENT_SERVERCLASS_ST(CMaker_BugHole, DT_Maker_BugHole)
END_SEND_TABLE();
BEGIN_DATADESC( CMaker_BugHole )
DEFINE_KEYFIELD( m_iMaxPool, FIELD_INTEGER, "PoolSize" ),
DEFINE_KEYFIELD( m_flPoolRegenTime, FIELD_FLOAT, "PoolRegen" ),
DEFINE_KEYFIELD( m_flPatrolTime, FIELD_FLOAT, "PatrolTime" ),
DEFINE_KEYFIELD( m_iszPatrolPathName, FIELD_STRING, "PatrolName" ),
DEFINE_KEYFIELD( m_iMaxNumberOfPatrollers, FIELD_INTEGER, "MaxPatrollers" ),
DEFINE_KEYFIELD( m_iMaxNumberOfBuilders, FIELD_INTEGER, "MaxBuilders" ),
END_DATADESC()
// Maximum speed at which a bughole thinks. Regen/Spawn times faster than this won't make it work faster.
#define BUGHOLE_THINK_SPEED 3.0
static ConVar npc_bughole_health( "npc_bughole_health","300", FCVAR_NONE, "Bug hole's health." );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMaker_BugHole::CMaker_BugHole( void)
{
m_iszNPCClassname_Warrior = MAKE_STRING( "npc_bug_warrior" );
m_iszNPCClassname_Builder = MAKE_STRING( "npc_bug_builder" );
m_iszNPCClassname = m_iszNPCClassname_Warrior;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaker_BugHole::Spawn( void )
{
// Set these up before calling base spawn
m_spawnflags |= SF_NPCMAKER_INF_CHILD;
m_bDisabled = true;
// Start with a full pool
m_iPool = m_iMaxPool;
BaseClass::Spawn();
// Bug holes are destroyable
SetSolid( SOLID_BBOX );
SetModel( "models/npcs/bugs/bug_hole.mdl" );
m_takedamage = DAMAGE_YES;
m_iHealth = npc_bughole_health.GetInt();
// Setup spawn & regen times
m_flNextSpawnTime = 0;
m_flNextRegenTime = gpGlobals->curtime + m_flPoolRegenTime;
m_flNextPatrolTime = gpGlobals->curtime + m_flPatrolTime;
// Override the base class think, and think with some random so bugholes don't all think at the same time
SetThink ( BugHoleThink );
SetNextThink( gpGlobals->curtime + BUGHOLE_THINK_SPEED + random->RandomFloat( -0.5, 0.5 ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaker_BugHole::Precache( void )
{
BaseClass::Precache();
PrecacheModel( "models/npcs/bugs/bug_hole.mdl" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaker_BugHole::BugHoleThink( void )
{
// Regenerate our bug pool
if ( m_flNextRegenTime < gpGlobals->curtime )
{
if ( m_iPool < m_iMaxPool )
{
m_iPool++;
}
m_flNextRegenTime += m_flPoolRegenTime;
}
// Spawn if we're set to
if ( m_flNextSpawnTime && m_flNextSpawnTime < gpGlobals->curtime )
{
m_flNextSpawnTime = 0;
MakeNPC();
}
else
{
// If I can see a player, try and spawn a bug
CBaseEntity *pList[100];
Vector vecDelta( 768, 768, 768 );
int count = UTIL_EntitiesInBox( pList, 32, GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, FL_CLIENT|FL_OBJECT );
for ( int i = 0; i < count; i++ )
{
CBaseEntity *pEnt = pList[i];
if ( pEnt->IsAlive() && FVisible(pEnt) )
{
BugHoleUnderAttack();
break;
}
}
}
// Send out a patrol if we haven't spawned anything for a long time
if ( m_flNextPatrolTime < gpGlobals->curtime )
{
m_flNextPatrolTime = gpGlobals->curtime + m_flPatrolTime;
StartPatrol();
CheckBuilder();
}
SetNextThink( gpGlobals->curtime + BUGHOLE_THINK_SPEED );
}
//-----------------------------------------------------------------------------
// Purpose: Spawn a bug, if we're not waiting to spawn one already
//-----------------------------------------------------------------------------
void CMaker_BugHole::SpawnBug( float flTime )
{
// If no time was passed in, spawn immediately
if ( !flTime )
{
MakeNPC();
}
else if ( !m_flNextSpawnTime )
{
m_flNextSpawnTime = gpGlobals->curtime + flTime;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaker_BugHole::SpawnWarrior( float flTime )
{
m_iszNPCClassname = m_iszNPCClassname_Warrior;
SpawnBug( flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMaker_BugHole::SpawnBuilder( float flTime )
{
m_iszNPCClassname = m_iszNPCClassname_Builder;
SpawnBug( flTime );
}
//-----------------------------------------------------------------------------
// Purpose: BugHole has spotted a player near it
//-----------------------------------------------------------------------------
void CMaker_BugHole::BugHoleUnderAttack( void )
{
// Call any patrollers back to defend the base
for ( int i = 0; i < m_aWarriorBugs.Size(); i++ )
{
if ( m_aWarriorBugs[i]->IsPatrolling() )
{
m_aWarriorBugs[i]->ReturnToBugHole();
}
}
// Try and spawn a warrior
SpawnWarrior( random->RandomFloat( 1.0, 3.0 ) );
}
//-----------------------------------------------------------------------------
// Purpose: Send a bug out to patrol
//-----------------------------------------------------------------------------
void CMaker_BugHole::StartPatrol( void )
{
// Don't patrol if I don't have a patrol name
if ( !m_iszPatrolPathName || !m_iMaxNumberOfPatrollers )
return;
// If I don't have any children, spawn one for to patrol with
if ( m_nLiveChildren < m_iMaxNumberOfPatrollers )
{
SpawnWarrior(0);
// Think again to use the bug we just created
m_flNextPatrolTime = gpGlobals->curtime + 2.0;
}
// We might have failed due to having none in the pool
if ( !m_aWarriorBugs.Size() )
return;
// Count number of bugs patrolling
int i, iCount;
iCount = 0;
for ( i = 0; i < m_aWarriorBugs.Size(); i++ )
{
if ( m_aWarriorBugs[i]->IsPatrolling() )
{
iCount++;
}
}
// Find bugs willing to patrol
for ( i = 0; i < m_aWarriorBugs.Size(); i++ )
{
// Make sure we don't have too many
if ( iCount >= m_iMaxNumberOfPatrollers )
return;
if ( m_aWarriorBugs[i]->StartPatrolling( m_iszPatrolPathName ) )
{
iCount++;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: See if we should spawn a builder bug
//-----------------------------------------------------------------------------
void CMaker_BugHole::CheckBuilder( void )
{
// If my pool is full, and I have spaw builder spots, spawn a builder bug
/*
if ( m_iPool == m_iMaxPool && m_aBuilderBugs.Size() < m_iMaxNumberOfBuilders )
{
SpawnBuilder(0);
}
*/
}
//-----------------------------------------------------------------------------
// Purpose: Bughole makes multiple types of bugs
//-----------------------------------------------------------------------------
void CMaker_BugHole::MakeNPC( void )
{
// Don't try and patrol for a while
m_flNextPatrolTime = gpGlobals->curtime + m_flPatrolTime;
// If my pool is empty, don't spawn a bug
if ( !m_iPool )
return;
BaseClass::MakeNPC();
}
//-----------------------------------------------------------------------------
// Purpose: Hook to allow bugs to move before they spawn
//-----------------------------------------------------------------------------
void CMaker_BugHole::ChildPreSpawn( CAI_BaseNPC *pChild )
{
BaseClass::ChildPreSpawn( pChild );
// Drop the bug down and remove it's onground flag
Vector origin = GetLocalOrigin();
origin.z -= 64;
pChild->SetLocalOrigin( origin );
pChild->SetGroundEntity( NULL );
}
//-----------------------------------------------------------------------------
// Purpose: Hook to allow bugs to pack specific data into their known class fields
// Input : *pChild - pointer to the spawned entity to work on
//-----------------------------------------------------------------------------
void CMaker_BugHole::ChildPostSpawn( CAI_BaseNPC *pChild )
{
BaseClass::ChildPostSpawn( pChild );
// May be a warrior or a builder
CNPC_Bug_Warrior *pWarrior = dynamic_cast<CNPC_Bug_Warrior*>((CAI_BaseNPC*)pChild);
if ( pWarrior )
{
pWarrior->SetBugHole( this );
// Add him to my bug list
WarriorHandle_t hHandle;
hHandle = pWarrior;
m_aWarriorBugs.AddToTail( hHandle );
}
else
{
CNPC_Bug_Builder *pBuilder = dynamic_cast<CNPC_Bug_Builder*>((CAI_BaseNPC*)pChild);
ASSERT( pBuilder );
pBuilder->SetBugHole( this );
// Add him to my bug list
BuilderHandle_t hHandle;
hHandle = pBuilder;
m_aBuilderBugs.AddToTail( hHandle );
}
m_iPool--;
}
//-----------------------------------------------------------------------------
// Purpose: Remove the bug from our list of bugs
//-----------------------------------------------------------------------------
void CMaker_BugHole::DeathNotice( CBaseEntity *pVictim )
{
BaseClass::DeathNotice( pVictim );
// May be a warrior or a builder
CNPC_Bug_Warrior *pWarrior = dynamic_cast<CNPC_Bug_Warrior*>((CAI_BaseNPC*)pVictim);
if ( pWarrior )
{
// Remove him from my list
WarriorHandle_t hHandle;
hHandle = pWarrior;
m_aWarriorBugs.FindAndRemove( hHandle );
}
else
{
CNPC_Bug_Builder *pBuilder = dynamic_cast<CNPC_Bug_Builder*>((CAI_BaseNPC*)pVictim);
ASSERT( pBuilder );
// Remove him from my list
BuilderHandle_t hHandle;
hHandle = pBuilder;
m_aBuilderBugs.FindAndRemove( hHandle );
}
}
//-----------------------------------------------------------------------------
// Purpose: On death, fall to the ground and vanish
//-----------------------------------------------------------------------------
void CMaker_BugHole::Event_Killed( const CTakeDamageInfo &info )
{
BaseClass::Event_Killed( info );
SetMoveType( MOVETYPE_FLYGRAVITY );
SetGroundEntity( NULL );
SetThink( SUB_Remove );
SetNextThink( gpGlobals->curtime + 5.0 );
}
//-----------------------------------------------------------------------------
// Purpose: A bug is fleeing to me, see if I want to do anything
//-----------------------------------------------------------------------------
void CMaker_BugHole::IncomingFleeingBug( CAI_BaseNPC *pBug )
{
SpawnWarrior( random->RandomFloat( 3.0, 5.0 ) );
// If I have available warriors, tell them to engage
for ( int i = 0; i < m_aWarriorBugs.Size(); i++ )
{
m_aWarriorBugs[i]->Assist( pBug );
}
}
//-----------------------------------------------------------------------------
// Purpose: One of my bugs has returned to me
//-----------------------------------------------------------------------------
void CMaker_BugHole::BugReturned( void )
{
if ( m_iPool < m_iMaxPool )
{
m_iPool++;
}
}

View File

@@ -0,0 +1,76 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef NPC_BUG_HOLE_H
#define NPC_BUG_HOLE_H
#ifdef _WIN32
#pragma once
#endif
class CNPC_Bug_Warrior;
class CNPC_Bug_Builder;
//-----------------------------------------------------------------------------
// Purpose: BUG HOLE
//-----------------------------------------------------------------------------
class CMaker_BugHole : public CNPCMaker
{
DECLARE_CLASS( CMaker_BugHole, CNPCMaker );
public:
CMaker_BugHole( void );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual void Spawn( void );
virtual void Precache( void );
virtual void MakeNPC( void );
virtual void ChildPreSpawn( CAI_BaseNPC *pChild );
virtual void ChildPostSpawn( CAI_BaseNPC *pChild );
virtual void DeathNotice( CBaseEntity *pVictim );
virtual void Event_Killed( const CTakeDamageInfo &info );
// Bug interactions
void BugHoleThink( void );
void SpawnBug( float flTime );
void SpawnWarrior( float flTime );
void SpawnBuilder( float flTime );
void BugHoleUnderAttack( void );
void StartPatrol( void );
void CheckBuilder( void );
void IncomingFleeingBug( CAI_BaseNPC *pBug );
void BugReturned( void );
private:
string_t m_iszNPCClassname_Warrior;
string_t m_iszNPCClassname_Builder;
// Bug pool
int m_iPool;
int m_iMaxPool;
float m_flPoolRegenTime;
float m_flNextSpawnTime;
float m_flNextRegenTime;
// Patrols
int m_iMaxNumberOfPatrollers;
float m_flPatrolTime;
float m_flNextPatrolTime;
string_t m_iszPatrolPathName;
// Builders
int m_iMaxNumberOfBuilders;
// List of bugs I have out there
typedef CHandle<CNPC_Bug_Warrior> WarriorHandle_t;
CUtlVector<WarriorHandle_t> m_aWarriorBugs;
typedef CHandle<CNPC_Bug_Builder> BuilderHandle_t;
CUtlVector<BuilderHandle_t> m_aBuilderBugs;
};
#endif // NPC_BUG_HOLE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef NPC_BUG_WARRIOR_H
#define NPC_BUG_WARRIOR_H
#ifdef _WIN32
#pragma once
#endif
#include "AI_BaseNPC.h"
// Animation events
#define BUG_WARRIOR_AE_MELEE_SOUND1 11 // Start attack sound
#define BUG_WARRIOR_AE_MELEE_HIT1 15 // Melee hit, one arm
// Attack range definitions
#define BUG_WARRIOR_MELEE1_RANGE 128.0f
#define BUG_WARRIOR_MELEE1_RANGE_MIN 128.0f
#define BUG_WARRIOR_MODEL "models/npcs/bugs/bug_warrior.mdl"
class CMaker_BugHole;
//-----------------------------------------------------------------------------
// Purpose: WARRIOR BUG
//-----------------------------------------------------------------------------
class CNPC_Bug_Warrior : public CAI_BaseNPC
{
DECLARE_CLASS( CNPC_Bug_Warrior, CAI_BaseNPC );
public:
CNPC_Bug_Warrior( void );
virtual void Spawn( void );
virtual void Precache( void );
virtual int SelectSchedule( void );
virtual int TranslateSchedule( int scheduleType );
virtual void StartTask( const Task_t *pTask );
virtual void RunTask( const Task_t *pTask );
virtual bool FValidateHintType(CAI_Hint *pHint);
virtual void HandleAnimEvent( animevent_t *pEvent );
virtual void IdleSound( void );
virtual void PainSound( const CTakeDamageInfo &info );
virtual void AlertSound( void );
virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender );
virtual bool ShouldPlayIdleSound( void );
virtual int MeleeAttack1Conditions( float flDot, float flDist );
virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
virtual Class_T Classify( void ) { return CLASS_ANTLION; }
virtual float MaxYawSpeed ( void );
virtual float CalcIdealYaw( const Vector &vecTarget );
DECLARE_DATADESC();
// Returns true if the bug should flee
bool ShouldFlee( void );
// Squad handling
bool IsAlone( void );
// BugHole handling
void SetBugHole( CMaker_BugHole *pBugHole );
void ReturnToBugHole( void );
void Assist( CAI_BaseNPC *pBug );
// Patrolling
bool StartPatrolling( string_t iszPatrolPathName );
bool IsPatrolling( void );
private:
virtual Disposition_t IRelationType( CBaseEntity *pTarget );
virtual int IRelationPriority( CBaseEntity *pTarget );
void Event_Killed( const CTakeDamageInfo &info );
void MeleeAttack( float distance, float damage, QAngle& viewPunch, Vector& shove );
float m_flIdleDelay;
// BugHole handling
CHandle< CMaker_BugHole > m_hMyBugHole;
CHandle< CAI_BaseNPC > m_hAssistTarget;
// Patrolling
string_t m_iszPatrolPathName;
int m_iPatrolPoint;
// Bug's decided he's not fleeing anymore
bool m_bFightingToDeath;
DEFINE_CUSTOM_AI;
};
#endif // NPC_BUG_WARRIOR_H

View File

@@ -0,0 +1,177 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_assist.h"
#include "tf_team.h"
#include "order_helpers.h"
// If a player has been shot within this time delta, commandos will get orders to assist.
#define COMMANDO_ASSIST_SHOT_DELAY 3.0f
// How close a commando has to be to a teammate to get an assist order.
#define COMMAND_ASSIST_DISTANCE 1200
#define COMMAND_ASSIST_DISTANCE_SQR (COMMAND_ASSIST_DISTANCE*COMMAND_ASSIST_DISTANCE)
IMPLEMENT_SERVERCLASS_ST( COrderAssist, DT_OrderAssist )
END_SEND_TABLE()
static bool IsValidFn_OnEnemyTeam( void *pUserData, int a )
{
edict_t *pEdict = engine->PEntityOfEntIndex( a+1 );
if ( !pEdict )
return false;
CBaseEntity *pBaseEntity = CBaseEntity::Instance( pEdict );
if ( !pBaseEntity )
return false;
CSortBase *p = (CSortBase*)pUserData;
return pBaseEntity->GetTeam() != p->m_pPlayer->GetTeam();
}
static bool IsValidFn_PlayersWantingAssist( void *pUserData, int a )
{
CSortBase *pSortBase = (CSortBase*)pUserData;
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)pSortBase->m_pPlayer->GetTeam()->GetPlayer( a );
if ( !pPlayer->IsAlive() )
{
// This guy sure could have used an assist but YOU'RE TOO SLOW!!!
return false;
}
// Don't try to assist yourself...
if ( pPlayer == pSortBase->m_pPlayer )
return false;
// Make sure this guy was shot recently.
if ( (gpGlobals->curtime - pPlayer->LastTimeDamagedByEnemy()) > COMMANDO_ASSIST_SHOT_DELAY )
return false;
// Is the guy close enough?
if ( pSortBase->m_pPlayer->GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ) > COMMAND_ASSIST_DISTANCE_SQR )
return false;
return true;
}
bool COrderAssist::CreateOrder( CPlayerClass *pClass )
{
// Search for a (live) nearby player who's just been shot.
CSortBase info;
info.m_pPlayer = pClass->GetPlayer();
int sorted[512];
int nSorted = BuildSortedActiveList(
sorted,
ARRAYSIZE( sorted ),
SortFn_TeamPlayersByDistance,
IsValidFn_PlayersWantingAssist,
&info,
pClass->GetTeam()->GetNumPlayers()
);
if ( nSorted )
{
COrderAssist *pOrder = new COrderAssist;
CBaseTFPlayer *pPlayerToAssist = (CBaseTFPlayer*)pClass->GetTeam()->GetPlayer( sorted[0] );
pClass->GetTeam()->AddOrder(
ORDER_ASSIST,
pPlayerToAssist,
info.m_pPlayer,
COMMAND_ASSIST_DISTANCE,
25,
pOrder );
// Add the closest enemies.
CSortBase enemySortInfo;
enemySortInfo.m_pPlayer = pPlayerToAssist;
int sortedEnemies[256];
int nSortedEnemies = BuildSortedActiveList(
sortedEnemies,
ARRAYSIZE( sortedEnemies ),
SortFn_PlayerEntitiesByDistance,
IsValidFn_OnEnemyTeam,
&info,
gpGlobals->maxClients
);
nSortedEnemies = MIN( nSortedEnemies, NUM_ASSIST_ENEMIES );
for ( int i=0; i < nSortedEnemies; i++ )
{
CBaseEntity *pEnt = CBaseEntity::Instance( engine->PEntityOfEntIndex( sortedEnemies[i] + 1 ) );
Assert( dynamic_cast<CBasePlayer*>( pEnt ) );
pOrder->m_Enemies[i] = pEnt;
}
}
return false;
}
bool COrderAssist::Update()
{
if ( !GetTargetEntity() || !GetTargetEntity()->IsAlive() )
return true;
return BaseClass::Update();
}
bool COrderAssist::UpdateOnEvent( COrderEvent_Base *pEvent )
{
if ( !GetTargetEntity() )
return true;
switch( pEvent->GetType() )
{
// If our boy dies, then he doesn't care about assistance anymore.
case ORDER_EVENT_PLAYER_KILLED:
{
COrderEvent_PlayerKilled *pKilled = (COrderEvent_PlayerKilled*)pEvent;
if ( pKilled->m_pPlayer == GetTargetEntity() )
return true;
}
break;
// Did we damage one of the enemies?
case ORDER_EVENT_PLAYER_DAMAGED:
{
COrderEvent_PlayerDamaged *pPlayerDamaged = (COrderEvent_PlayerDamaged*)pEvent;
if ( pPlayerDamaged->m_TakeDamageInfo.GetInflictor() == GetOwner() )
{
for ( int i=0; i < NUM_ASSIST_ENEMIES; i++ )
{
if ( pPlayerDamaged->m_pPlayerDamaged == m_Enemies[i].Get() )
{
// Reset the timer on the guy we're defending, in case we killed his
// attacker really quickly.
// CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetTargetEntity();
// pPlayer->m_flLastTimeDamagedByEnemy = 0;
return true;
}
}
}
}
break;
}
return BaseClass::UpdateOnEvent( pEvent );
}

View File

@@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_ORDER_ASSIST_H
#define TF_ORDER_ASSIST_H
#ifdef _WIN32
#pragma once
#endif
#include "order_player.h"
class CPlayerClass;
class COrderAssist : public COrderPlayer
{
public:
DECLARE_CLASS( COrderAssist, COrderPlayer );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool Update();
virtual bool UpdateOnEvent( COrderEvent_Base *pEvent );
private:
enum
{
NUM_ASSIST_ENEMIES = 2
};
// The order goes away when the player who has the assist order shoots
// one of these enemies.
CHandle<CBaseEntity> m_Enemies[NUM_ASSIST_ENEMIES];
};
#endif // TF_ORDER_ASSIST_H

View File

@@ -0,0 +1,55 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_buildsentrygun.h"
#include "tf_class_defender.h"
#include "tf_team.h"
#include "order_helpers.h"
// The defender will get orders to cover objects with sentry guns this far away.
#define SENTRYGUN_ORDER_DIST 3500
IMPLEMENT_SERVERCLASS_ST( COrderBuildSentryGun, DT_OrderBuildSentryGun )
END_SEND_TABLE()
bool COrderBuildSentryGun::CreateOrder( CPlayerClassDefender *pClass )
{
if ( !pClass->CanBuildSentryGun() )
return false;
COrderBuildSentryGun *pOrder = new COrderBuildSentryGun;
if ( OrderCreator_GenericObject( pClass, OBJ_SENTRYGUN_PLASMA, SENTRYGUN_ORDER_DIST, pOrder ) )
{
return true;
}
else
{
UTIL_RemoveImmediate( pOrder );
return false;
}
}
bool COrderBuildSentryGun::Update()
{
// If the entity we were supposed to cover with the sentry gun is covered now,
// then the order is done.
CBaseEntity *pEnt = GetTargetEntity();
if( !pEnt || !m_hOwningPlayer.Get() )
return true;
CTFTeam *pTeam = m_hOwningPlayer->GetTFTeam();
if( pTeam->IsCoveredBySentryGun( pEnt->GetAbsOrigin() ) )
return true;
return false;
}

View File

@@ -0,0 +1,38 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_BUILDSENTRYGUN_H
#define ORDER_BUILDSENTRYGUN_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class CPlayerClassDefender;
class COrderBuildSentryGun : public COrder
{
public:
DECLARE_CLASS( COrderBuildSentryGun, COrder );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClassDefender *pClass );
// COrder overrides.
public:
virtual bool Update( void );
};
#endif // ORDER_BUILDSENTRYGUN_H

View File

@@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_buildshieldwall.h"
#include "tf_team.h"
#include "order_helpers.h"
// The defender will get orders to cover objects with sentry guns this far away.
#define SANDBAG_ORDER_DIST 3500
IMPLEMENT_SERVERCLASS_ST( COrderBuildShieldWall, DT_OrderBuildShieldWall )
END_SEND_TABLE()
bool COrderBuildShieldWall::CreateOrder( CPlayerClass *pClass )
{
COrderBuildShieldWall *pOrder = new COrderBuildShieldWall;
if ( OrderCreator_GenericObject( pClass, OBJ_SHIELDWALL, 2000, pOrder ) )
{
return true;
}
else
{
UTIL_RemoveImmediate( pOrder );
return false;
}
}
bool COrderBuildShieldWall::Update()
{
CBaseEntity *pEnt = GetTargetEntity();
if( !pEnt )
return true;
if ( !m_hOwningPlayer.Get() || m_hOwningPlayer->CanBuild( OBJ_SHIELDWALL ) != CB_CAN_BUILD )
return true;
CTFTeam *pTeam = m_hOwningPlayer->GetTFTeam();
if ( pTeam->GetNumShieldWallsCoveringPosition( pEnt->GetAbsOrigin() ) )
return true;
return false;
}

View File

@@ -0,0 +1,35 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_BUILDSHIELDWALL_H
#define ORDER_BUILDSHIELDWALL_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class COrderBuildShieldWall : public COrder
{
public:
DECLARE_CLASS( COrderBuildShieldWall, COrder );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool Update( void );
};
#endif // ORDER_BUILDSHIELDWALL_H

View File

@@ -0,0 +1,26 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "order_events.h"
#include "tf_team.h"
//-----------------------------------------------------------------------------
// Purpose: Fire an event for all teams telling them to update their orders
//-----------------------------------------------------------------------------
void GlobalOrderEvent( COrderEvent_Base *pOrder )
{
// Loop through the teams
for ( int i = 0; i < GetNumberOfTeams(); i++ )
{
CTFTeam *pTeam = GetGlobalTFTeam( i );
pTeam->UpdateOrdersOnEvent( pOrder );
}
}

View File

@@ -0,0 +1,110 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_ORDER_EVENTS_H
#define TF_ORDER_EVENTS_H
#ifdef _WIN32
#pragma once
#endif
#include "tf_player.h"
//-----------------------------------------------------------------------------
// ORDER EVENTS
//-----------------------------------------------------------------------------
typedef enum
{
ORDER_EVENT_PLAYER_DISCONNECTED, // COrderEvent_PlayerDisconnected
ORDER_EVENT_PLAYER_KILLED, // CorderEvent_PlayerKilled
ORDER_EVENT_PLAYER_RESPAWNED, // COrderEvent_PlayerRespawned
ORDER_EVENT_OBJECT_DESTROYED, // COrderEvent_ObjectDestroyed
ORDER_EVENT_PLAYER_DAMAGED // COrderEvent_PlayerDamaged
} OrderEventType;
abstract_class COrderEvent_Base
{
public:
virtual OrderEventType GetType() = 0;
};
// Fire a global order event. It goes to all orders so they can determine if
// they want to react.
void GlobalOrderEvent( COrderEvent_Base *pOrder );
class COrderEvent_PlayerDisconnected : public COrderEvent_Base
{
public:
COrderEvent_PlayerDisconnected( CBaseEntity *pPlayer )
{
m_pPlayer = pPlayer;
}
virtual OrderEventType GetType() { return ORDER_EVENT_PLAYER_DISCONNECTED; }
CBaseEntity *m_pPlayer;
};
class COrderEvent_PlayerKilled : public COrderEvent_Base
{
public:
COrderEvent_PlayerKilled( CBaseEntity *pPlayer )
{
m_pPlayer = pPlayer;
}
virtual OrderEventType GetType() { return ORDER_EVENT_PLAYER_KILLED; }
CBaseEntity *m_pPlayer;
};
class COrderEvent_PlayerRespawned : public COrderEvent_Base
{
public:
COrderEvent_PlayerRespawned( CBaseEntity *pPlayer )
{
m_pPlayer = pPlayer;
}
virtual OrderEventType GetType() { return ORDER_EVENT_PLAYER_RESPAWNED; }
CBaseEntity *m_pPlayer;
};
class COrderEvent_ObjectDestroyed : public COrderEvent_Base
{
public:
COrderEvent_ObjectDestroyed( CBaseEntity *pObj )
{
m_pObject = pObj;
}
virtual OrderEventType GetType() { return ORDER_EVENT_OBJECT_DESTROYED; }
CBaseEntity *m_pObject;
};
class COrderEvent_PlayerDamaged : public COrderEvent_Base
{
public:
virtual OrderEventType GetType() { return ORDER_EVENT_PLAYER_DAMAGED; }
CBaseEntity *m_pPlayerDamaged;
CTakeDamageInfo m_TakeDamageInfo;
};
#endif // TF_ORDER_EVENTS_H

View File

@@ -0,0 +1,111 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_heal.h"
#include "tf_team.h"
#include "tf_playerclass.h"
#include "order_helpers.h"
#define MAX_HEAL_DIST 1500
IMPLEMENT_SERVERCLASS_ST( COrderHeal, DT_OrderHeal )
END_SEND_TABLE()
bool IsValidFn_Heal( void *pUserData, int a )
{
// Can't heal dead players.
CSortBase *p = (CSortBase*)pUserData;
CBasePlayer *pPlayer = p->m_pPlayer->GetTeam()->GetPlayer( a );
// Can't heal yourself...
if (p->m_pPlayer == pPlayer)
return false;
// Don't heal players that are too far away...
const Vector &vPlayer = p->m_pPlayer->GetAbsOrigin();
if (vPlayer.DistToSqr(pPlayer->GetAbsOrigin()) > MAX_HEAL_DIST * MAX_HEAL_DIST )
return false;
return pPlayer->IsAlive() && pPlayer->m_iHealth < pPlayer->m_iMaxHealth;
}
int SortFn_Heal( void *pUserData, int a, int b )
{
CSortBase *p = (CSortBase*)pUserData;
const Vector &vPlayer = p->m_pPlayer->GetAbsOrigin();
const Vector &va = p->m_pPlayer->GetTeam()->GetPlayer( a )->GetAbsOrigin();
const Vector &vb = p->m_pPlayer->GetTeam()->GetPlayer( b )->GetAbsOrigin();
return vPlayer.DistToSqr( va ) < vPlayer.DistToSqr( vb );
}
bool COrderHeal::CreateOrder( CPlayerClass *pClass )
{
CTFTeam *pTeam = pClass->GetTeam();
CSortBase info;
info.m_pPlayer = pClass->GetPlayer();
int sorted[MAX_PLAYERS];
int nSorted = BuildSortedActiveList(
sorted,
MAX_PLAYERS,
SortFn_Heal,
IsValidFn_Heal,
&info,
pTeam->GetNumPlayers()
);
if ( nSorted )
{
COrderHeal *pOrder = new COrderHeal;
pClass->GetTeam()->AddOrder(
ORDER_HEAL,
pTeam->GetPlayer( sorted[0] ),
pClass->GetPlayer(),
1e24,
60,
pOrder );
return true;
}
else
{
return false;
}
}
bool COrderHeal::Update()
{
CBaseEntity *pTarget = GetTargetEntity();
if ( !pTarget || pTarget->m_iHealth >= pTarget->m_iMaxHealth )
return true;
return false;
}
bool COrderHeal::UpdateOnEvent( COrderEvent_Base *pEvent )
{
if ( pEvent->GetType() == ORDER_EVENT_PLAYER_KILLED )
{
COrderEvent_PlayerKilled *pKilled = (COrderEvent_PlayerKilled*)pEvent;
if ( pKilled->m_pPlayer == GetTargetEntity() )
return true;
}
return BaseClass::UpdateOnEvent( pEvent );
}

View File

@@ -0,0 +1,39 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_HEAL_H
#define ORDER_HEAL_H
#ifdef _WIN32
#pragma once
#endif
#include "order_player.h"
class CPlayerClass;
class COrderHeal : public COrderPlayer
{
public:
DECLARE_CLASS( COrderHeal, COrderPlayer );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool Update();
virtual bool UpdateOnEvent( COrderEvent_Base *pEvent );
};
#endif // ORDER_HEAL_H

View File

@@ -0,0 +1,345 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_helpers.h"
#include "tf_team.h"
#include "tf_func_resource.h"
#include "tf_obj.h"
// ------------------------------------------------------------------------ //
// CSortBase implementation.
// ------------------------------------------------------------------------ //
CSortBase::CSortBase()
{
m_pPlayer = 0;
m_pTeam = 0;
}
CTFTeam* CSortBase::GetTeam()
{
if ( m_pTeam )
return m_pTeam;
else
return m_pPlayer->GetTFTeam();
}
// ------------------------------------------------------------------------ //
// Global functions.
// ------------------------------------------------------------------------ //
int SortFn_TeamPlayersByDistance( void *pUserData, int a, int b )
{
CSortBase *p = (CSortBase*)pUserData;
const Vector &vPlayer = p->m_pPlayer->GetAbsOrigin();
const Vector &va = p->m_pPlayer->GetTeam()->GetPlayer( a )->GetAbsOrigin();
const Vector &vb = p->m_pPlayer->GetTeam()->GetPlayer( b )->GetAbsOrigin();
return vPlayer.DistTo( va ) < vPlayer.DistTo( vb );
}
// This is a generic function that takes a number of items and builds a sorted
// list of the valid items.
int BuildSortedActiveList(
int *pList, // This is the list where the final data is placed.
int nMaxItems,
sortFn pSortFn, // Callbacks.
isValidFn pIsValidFn, // This can be null, in which case all items are valid.
void *pUserData, // Passed into the function pointers.
int nItems // Number of items in the list to sort.
)
{
// First build the list of active items.
if( nItems > nMaxItems )
nItems = nMaxItems;
int nActive = 0;
for( int i=0; i < nItems; i++ )
{
if( pIsValidFn )
{
if( !pIsValidFn( pUserData, i ) )
continue;
}
int j;
for( j=0; j < nActive; j++ )
{
Assert( pList[j] < nItems );
if( pSortFn( pUserData, i, pList[j] ) > 0 )
{
break;
}
}
// Slide everything up.
if( nActive )
{
Q_memmove( &pList[j+1], &pList[j], (nActive-j) * sizeof(int) );
}
// Add the new item to the list.
pList[j] = i;
++nActive;
for (int l = 0; l < nActive ; ++l )
{
Assert( pList[l] < nItems );
}
}
return nActive;
}
// Finds the closest resource zone without the specified object on it and
// gives an order to the player to build the object.
bool OrderCreator_ResourceZoneObject(
CBaseTFPlayer *pPlayer,
int objType,
COrder *pOrder
)
{
// Can we even build a resource box?
if ( pPlayer->CanBuild( objType ) != CB_CAN_BUILD )
return false;
CTFTeam *pTeam = pPlayer->GetTFTeam();
if( !pTeam )
return false;
// Let's have one near each resource zone that we own.
CResourceZone *pClosest = 0;
float flClosestDist = 100000000;
CBaseEntity *pEntity = NULL;
while( (pEntity = gEntList.FindEntityByClassname( pEntity, "trigger_resourcezone" )) != NULL )
{
CResourceZone *pZone = (CResourceZone*)pEntity;
// Ignore empty zones and zones not captured by this team.
if ( pZone->IsEmpty() || !pZone->GetActive() )
continue;
Vector vZoneCenter = pZone->WorldSpaceCenter();
// Look for a resource pump on this zone.
bool bPump = objType == OBJ_RESOURCEPUMP && pPlayer->NumPumpsOnResourceZone( pZone ) == 0;
if ( bPump )
{
// Make sure it's their preferred tech.
float flTestDist = pPlayer->GetAbsOrigin().DistTo( vZoneCenter );
if ( flTestDist < flClosestDist )
{
pClosest = pZone;
flClosestDist = flTestDist;
}
}
}
if ( pClosest )
{
// No pump here. Build one!
pPlayer->GetTFTeam()->AddOrder(
ORDER_BUILD,
pClosest,
pPlayer,
1e24,
60,
pOrder
);
return true;
}
else
{
return false;
}
}
int SortFn_PlayerObjectsByDistance( void *pUserData, int a, int b )
{
CSortBase *pSortBase = (CSortBase*)pUserData;
CBaseObject* pObjA = pSortBase->m_pPlayer->GetObject(a);
CBaseObject* pObjB = pSortBase->m_pPlayer->GetObject(b);
if (!pObjA)
return false;
if (!pObjB)
return true;
const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
return v.DistTo( pObjA->GetAbsOrigin() ) < v.DistTo( pObjB->GetAbsOrigin() );
}
int SortFn_TeamObjectsByDistance( void *pUserData, int a, int b )
{
CSortBase *pSortBase = (CSortBase*)pUserData;
CBaseObject *pObj1 = pSortBase->m_pPlayer->GetTFTeam()->GetObject( a );
CBaseObject *pObj2 = pSortBase->m_pPlayer->GetTFTeam()->GetObject( b );
const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
return v.DistTo( pObj1->GetAbsOrigin() ) < v.DistTo( pObj2->GetAbsOrigin() );
}
int SortFn_PlayerEntitiesByDistance( void *pUserData, int a, int b )
{
CSortBase *pSortBase = (CSortBase*)pUserData;
CBaseEntity *pObj1 = CBaseEntity::Instance( engine->PEntityOfEntIndex( a+1 ) );
CBaseEntity *pObj2 = CBaseEntity::Instance( engine->PEntityOfEntIndex( b+1 ) );
const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
return v.DistTo( pObj1->GetAbsOrigin() ) < v.DistTo( pObj2->GetAbsOrigin() );
}
int SortFn_DistanceAndConcentration( void *pUserData, int a, int b )
{
CSortBase *p = (CSortBase*)pUserData;
// Compare distances. Each rope attachment to another ent
// subtracts 200 inches, so the order is biased towards covering
// groups of objects together.
CBaseObject *pObjectA = p->GetTeam()->GetObject( a );
CBaseObject *pObjectB = p->GetTeam()->GetObject( b );
const Vector &vOrigin1 = pObjectA->GetAbsOrigin();
const Vector &vOrigin2 = p->GetTeam()->GetObject( b )->GetAbsOrigin();
float flScore1 = -p->m_pPlayer->GetAbsOrigin().DistTo( vOrigin1 );
float flScore2 = -p->m_pPlayer->GetAbsOrigin().DistTo( vOrigin2 );
flScore1 += pObjectA->RopeCount() * 200;
flScore2 += pObjectB->RopeCount() * 200;
return flScore1 > flScore2;
}
bool IsValidFn_NearAndNotCovered( void *pUserData, int a )
{
CSortBase *p = (CSortBase*)pUserData;
CBaseObject *pObj = p->m_pPlayer->GetTFTeam()->GetObject( a );
// Is the object too far away to be covered?
if ( p->m_pPlayer->GetAbsOrigin().DistTo( pObj->GetAbsOrigin() ) > p->m_flMaxDist )
return false;
// Don't cover certain entities (like sentry guns, sand bags, etc).
switch( p->m_ObjectType )
{
case OBJ_SENTRYGUN_PLASMA:
{
if ( !pObj->WantsCoverFromSentryGun() )
return false;
if ( p->m_pPlayer->GetTFTeam()->IsCoveredBySentryGun( pObj->GetAbsOrigin() ) )
return false;
}
break;
case OBJ_SHIELDWALL:
{
if ( !pObj->WantsCover() )
return false;
if ( p->m_pPlayer->GetTFTeam()->GetNumShieldWallsCoveringPosition( pObj->GetAbsOrigin() ) )
return false;
}
break;
case OBJ_RESUPPLY:
{
if ( p->m_pPlayer->GetTFTeam()->GetNumResuppliesCoveringPosition( pObj->GetAbsOrigin() ) )
return false;
}
break;
case OBJ_RESPAWN_STATION:
{
if ( p->m_pPlayer->GetTFTeam()->GetNumRespawnStationsCoveringPosition( pObj->GetAbsOrigin() ) )
return false;
}
break;
default:
{
Assert( !"Unsupported object type" );
}
break;
}
return true;
}
bool OrderCreator_GenericObject(
CPlayerClass *pClass,
int objectType,
float flMaxDist,
COrder *pOrder
)
{
// Can we build one?
if ( pClass->CanBuild( objectType ) != CB_CAN_BUILD )
return false;
CBaseTFPlayer *pPlayer = pClass->GetPlayer();
CTFTeam *pTeam = pClass->GetTeam();
// Sort nearby objects.
CSortBase info;
info.m_pPlayer = pPlayer;
info.m_flMaxDist = flMaxDist;
info.m_ObjectType = objectType;
int sorted[MAX_TEAM_OBJECTS];
int nSorted = BuildSortedActiveList(
sorted, // the sorted list of objects
MAX_TEAM_OBJECTS,
SortFn_DistanceAndConcentration, // sort on distance and entity concentration
IsValidFn_NearAndNotCovered, // filter function
&info, // user data
pTeam->GetNumObjects() // number of objects to check
);
if( nSorted )
{
// Ok, make an order to cover the closest object with a sentry gun.
CBaseEntity *pEnt = pTeam->GetObject( sorted[0] );
pTeam->AddOrder(
ORDER_BUILD,
pEnt,
pPlayer,
flMaxDist,
60,
pOrder
);
return true;
}
else
{
return false;
}
}

View File

@@ -0,0 +1,128 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_HELPERS_H
#define ORDER_HELPERS_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class CTFTeam;
class CSortBase
{
public:
CSortBase();
// Returns m_pTeam, and if that doesn't exist, returns m_pPlayer->GetTFTeam().
CTFTeam* GetTeam();
public:
CBaseTFPlayer *m_pPlayer;
// If this is left at null, then GetTeam() returns m_pPlayer->GetTFTeam().
CTFTeam *m_pTeam;
// One of the OBJ_ defines telling what type of object it's thinking of building.
int m_ObjectType;
// If the object is further from m_vPlayerOrigin than this, then an order
// won't be generated to cover it.
float m_flMaxDist;
};
// Return positive if iItem1 > iItem2.
// Return negative if iItem1 < iItem2.
// Return zero if they're equal.
typedef int (*sortFn)( void *pUserData, int iItem1, int iItem2 );
typedef bool (*isValidFn)( void *pUserData, int iItem );
// Index engine->PEntityOfIndex(a+1) and b+1.
int SortFn_PlayerEntitiesByDistance( void *pUserData, int a, int b );
// Helper sort function. Sorts CSortBase::m_pPlayer's objects by distance.
// pUserData must be a CSortBase.
int SortFn_PlayerObjectsByDistance( void *pUserData, int a, int b );
// Helper sort function. Sorts CSortBase::m_pPlayer->GetTeam()'s objects by distance.
// pUserData must be a CSortBase.
int SortFn_TeamObjectsByDistance( void *pUserData, int a, int b );
// Sort by distance and concentation. pUserData must point at something
// derived from CSortBase.
int SortFn_DistanceAndConcentration( void *pUserData, int a, int b );
// pUserData is a CSortBase
// a and b index CSortBase::m_pPlayer->GetTeam()->GetPlayer()
// Sort players on distance.
int SortFn_TeamPlayersByDistance( void *pUserData, int a, int b );
// pUserdata is a CSortBase.
//
// Rejects the object if:
// - it's already covered by CSortBase::m_ObjectType
// - it's being ferried
// - it's further from the player than CSortBase::m_flMaxDist;
//
// This function currently supports:
// - OBJ_SENTRYGUN_PLASMA
// - OBJ_SANDBAG
// - OBJ_AUTOREPAIR
// - OBJ_SHIELDWALL
// - OBJ_RESUPPLY
bool IsValidFn_NearAndNotCovered( void *pUserData, int a );
// This is a generic function that takes a number of items and builds a sorted
// list of the valid items.
int BuildSortedActiveList(
int *pList, // This is the list where the final data is placed.
int nMaxItems,
sortFn pSortFn, // Callbacks.
isValidFn pIsValidFn, // This can be null, in which case all items are valid.
void *pUserData, // Passed into the function pointers.
int nItems // Number of items in the list to sort.
);
// Finds the closest resource zone without the specified object on it and
// gives an order to the player to build the object.
// This function supports OBJ_RESOURCEBOX, OBJ_RESOURCEPUMP, and OBJ_ZONE_INCREASER.
bool OrderCreator_ResourceZoneObject(
CBaseTFPlayer *pPlayer,
int objType,
COrder *pOrder
);
// This function is shared by lots of the order creation functions.
// It makes an order to create a specific type of object by looking for nearby
// concentrations of team objects that aren't "covered" by objectType.
//
// It uses IsValidFn_NearAndNotCovered, so any object type you specify in here
// must be supported in IsValidFn_NearAndNotCovered.
bool OrderCreator_GenericObject(
CPlayerClass *pClass,
int objectType,
float flMaxDist,
COrder *pOrder
);
#endif // ORDER_HELPERS_H

View File

@@ -0,0 +1,125 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_helpers.h"
#include "order_killmortarguy.h"
#include "tf_team.h"
#define KILLMORTARGUY_DIST 3500
IMPLEMENT_SERVERCLASS_ST( COrderKillMortarGuy, DT_OrderKillMortarGuy )
END_SEND_TABLE()
static bool IsValidFn_DeployedBrianJacobsons( void *pUserData, int iClient )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)UTIL_PlayerByIndex( iClient+1 );
if ( !pPlayer || pPlayer->IsClass( TFCLASS_UNDECIDED ) || !pPlayer->GetTeam() )
return false;
// Is this person on an enemy team?
CSortBase *pSortBase = (CSortBase*)pUserData;
CBaseTFPlayer *pMyPlayer = pSortBase->m_pPlayer;
if( pPlayer->GetTeam()->GetTeamNumber() == pMyPlayer->GetTeam()->GetTeamNumber() )
return false;
// Is he alive?
if( !pPlayer->IsAlive() )
return false;
// ROBIN: Removed mortar object. This needs to handle mortar vehicles instead.
// Is he looking surly?
//if( pPlayer->GetNumObjects( OBJ_MORTAR ) == 0 )
//return false;
// Is he close enough?
if( pMyPlayer->GetAbsOrigin().DistTo( pPlayer->GetAbsOrigin() ) > KILLMORTARGUY_DIST )
return false;
// Is he visible to the tactical?
// if( !pMyPlayer->GetTFTeam()->IsEntityVisibleToTactical( pPlayer ) )
// return false;
// KILL HIM!!!
return true;
}
static int SortFn_PlayerEntsByDistance( void *pUserData, int a, int b )
{
CBaseEntity *pEdictA = CBaseEntity::Instance( engine->PEntityOfEntIndex( a+1 ) );
CBaseEntity *pEdictB = CBaseEntity::Instance( engine->PEntityOfEntIndex( b+1 ) );
if ( !pEdictA || !pEdictB )
return 1;
CSortBase *pSortBase = (CSortBase*)pUserData;
const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
return v.DistTo( pEdictA->GetAbsOrigin() ) < v.DistTo( pEdictB->GetAbsOrigin() );
}
bool COrderKillMortarGuy::CreateOrder( CPlayerClass *pClass )
{
CSortBase info;
info.m_pPlayer = pClass->GetPlayer();
// Look for an enemy sniper visible to the
int supports[MAX_PLAYERS];
int nSupports = BuildSortedActiveList(
supports, // the sorted list
MAX_PLAYERS,
SortFn_PlayerEntsByDistance, // sort on distance
IsValidFn_DeployedBrianJacobsons, // only get deployed support guys
&info, // pUserData
gpGlobals->maxClients // how many players to look through
);
// Kill the closest punk.
if( nSupports )
{
CBaseTFPlayer *pBrian = (CBaseTFPlayer*)UTIL_PlayerByIndex( supports[0]+1 );
Assert( pBrian );
COrderKillMortarGuy *pOrder = new COrderKillMortarGuy;
pClass->GetTeam()->AddOrder(
ORDER_KILL,
pBrian,
pClass->GetPlayer(),
1e24,
60,
pOrder
);
return true;
}
else
{
return false;
}
}
bool COrderKillMortarGuy::UpdateOnEvent( COrderEvent_Base *pEvent )
{
if ( pEvent->GetType() == ORDER_EVENT_PLAYER_KILLED )
{
COrderEvent_PlayerKilled *pKilled = (COrderEvent_PlayerKilled*)pEvent;
if ( pKilled->m_pPlayer == GetTargetEntity() )
return true;
}
return BaseClass::UpdateOnEvent( pEvent );
}

View File

@@ -0,0 +1,38 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_KILLMORTARGUY_H
#define ORDER_KILLMORTARGUY_H
#ifdef _WIN32
#pragma once
#endif
#include "order_player.h"
class CPlayerClass;
class COrderKillMortarGuy : public COrderPlayer
{
public:
DECLARE_CLASS( COrderKillMortarGuy, COrderPlayer );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool UpdateOnEvent( COrderEvent_Base *pEvent );
};
#endif // ORDER_KILLMORTARGUY_H

View File

@@ -0,0 +1,77 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_mortar_attack.h"
#include "tf_team.h"
#include "order_helpers.h"
#include "tf_obj.h"
// How far away the escort guy will get orders to shell enemy objects.
#define ENEMYOBJ_MORTAR_DIST 3000
IMPLEMENT_SERVERCLASS_ST( COrderMortarAttack, DT_OrderMortarAttack )
END_SEND_TABLE()
static bool IsValidFn_WithinMortarRange( void *pUserData, int a )
{
CSortBase *p = (CSortBase*)pUserData;
CBaseObject *pObj = p->GetTeam()->GetObject( a );
return pObj->GetAbsOrigin().DistTo( p->m_pPlayer->GetAbsOrigin() ) < ENEMYOBJ_MORTAR_DIST;
}
bool COrderMortarAttack::CreateOrder( CPlayerClass *pClass )
{
// Look for some nearby enemy objects that would be fun to destroy.
CTFTeam *pEnemyTeam;
if ( !pClass->GetTeam() || (pEnemyTeam = pClass->GetTeam()->GetEnemyTeam()) == NULL )
return false;
CBaseTFPlayer *pPlayer = pClass->GetPlayer();
CSortBase info;
info.m_pPlayer = pPlayer;
info.m_pTeam = pEnemyTeam;
int sorted[MAX_TEAM_OBJECTS];
int nSorted = BuildSortedActiveList(
sorted, // the sorted list of objects
MAX_TEAM_OBJECTS,
SortFn_DistanceAndConcentration, // sort on distance and entity concentration
IsValidFn_WithinMortarRange, // filter function
&info, // user data
pEnemyTeam->GetNumObjects() // number of objects to check
);
if( nSorted > 0 )
{
CBaseEntity *pEnt = pEnemyTeam->GetObject( sorted[0] );
COrderMortarAttack *pOrder = new COrderMortarAttack;
pClass->GetTeam()->AddOrder(
ORDER_MORTAR_ATTACK,
pEnt,
pPlayer,
1e24,
40,
pOrder
);
return true;
}
else
{
return false;
}
}

View File

@@ -0,0 +1,29 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_BUILDMORTAR_H
#define ORDER_BUILDMORTAR_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class COrderMortarAttack : public COrder
{
public:
DECLARE_CLASS( COrderMortarAttack, COrder );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
};
#endif // ORDER_BUILDMORTAR_H

View File

@@ -0,0 +1,26 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_player.h"
IMPLEMENT_SERVERCLASS_ST( COrderPlayer, DT_OrderPlayer )
END_SEND_TABLE()
bool COrderPlayer::UpdateOnEvent( COrderEvent_Base *pEvent )
{
// All player orders give up if the player disconnects
if ( pEvent->GetType() == ORDER_EVENT_PLAYER_DISCONNECTED )
return true;
return BaseClass::UpdateOnEvent( pEvent );
}

View File

@@ -0,0 +1,32 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_PLAYER_H
#define ORDER_PLAYER_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class COrderPlayer : public COrder
{
public:
DECLARE_CLASS( COrderPlayer, COrder );
DECLARE_SERVERCLASS();
// COrder overrides.
public:
virtual bool UpdateOnEvent( COrderEvent_Base *pEvent );
};
#endif // ORDER_PLAYER_H

View File

@@ -0,0 +1,157 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_repair.h"
#include "tf_team.h"
#include "tf_class_defender.h"
#include "order_helpers.h"
#include "tf_obj.h"
IMPLEMENT_SERVERCLASS_ST( COrderRepair, DT_OrderRepair )
END_SEND_TABLE()
static int SortFn_Defender( void *pUserData, int a, int b )
{
CSortBase *p = (CSortBase*)pUserData;
const Vector &vOrigin1 = p->m_pPlayer->GetTFTeam()->GetObject( a )->GetAbsOrigin();
const Vector &vOrigin2 = p->m_pPlayer->GetTFTeam()->GetObject( b )->GetAbsOrigin();
return p->m_pPlayer->GetAbsOrigin().DistTo( vOrigin1 ) < p->m_pPlayer->GetAbsOrigin().DistTo( vOrigin2 );
}
static bool IsValidFn_RepairFriendlyObjects( void *pUserData, int a )
{
// Only pick objects that are damaged.
CSortBase *p = (CSortBase*)pUserData;
CBaseObject *pObj = p->m_pPlayer->GetTFTeam()->GetObject( a );
// Skip objects under construction
if ( pObj->IsBuilding() )
return false;
return ( pObj->m_iHealth < pObj->m_iMaxHealth );
}
static bool IsValidFn_RepairOwnObjects( void *pUserData, int a )
{
// Only pick objects that are damaged.
CSortBase *pSortBase = (CSortBase*)pUserData;
CBaseObject *pObj = pSortBase->m_pPlayer->GetObject(a);
// Skip objects under construction
if ( !pObj || pObj->IsBuilding() )
return false;
return pObj->m_iHealth < pObj->m_iMaxHealth;
}
bool COrderRepair::CreateOrder_RepairFriendlyObjects( CPlayerClassDefender *pClass )
{
if( !pClass->CanBuildSentryGun() )
return false;
CBaseTFPlayer *pPlayer = pClass->GetPlayer();
CTFTeam *pTeam = pClass->GetTeam();
// Sort the list and filter out fully healed objects..
CSortBase info;
info.m_pPlayer = pPlayer;
int sorted[MAX_TEAM_OBJECTS];
int nSorted = BuildSortedActiveList(
sorted,
MAX_TEAM_OBJECTS,
SortFn_Defender,
IsValidFn_RepairFriendlyObjects,
&info,
pTeam->GetNumObjects() );
// If the player is close enough to the closest damaged object, issue an order.
if( nSorted )
{
CBaseObject *pObjToHeal = pTeam->GetObject( sorted[0] );
static float flClosestDist = 1024;
if( pPlayer->GetAbsOrigin().DistTo( pObjToHeal->GetAbsOrigin() ) < flClosestDist )
{
COrder *pOrder = new COrderRepair;
pTeam->AddOrder(
ORDER_REPAIR,
pObjToHeal,
pPlayer,
1e24,
60,
pOrder
);
return true;
}
}
return false;
}
bool COrderRepair::CreateOrder_RepairOwnObjects( CPlayerClass *pClass )
{
CSortBase info;
info.m_pPlayer = pClass->GetPlayer();
int sorted[16];
int nSorted = BuildSortedActiveList(
sorted,
sizeof( sorted ) / sizeof( sorted[0] ),
SortFn_PlayerObjectsByDistance,
IsValidFn_RepairOwnObjects,
&info,
info.m_pPlayer->GetObjectCount() );
if( nSorted )
{
// Make an order to repair the closest damaged object.
CBaseObject *pObj = info.m_pPlayer->GetObject( sorted[0] );
if (!pObj)
return false;
COrderRepair *pOrder = new COrderRepair;
info.m_pPlayer->GetTFTeam()->AddOrder(
ORDER_REPAIR,
pObj,
info.m_pPlayer,
1e24,
60,
pOrder
);
return true;
}
else
{
return false;
}
}
bool COrderRepair::Update()
{
CBaseEntity *pEnt = GetTargetEntity();
if( !pEnt )
return true;
// Kill the order when the object is repaired.
return pEnt->m_iHealth >= pEnt->m_iMaxHealth;
}

View File

@@ -0,0 +1,42 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_REPAIR_H
#define ORDER_REPAIR_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class CPlayerClass;
class CPlayerClassDefender;
class COrderRepair : public COrder
{
public:
DECLARE_CLASS( COrderRepair, COrder );
DECLARE_SERVERCLASS();
// Create an order for the defender to fix friendly objects.
static bool CreateOrder_RepairFriendlyObjects( CPlayerClassDefender *pClass );
// Create an order for anyone to repair their own objects.
static bool CreateOrder_RepairOwnObjects( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool Update( void );
};
#endif // ORDER_REPAIR_H

View File

@@ -0,0 +1,66 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_resourcepump.h"
#include "tf_team.h"
#include "tf_obj_resourcepump.h"
#include "tf_func_resource.h"
#include "order_helpers.h"
IMPLEMENT_SERVERCLASS_ST( COrderResourcePump, DT_OrderResourcePump )
END_SEND_TABLE()
bool COrderResourcePump::CreateOrder( CPlayerClass *pClass )
{
COrderResourcePump *pOrder = new COrderResourcePump;
if ( OrderCreator_ResourceZoneObject( pClass->GetPlayer(), OBJ_RESOURCEPUMP, pOrder ) )
{
return true;
}
else
{
UTIL_RemoveImmediate( pOrder );
return false;
}
}
bool COrderResourcePump::Update()
{
// Can they still build resource pumps?
if ( !m_hOwningPlayer.Get() || m_hOwningPlayer->CanBuild( OBJ_RESOURCEPUMP ) != CB_CAN_BUILD )
return true;
// Lost our resource zone?
if ( !GetTargetEntity() )
return true;
// Is our target zone now empty?
if ( ((CResourceZone*)GetTargetEntity())->IsEmpty() )
return true;
// Have they built a pump on this zone?
for( int i=0; i < m_hOwningPlayer->GetObjectCount(); i++ )
{
CBaseObject *pObj = m_hOwningPlayer->GetObject(i);
if( pObj && pObj->GetType() == OBJ_RESOURCEPUMP )
{
CObjectResourcePump *pPump = (CObjectResourcePump*)pObj;
CResourceZone *pZone = pPump->GetResourceZone();
if( pZone && pZone->entindex() == m_iTargetEntIndex && !pZone->IsEmpty() )
return true;
}
}
return BaseClass::Update();
}

View File

@@ -0,0 +1,38 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_RESOURCEPUMP_H
#define ORDER_RESOURCEPUMP_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class CPlayerClass;
class COrderResourcePump : public COrder
{
public:
DECLARE_CLASS( COrderResourcePump, COrder );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool Update( void );
};
#endif // ORDER_RESOURCEPUMP_H

View File

@@ -0,0 +1,54 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "order_resupply.h"
#include "tf_team.h"
#include "tf_playerclass.h"
#include "order_helpers.h"
// Orders to build resupplies near objects come in within this range.
#define RESUPPLY_ORDER_MAXDIST 2000
IMPLEMENT_SERVERCLASS_ST( COrderResupply, DT_OrderResupply )
END_SEND_TABLE()
bool COrderResupply::CreateOrder( CPlayerClass *pClass )
{
COrderResupply *pOrder = new COrderResupply;
if ( OrderCreator_GenericObject( pClass, OBJ_RESUPPLY, RESUPPLY_ORDER_MAXDIST, pOrder ) )
{
return true;
}
else
{
UTIL_RemoveImmediate( pOrder );
return false;
}
}
bool COrderResupply::Update()
{
CBaseEntity *pEnt = GetTargetEntity();
if( !pEnt )
return true;
if ( !m_hOwningPlayer.Get() || m_hOwningPlayer->CanBuild( OBJ_RESUPPLY ) != CB_CAN_BUILD )
return true;
CTFTeam *pTeam = m_hOwningPlayer->GetTFTeam();
if ( pTeam->GetNumResuppliesCoveringPosition( pEnt->GetAbsOrigin() ) )
return true;
return BaseClass::Update();
}

View File

@@ -0,0 +1,38 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDER_RESUPPLY_H
#define ORDER_RESUPPLY_H
#ifdef _WIN32
#pragma once
#endif
#include "orders.h"
class CPlayerClass;
class COrderResupply : public COrder
{
public:
DECLARE_CLASS( COrderResupply, COrder );
DECLARE_SERVERCLASS();
// Create an order for the player.
static bool CreateOrder( CPlayerClass *pClass );
// COrder overrides.
public:
virtual bool Update();
};
#endif // ORDER_RESUPPLY_H

215
game/server/tf2/orders.cpp Normal file
View File

@@ -0,0 +1,215 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Order handling
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "orders.h"
#include "tf_player.h"
#include "tf_func_resource.h"
#include "tf_team.h"
#include "tf_obj_resourcepump.h"
IMPLEMENT_SERVERCLASS_ST(COrder, DT_Order)
SendPropInt( SENDINFO(m_iOrderType), 4, SPROP_UNSIGNED ),
SendPropInt( SENDINFO(m_iTargetEntIndex), 16, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( tf_order, COrder );
COrder::COrder()
{
m_iOrderType = 0;
m_iTargetEntIndex = 0;
m_hTarget = NULL;
m_flDistanceToRemove = 0;
m_hOwningPlayer = NULL;
m_flDieTime = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COrder::UpdateOnRemove( void )
{
DetachFromPlayer();
// Chain at end to mimic destructor unwind order
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose: Transmit weapon data
//-----------------------------------------------------------------------------
int COrder::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
CBaseEntity* pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
// If this is a personal order, only send to it's owner
if ( GetOwner() )
{
if ( GetOwner() == pRecipientEntity )
return FL_EDICT_ALWAYS;
return FL_EDICT_DONTSEND;
}
// Otherwise, only send to players on our team
if ( InSameTeam( pRecipientEntity ) )
return FL_EDICT_ALWAYS;
return FL_EDICT_DONTSEND;
}
void COrder::DetachFromPlayer()
{
// Detach from our owner.
if ( m_hOwningPlayer )
{
m_hOwningPlayer->SetOrder( NULL );
m_hOwningPlayer = NULL;
if ( GetTeam() )
{
((CTFTeam*)GetTeam())->RemoveOrder( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int COrder::GetType( void )
{
return m_iOrderType;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseEntity *COrder::GetTargetEntity( void )
{
return m_hTarget;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COrder::SetType( int iOrderType )
{
m_iOrderType = iOrderType;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COrder::SetTarget( CBaseEntity *pTarget )
{
m_hTarget = pTarget;
if ( m_hTarget )
{
m_iTargetEntIndex = m_hTarget->entindex();
}
else
{
m_iTargetEntIndex = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COrder::SetDistance( float flDistance )
{
m_flDistanceToRemove = flDistance;
}
void COrder::SetLifetime( float flLifetime )
{
m_flDieTime = gpGlobals->curtime + flLifetime;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: for updates on the order. Return true if the order should be removed.
//-----------------------------------------------------------------------------
bool COrder::Update( void )
{
// Orders with no targets & no owners don't go away on their own
if ( !GetOwner() )
return false;
// Has it timed out?
if( gpGlobals->curtime > m_flDieTime )
return true;
// Check to make sure we're still within the correct distance
if ( m_flDistanceToRemove )
{
CBaseEntity *pTarget = GetTargetEntity();
if ( pTarget )
{
// Have the player and the target moved away from each other?
if ( (m_hOwningPlayer->GetAbsOrigin() - pTarget->GetAbsOrigin()).Length() > (m_flDistanceToRemove * 1.25) )
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: An event for this order's target has arrived. Return true if this order should be removed.
//-----------------------------------------------------------------------------
bool COrder::UpdateOnEvent( COrderEvent_Base *pEvent )
{
// Default behavior is to get rid of the order if the object we're referencing
// gets destroyed.
if ( pEvent->GetType() == ORDER_EVENT_OBJECT_DESTROYED )
{
COrderEvent_ObjectDestroyed *pObjDestroyed = (COrderEvent_ObjectDestroyed*)pEvent;
if ( pObjDestroyed->m_pObject == GetTargetEntity() )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseTFPlayer *COrder::GetOwner( void )
{
return m_hOwningPlayer;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COrder::SetOwner( CBaseTFPlayer *pPlayer )
{
// Null out our m_hOwningPlayer so we don't recurse infinitely.
CHandle<CBaseTFPlayer> hPlayer = m_hOwningPlayer;
m_hOwningPlayer = 0;
if ( hPlayer.Get() && (hPlayer != pPlayer) )
{
Assert( hPlayer->GetOrder() == this );
hPlayer->SetOrder( NULL );
}
m_hOwningPlayer = pPlayer;
}

90
game/server/tf2/orders.h Normal file
View File

@@ -0,0 +1,90 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Order handling
//
// $NoKeywords: $
//=============================================================================//
#ifndef ORDERS_H
#define ORDERS_H
#ifdef _WIN32
#pragma once
#endif
class CTFTeam;
class CBaseTFPlayer;
#include "order_events.h"
//-----------------------------------------------------------------------------
// Purpose: Datatable container class for orders
//-----------------------------------------------------------------------------
class COrder : public CBaseEntity
{
DECLARE_CLASS( COrder, CBaseEntity );
public:
DECLARE_SERVERCLASS();
COrder();
virtual void UpdateOnRemove( void );
virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_FULLCHECK ); }
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
// This is called when removing the order.
void DetachFromPlayer();
// Overridables.
public:
// Purpose: for updates on the order. Return true if the order should be removed.
virtual bool Update( void );
virtual bool UpdateOnEvent( COrderEvent_Base *pEvent );
public:
CBaseTFPlayer *GetOwner( void );
CBaseEntity *GetTargetEntity( void );
int GetType( void );
void SetOwner( CBaseTFPlayer *pPlayer );
void SetType( int iOrderType );
void SetTarget( CBaseEntity *pTarget );
void SetDistance( float flDistance );
void SetLifetime( float flLifetime );
public:
// Sent via datatable
CNetworkVar( int, m_iOrderType );
float m_flDistanceToRemove;
// When the order goes away.
double m_flDieTime;
// Personal order owner
CHandle< CBaseTFPlayer > m_hOwningPlayer;
EHANDLE m_hTarget;
CNetworkVar( int, m_iTargetEntIndex );
};
//-----------------------------------------------------------------------------
// ORDER CREATION DATA
//-----------------------------------------------------------------------------
// Time between personal order updates
#define PERSONAL_ORDER_UPDATE_TIME 2.0
// KILL orders
#define ORDER_KILL_ENEMY_DISTANCE 2048 // Distance the enemy must be within for this player to receive this order
// HEAL orders
#define ORDER_HEAL_FRIENDLY_DISTANCE 2048 // Distance the friendly must be within this player to receive this order
#endif // ORDERS_H

View File

@@ -0,0 +1,117 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Resource chunks
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ragdoll_shadow.h"
#include "tf_player.h"
#include "sendproxy.h"
// FIXME Hook up a real player standin
static char *sRagdollShadowModel = "models/player/human_commando.mdl";
IMPLEMENT_SERVERCLASS_ST( CRagdollShadow, DT_RagdollShadow )
SendPropInt( SENDINFO( m_nPlayer ), 10, SPROP_UNSIGNED ),
SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[0]" ),
SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[1]" ),
SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[2]" ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( ragdoll_shadow, CRagdollShadow );
PRECACHE_REGISTER( ragdoll_shadow );
CRagdollShadow::CRagdollShadow( void )
{
m_pPlayer = NULL;
m_nPlayer = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollShadow::Spawn( )
{
// Init value & model
if ( m_pPlayer )
{
SetModelName( m_pPlayer->GetModelName() );
}
else
{
SetModelName( AllocPooledString( sRagdollShadowModel ) );
}
BaseClass::Spawn();
// Create the object in the physics system
IPhysicsObject *pPhysics = VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_SOLID, false );
// IPhysicsObject *pPhysics = VPhysicsInitNormal( SOLID_VPHYSICS, 0, false );
// disable physics sounds on this object
pPhysics->SetMaterialIndex( physprops->GetSurfaceIndex("default_silent") );
UTIL_SetSize( this, Vector(-36,-36, 0), Vector(36,36,72) );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : **ppSendTable -
// *recipient -
// *pvs -
// clientArea -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
int CRagdollShadow::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
// Always send to local player
if ( Instance( pInfo->m_pClientEnt ) == GetOwnerEntity() )
return FL_EDICT_ALWAYS;
return BaseClass::ShouldTransmit( pInfo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRagdollShadow::Precache( void )
{
PrecacheModel( sRagdollShadowModel );
}
//-----------------------------------------------------------------------------
// Purpose: Create a resource chunk
//-----------------------------------------------------------------------------
CRagdollShadow *CRagdollShadow::Create( CBaseTFPlayer *player, const Vector& force )
{
CRagdollShadow *pRagdollShadow = (CRagdollShadow*)CreateEntityByName("ragdoll_shadow");
UTIL_SetOrigin( pRagdollShadow, player->GetAbsOrigin() );
pRagdollShadow->m_pPlayer = player;
pRagdollShadow->m_nPlayer = player->entindex();
pRagdollShadow->Spawn();
pRagdollShadow->SetAbsVelocity( force );
pRagdollShadow->SetLocalAngles( vec3_angle );
pRagdollShadow->SetLocalAngularVelocity( RandomAngle( -100, 100 ) );
//pRagdollShadow->AddEffects( EF_NODRAW );
pRagdollShadow->AddEffects( EF_NOSHADOW );
pRagdollShadow->m_lifeState = LIFE_DYING;
IPhysicsObject *pPhysicsObject = pRagdollShadow->VPhysicsGetObject();
if ( pPhysicsObject )
{
AngularImpulse tmp;
QAngleToAngularImpulse( pRagdollShadow->GetLocalAngularVelocity(), tmp );
pPhysicsObject->AddVelocity( &pRagdollShadow->GetAbsVelocity(), &tmp );
}
return pRagdollShadow;
}

View File

@@ -0,0 +1,43 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef RAGDOLL_SHADOW_H
#define RAGDOLL_SHADOW_H
#ifdef _WIN32
#pragma once
#endif
#include "props.h"
class CBaseTFPlayer;
class IPhysicsObject;
//-----------------------------------------------------------------------------
// Purpose: A shadow object used to bound the position of a player ragdoll
//-----------------------------------------------------------------------------
class CRagdollShadow : public CBaseProp
{
DECLARE_CLASS( CRagdollShadow, CBaseProp );
public:
DECLARE_SERVERCLASS();
CRagdollShadow( void ) ;
virtual void Spawn( void );
virtual void Precache( void );
virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_FULLCHECK); }
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
static CRagdollShadow *Create( CBaseTFPlayer *player, const Vector& force );
public:
CBaseTFPlayer *m_pPlayer;
CNetworkVar( int, m_nPlayer );
};
#endif // RAGDOLL_SHADOW_H

View File

@@ -0,0 +1,172 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Resource chunks
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_func_resource.h"
#include "tf_team.h"
#include "tf_basecombatweapon.h"
#include "tf_obj.h"
#include "resource_chunk.h"
#include "vstdlib/random.h"
#include "tf_stats.h"
#include "engine/IEngineSound.h"
ConVar resource_chunk_value( "resource_chunk_value","20", FCVAR_NONE, "Resource value of a single resource chunk." );
ConVar resource_chunk_processed_value( "resource_chunk_processed_value","80", FCVAR_NONE, "Resource value of a single processed resource chunk." );
// Resource Chunk Models
char *sResourceChunkModel = "models/resources/resource_chunk_B.mdl";
char *sProcessedResourceChunkModel = "models/resources/processed_resource_chunk_B.mdl";
BEGIN_DATADESC( CResourceChunk )
// functions
DEFINE_FUNCTION( ChunkTouch ),
DEFINE_FUNCTION( ChunkRemove ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CResourceChunk, DT_ResourceChunk )
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( resource_chunk, CResourceChunk );
PRECACHE_REGISTER( resource_chunk );
//-----------------------------------------------------------------------------
// Purpose: Remove me from any lists I'm in when I'm deleted
//-----------------------------------------------------------------------------
void CResourceChunk::UpdateOnRemove( void )
{
if ( m_hZone )
{
m_hZone->RemoveChunk( this, false );
m_hZone = NULL;
}
// Chain at end to mimic destructor unwind order
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CResourceChunk::Spawn( )
{
// Init model
if ( IsProcessed() )
{
SetModelName( AllocPooledString( sProcessedResourceChunkModel ) );
}
else
{
SetModelName( AllocPooledString( sResourceChunkModel ) );
}
BaseClass::Spawn();
UTIL_SetSize( this, Vector(-4,-4,-4), Vector(4,4,4) );
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_TRIGGER );
CollisionProp()->UseTriggerBounds( true, 24 );
SetCollisionGroup( TFCOLLISION_GROUP_RESOURCE_CHUNK );
SetGravity( 1.0 );
SetFriction( 1 );
SetTouch( ChunkTouch );
SetThink( ChunkRemove );
SetNextThink( gpGlobals->curtime + random->RandomFloat( 50.0, 80.0 ) ); // Remove myself the
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CResourceChunk::Precache( void )
{
PrecacheModel( sResourceChunkModel );
PrecacheModel( sProcessedResourceChunkModel );
PrecacheModel( "sprites/redglow1.vmt" );
PrecacheScriptSound( "ResourceChunk.Pickup" );
}
//-----------------------------------------------------------------------------
// Purpose: Create a resource chunk
//-----------------------------------------------------------------------------
CResourceChunk *CResourceChunk::Create( bool bProcessed, const Vector &vecOrigin, const Vector &vecVelocity )
{
CResourceChunk *pChunk = (CResourceChunk*)CreateEntityByName("resource_chunk");
UTIL_SetOrigin( pChunk, vecOrigin );
pChunk->m_bIsProcessed = bProcessed;
pChunk->m_bBeingCollected = false;
pChunk->Spawn();
pChunk->SetAbsVelocity( vecVelocity );
pChunk->SetLocalAngularVelocity( RandomAngle( -100, 100 ) );
pChunk->SetLocalAngles( vec3_angle );
return pChunk;
}
//-----------------------------------------------------------------------------
// Purpose: If we're picked up by another pla`yer, give resources to that team
//-----------------------------------------------------------------------------
void CResourceChunk::ChunkTouch( CBaseEntity *pOther )
{
if ( pOther->IsPlayer() || pOther->GetServerVehicle() )
{
// Give the team the resources
int iAmountPerPlayer = ((CTFTeam *)pOther->GetTeam())->AddTeamResources( GetResourceValue(), TF_PLAYER_STAT_RESOURCES_ACQUIRED_FROM_CHUNKS );
TFStats()->IncrementTeamStat( pOther->GetTeamNumber(), TF_TEAM_STAT_RESOURCE_CHUNKS_COLLECTED, GetResourceValue() );
pOther->EmitSound( "ResourceChunk.Pickup" );
// Tell the player
CSingleUserRecipientFilter user( (CBasePlayer*)pOther );
UserMessageBegin( user, "PickupRes" );
WRITE_BYTE( iAmountPerPlayer );
MessageEnd();
// Tell our zone to remove this chunk from it's list
if ( m_hZone )
{
m_hZone->RemoveChunk( this, false );
m_hZone = NULL;
}
// Remove this chunk
SetTouch( NULL );
UTIL_Remove( this );
return;
}
}
//-----------------------------------------------------------------------------
// Purpose: Remove myself if I'm not being harvested
//-----------------------------------------------------------------------------
void CResourceChunk::ChunkRemove( void )
{
// Remove this chunk
if ( m_hZone )
{
m_hZone->RemoveChunk( this, true );
m_hZone = NULL;
}
UTIL_Remove( this );
}
//-----------------------------------------------------------------------------
// Purpose: Return the resource value of this chunk
//-----------------------------------------------------------------------------
float CResourceChunk::GetResourceValue( void )
{
// Init value & model
if ( IsProcessed() )
return resource_chunk_processed_value.GetFloat();
return resource_chunk_value.GetFloat();
}

View File

@@ -0,0 +1,52 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef RESOURCE_CHUNK_H
#define RESOURCE_CHUNK_H
#ifdef _WIN32
#pragma once
#endif
#include "props.h"
class CResourceZone;
extern ConVar resource_chunk_value;
extern ConVar resource_chunk_processed_value;
//-----------------------------------------------------------------------------
// Purpose: A resource chunk that's harvestable by a player
//-----------------------------------------------------------------------------
class CResourceChunk : public CBaseProp
{
DECLARE_CLASS( CResourceChunk, CBaseProp );
public:
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
virtual void Spawn( void );
virtual void Precache( void );
virtual void UpdateOnRemove( void );
void ChunkTouch( CBaseEntity *pOther );
void ChunkRemove( void );
virtual bool IsStandable( const CBaseEntity *pStander ) { return true; } // can pStander stand on this entity?
virtual bool IsProcessed( void ) { return m_bIsProcessed; };
float GetResourceValue( void );
static CResourceChunk *Create( bool bProcessed, const Vector &vecOrigin, const Vector &vecVelocity );
public:
CHandle<CResourceZone> m_hZone;
bool m_bIsProcessed;
bool m_bBeingCollected;
};
#endif // RESOURCE_CHUNK_H

View File

@@ -0,0 +1,139 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Definitions of all the entities that control logic flow within a map
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "EntityInput.h"
#include "EntityOutput.h"
#include "tf_team.h"
#include "tf_obj.h"
//-----------------------------------------------------------------------------
// Purpose: Detects a bunch of tf team state
//-----------------------------------------------------------------------------
class CSensorTFTeam : public CLogicalEntity
{
DECLARE_CLASS( CSensorTFTeam, CLogicalEntity );
public:
void Spawn( void );
void Think( void );
private:
DECLARE_DATADESC();
// Computes the number of respawns stations on the sensed team
int ComputeRespawnCount();
// outputs
COutputInt m_OnRespawnCountChanged;
COutputInt m_OnResourceCountChanged;
COutputInt m_OnMemberCountChanged;
COutputInt m_OnRespawnCountChangedDelta;
COutputInt m_OnResourceCountChangedDelta;
COutputInt m_OnMemberCountChangedDelta;
// What team am I sensing?
int m_nTeam;
CTFTeam *m_pTeam;
// So we can know when state changes...
int m_nRespawnCount;
int m_nResourceCount;
int m_nMemberCount;
};
LINK_ENTITY_TO_CLASS( sensor_tf_team, CSensorTFTeam );
BEGIN_DATADESC( CSensorTFTeam )
DEFINE_OUTPUT( m_OnRespawnCountChanged, "OnRespawnCountChanged" ),
DEFINE_OUTPUT( m_OnResourceCountChanged, "OnResourceCountChanged" ),
DEFINE_OUTPUT( m_OnMemberCountChanged, "OnMemberCountChanged" ),
DEFINE_OUTPUT( m_OnRespawnCountChangedDelta, "OnRespawnCountChangedDelta" ),
DEFINE_OUTPUT( m_OnResourceCountChangedDelta, "OnResourceCountChangedDelta" ),
DEFINE_OUTPUT( m_OnMemberCountChangedDelta, "OnMemberCountChangedDelta" ),
DEFINE_KEYFIELD( m_nTeam, FIELD_INTEGER, "team"),
END_DATADESC()
//-----------------------------------------------------------------------------
// Spawn!
//-----------------------------------------------------------------------------
void CSensorTFTeam::Spawn( void )
{
// Hook us up to a team...
m_pTeam = GetGlobalTFTeam( m_nTeam );
// Gets us thinkin!
SetNextThink( gpGlobals->curtime + 0.1f );
// Force an output message on our first think
m_nRespawnCount = -1;
m_nResourceCount = -1;
}
//-----------------------------------------------------------------------------
// Compute the number of respawn stations on this team
//-----------------------------------------------------------------------------
int CSensorTFTeam::ComputeRespawnCount()
{
int nCount = 0;
for (int i = m_pTeam->GetNumObjects(); --i >= 0; )
{
CBaseObject *pObject = m_pTeam->GetObject(i);
if ( pObject && (pObject->GetType() == OBJ_RESPAWN_STATION) )
{
++nCount;
}
}
return nCount;
}
//-----------------------------------------------------------------------------
// Purpose: Forces a recompare
//-----------------------------------------------------------------------------
void CSensorTFTeam::Think( )
{
if (!m_pTeam)
return;
// Check for a difference in the number of respawn stations
int nRespawnCount = ComputeRespawnCount();
if ( nRespawnCount != m_nRespawnCount )
{
m_OnRespawnCountChangedDelta.Set( nRespawnCount - m_nRespawnCount, this, this );
m_nRespawnCount = nRespawnCount;
m_OnRespawnCountChanged.Set( m_nRespawnCount, this, this );
}
// Check for a difference in the number of resources harvested
if ( m_nResourceCount != m_pTeam->m_flTotalResourcesSoFar )
{
m_OnResourceCountChangedDelta.Set( m_pTeam->m_flTotalResourcesSoFar - m_nResourceCount, this, this );
m_nResourceCount = m_pTeam->m_flTotalResourcesSoFar;
m_OnResourceCountChanged.Set( m_nResourceCount, this, this );
}
// Check for a difference in the number of team members
if ( m_nMemberCount != m_pTeam->GetNumPlayers() )
{
m_OnMemberCountChangedDelta.Set( m_pTeam->GetNumPlayers() - m_nMemberCount, this, this );
m_nMemberCount = m_pTeam->GetNumPlayers();
m_OnMemberCountChanged.Set( m_nMemberCount, this, this );
}
SetNextThink( gpGlobals->curtime + 0.1f );
}

View File

@@ -0,0 +1,110 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "player.h"
#include "tf_team.h"
#include "team_messages.h"
#include "engine/IEngineSound.h"
//-----------------------------------------------------------------------------
// Purpose: Create the right class of message based upon the type
//-----------------------------------------------------------------------------
CTeamMessage *CTeamMessage::Create( CTFTeam *pTeam, int iMessageID, CBaseEntity *pEntity )
{
CTeamMessage *pMessage = NULL;
// Create the right type
switch ( iMessageID )
{
// Sound orders
case TEAMMSG_REINFORCEMENTS_ARRIVED:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 5.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.Reinforcements" );
break;
case TEAMMSG_CARRIER_UNDER_ATTACK:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.CarrierAttacked" );
break;
case TEAMMSG_CARRIER_DESTROYED:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.CarrierDestroyed" );
break;
case TEAMMSG_HARVESTER_UNDER_ATTACK:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.HarvesterAttacked" );
break;
case TEAMMSG_HARVESTER_DESTROYED:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.HarvesterDestroyed" );
break;
case TEAMMSG_NEW_TECH_LEVEL_OPEN:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.NewTechLevel" );
break;
case TEAMMSG_RESOURCE_ZONE_EMPTIED:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
((CTeamMessage_Sound*)pMessage)->SetSound( "TeamMessage.ResourceZoneEmpty" );
break;
case TEAMMSG_CUSTOM_SOUND:
pMessage = new CTeamMessage_Sound( pTeam, iMessageID, pEntity, 10.0 );
break;
default:
break;
};
return pMessage;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTeamMessage::CTeamMessage( CTFTeam *pTeam, int iMessageID, CBaseEntity *pEntity, float flTTL )
{
m_pTeam = pTeam;
m_iMessageID = iMessageID;
m_hEntity = pEntity;
m_flTTL = gpGlobals->curtime + flTTL;
}
//===============================================================================================================
// TEAM MESSAGE SOUND
//===============================================================================================================
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTeamMessage_Sound::CTeamMessage_Sound( CTFTeam *pTeam, int iMessageID, CBaseEntity *pEntity, float flTTL ) :
CTeamMessage( pTeam, iMessageID, pEntity, flTTL )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamMessage_Sound::SetSound( char *sSound )
{
CBaseEntity::PrecacheScriptSound( sSound );
m_SoundName = sSound;
}
//-----------------------------------------------------------------------------
// Purpose: Called when the team manager wants me to fire myself
//-----------------------------------------------------------------------------
void CTeamMessage_Sound::FireMessage( void )
{
Assert( m_SoundName.String() );
// Play my sound to all the team's members
for ( int i = 0; i < m_pTeam->GetNumPlayers(); i++ )
{
CBasePlayer *pPlayer = m_pTeam->GetPlayer(i);
CSingleUserRecipientFilter filter( pPlayer );
CBaseEntity::EmitSound( filter, pPlayer->entindex(), m_SoundName.String() );
}
}

View File

@@ -0,0 +1,83 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TEAM_MESSAGES_H
#define TEAM_MESSAGES_H
#ifdef _WIN32
#pragma once
#endif
#include "utlsymbol.h"
// Message IDs
enum
{
// Reinforcements
TEAMMSG_REINFORCEMENTS_ARRIVED,
// Carriers / Harvesters
TEAMMSG_CARRIER_UNDER_ATTACK,
TEAMMSG_CARRIER_DESTROYED,
TEAMMSG_HARVESTER_UNDER_ATTACK,
TEAMMSG_HARVESTER_DESTROYED,
// Resources
TEAMMSG_RESOURCE_ZONE_EMPTIED,
// Techtree
TEAMMSG_NEW_TECH_LEVEL_OPEN,
// Custom sounds
TEAMMSG_CUSTOM_SOUND,
};
//-----------------------------------------------------------------------------
// Purpose: Message sent to a team for the purpose of updating its members about some event
//-----------------------------------------------------------------------------
abstract_class CTeamMessage
{
public:
CTeamMessage( CTFTeam *pTeam, int iMessageID, CBaseEntity *pEntity, float flTTL );
static CTeamMessage *Create( CTFTeam *pTeam, int iMessageID, CBaseEntity *pEntity );
// Called when the team manager wants me to fire myself
virtual void FireMessage( void ) = 0;
// Accessors
virtual int GetID( void ) { return m_iMessageID; };
virtual float GetTTL( void ) { return m_flTTL; };
virtual CBaseEntity *GetEntity( void ) { return m_hEntity; };
virtual CTFTeam *GetTeam( void ) { return m_pTeam; };
virtual void SetData( char *pszData ) { return; }
protected:
int m_iMessageID;
float m_flTTL;
EHANDLE m_hEntity;
CTFTeam *m_pTeam;
CUtlSymbol m_SoundName;
};
//-----------------------------------------------------------------------------
// Purpose: Team message that plays a sound to the members of the team
//-----------------------------------------------------------------------------
class CTeamMessage_Sound : public CTeamMessage
{
public:
CTeamMessage_Sound( CTFTeam *pTeam, int iMessageID, CBaseEntity *pEntity, float flTTL );
// Set my sound
virtual void SetSound( char *sSound );
// Called when the team manager wants me to fire myself
virtual void FireMessage( void );
virtual void SetData( char *pszData ) { SetSound( pszData ); }
};
#endif // TEAM_MESSAGES_H

View File

@@ -0,0 +1,168 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: TF2 Accuracy system
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "player.h"
#include "tf_player.h"
#include "basecombatweapon.h"
#include "vstdlib/random.h"
// THIS ISN'T USED ANYMORE. NO REASON TO MAKE OUR HITSCAN WPNS THIS COMPLEX
// Accuracy is measured as the weapons spread in inches at 1024 units (~85 feet)
// Accuracy is sent to the client, where it's used to generate the size of the accuracy representation.
// Accuracy is a "floating" value, in that it's always moving towards a target accuracy, and takes some time to change
// Accuracy Multipliers
// < 1 increases accuracy, > 1 decreases
#define ACCMULT_DUCKING 0.75 // Player is ducking
#define ACCMULT_RUNNING 1.25 // Player is moving >50% of his max speed
// Ricochet Multiplier
// This works differently to other acc multipliers.
#define ACCMULT_RICOCHET 1.0 // Player is being suppressed by bullet fire
#define ACC_RICOCHET_TIME 1.0 // Amount of time accuracy is affected by a ricochet near the player
#define ACC_RICOCHET_MULTIPLE 0.25 // The effect of ricochets on accuracy is multiplied by this by the number of ricochets nearby
#define ACC_RICOCHET_CAP 10 // Maximum number of bullets to register for suppression fire
#define ACCURACY_CHANGE_SPEED 5
//-----------------------------------------------------------------------------
// Purpose: Calculates the players "accuracy" level
//-----------------------------------------------------------------------------
void CBaseTFPlayer::CalculateAccuracy( void )
{
static flLastTime = 0;
// Get the time since the last calculation
float flTimeSlice = (gpGlobals->curtime - flLastTime);
m_flTargetAccuracy = 0;
if ( !GetPlayerClass() )
return;
// Get the base accuracy from the current weapon
if ( m_hActiveWeapon )
{
m_flTargetAccuracy = m_hActiveWeapon->GetAccuracy();
// Accuracy is increased if the player's crouching
if ( GetFlags() & FL_DUCKING )
m_flTargetAccuracy *= m_hActiveWeapon->GetDuckingMultiplier();
// Accuracy is decreased if the player's moving
if ( m_vecVelocity.Length2D() > ( GetPlayerClass()->GetMaxSpeed() * 0.5 ) )
m_flTargetAccuracy *= m_hActiveWeapon->GetRunningMultiplier();
}
// Accuracy is decreased if the player's arms are injured
// Accuracy is increased if there's an Officer nearby
// Accuracy is decreased if this player's being supressed (bullets/explosions impacting nearby)
float flFarTime = (m_flLastRicochetNearby + ACC_RICOCHET_TIME);
if ( gpGlobals->curtime <= flFarTime )
m_flTargetAccuracy *= 1 + (m_flNumberOfRicochets * ACC_RICOCHET_MULTIPLE) * (ACCMULT_RICOCHET * ((flFarTime - gpGlobals->curtime) / ACC_RICOCHET_TIME));
// Accuracy is decreased if the player's just been hit by a bullet/explosion
// Now float towards the target accuracy
if ( m_bSnapAccuracy )
{
m_bSnapAccuracy = false;
m_flAccuracy = m_flTargetAccuracy;
}
else
{
if ( m_flAccuracy < m_flTargetAccuracy )
{
m_flAccuracy += (flTimeSlice * ACCURACY_CHANGE_SPEED);
if ( m_flAccuracy > m_flTargetAccuracy )
m_flAccuracy = m_flTargetAccuracy ;
}
else if ( m_flAccuracy > m_flTargetAccuracy )
{
m_flAccuracy -= (flTimeSlice * ACCURACY_CHANGE_SPEED);
if ( m_flAccuracy < m_flTargetAccuracy )
m_flAccuracy = m_flTargetAccuracy ;
}
}
// Clip to prevent silly accuracies
if ( m_flAccuracy > 1024 )
m_flAccuracy = 1024;
flLastTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose: Snap the players accuracy immediately
//-----------------------------------------------------------------------------
void CBaseTFPlayer::SnapAccuracy( void )
{
m_bSnapAccuracy = true;
}
//-----------------------------------------------------------------------------
// Purpose: Return the player's current accuracy
//-----------------------------------------------------------------------------
float CBaseTFPlayer::GetAccuracy( void )
{
return m_flAccuracy;
}
//-----------------------------------------------------------------------------
// Purpose: Bullets / Explosions are hitting near the player. Reduce his/her accuracy.
//-----------------------------------------------------------------------------
void CBaseTFPlayer::Supress( void )
{
if ( gpGlobals->curtime <= (m_flLastRicochetNearby + ACC_RICOCHET_TIME) )
{
m_flNumberOfRicochets = MIN( ACC_RICOCHET_CAP, m_flNumberOfRicochets + 1 );
}
else
{
m_flNumberOfRicochets = 1;
}
m_flLastRicochetNearby = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Vector CBaseTFPlayer::GenerateFireVector( Vector *viewVector )
{
// Calculate the weapon spread from the player's accuracy
float flAcc = (GetAccuracy() * 0.5) / ACCURACY_DISTANCE;
float flAccuracyAngle = RAD2DEG( atan( flAcc ) );
// If the user passed in a viewVector, use it, otherwise use player's v_angle
Vector angShootAngles = viewVector ? *viewVector : pl->v_angle;
if ( flAccuracyAngle )
{
float x, y, z;
do {
x = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
y = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
z = x*x+y*y;
} while (z > 1);
angShootAngles.x = UTIL_AngleMod( angShootAngles.x + (x * flAccuracyAngle) );
angShootAngles.y = UTIL_AngleMod( angShootAngles.y + (y * flAccuracyAngle) );
}
Vector forward;
AngleVectors( angShootAngles, &forward );
return forward;
}

View File

@@ -0,0 +1,34 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_AI_HINT_H
#define TF_AI_HINT_H
#ifdef _WIN32
#pragma once
#endif
//=========================================================
// hints - these MUST coincide with the HINTS listed under
// info_node in the FGD file!
//=========================================================
enum TF_Hint_e
{
HINT_RESOURCE_ZONE_AREA = 2000,
// The carrier starts at base_area land spot, goes first to
// the hover spot, then goes to the dropoff hover spot and
// finally to the dropoff landspot
HINT_AIR_CARRIER_DROPOFF_POINT_LANDSPOT,
HINT_AIR_CARRIER_DROPOFF_POINT_HOVERSPOT,
HINT_AIR_CARRIER_BASE_AREA_LANDSPOT,
HINT_AIR_CARRIER_BASE_AREA_HOVERSPOT,
// The ground collector needs hints to drive itself
HINT_GROUNDCOLLECTOR_ZONE_ENTRANCE,
};
#endif // TF_AI_HINT_H

View File

@@ -0,0 +1,137 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base TF Combat weapon
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "animation.h"
#include "tf_player.h"
#include "tf_basecombatweapon.h"
#include "soundent.h"
#include "weapon_twohandedcontainer.h"
#include "tf_gamerules.h"
#include "tf_obj.h"
//====================================================================================================
// BASE TF MACHINEGUN
//====================================================================================================
IMPLEMENT_SERVERCLASS_ST(CTFMachineGun, DT_TFMachineGun )
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMachineGun::PrimaryAttack( void )
{
// Only the player fires this way so we can cast
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if (!pPlayer)
return;
// Abort here to handle burst and auto fire modes
if ( (GetMaxClip1() != -1 && m_iClip1 == 0) || (GetMaxClip1() == -1 && !pPlayer->GetAmmoCount(m_iPrimaryAmmoType) ) )
return;
pPlayer->DoMuzzleFlash();
// To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems,
// especially if the weapon we're firing has a really fast rate of fire.
if ( GetSequence() != SelectWeightedSequence( ACT_VM_PRIMARYATTACK ))
{
m_flNextPrimaryAttack = gpGlobals->curtime;
}
int iBulletsToFire = 0;
float fireRate = GetFireRate();
while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
{
// MUST call sound before removing a round from the clip of a CMachineGun
WeaponSound(SINGLE, m_flNextPrimaryAttack);
m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
iBulletsToFire++;
}
// Make sure we don't fire more than the amount in the clip, if this weapon uses clips
if ( GetMaxClip1() != -1 )
{
if ( iBulletsToFire > m_iClip1 )
iBulletsToFire = m_iClip1;
m_iClip1 -= iBulletsToFire;
}
else
{
if ( iBulletsToFire > pPlayer->GetAmmoCount(m_iPrimaryAmmoType) )
iBulletsToFire = pPlayer->GetAmmoCount(m_iPrimaryAmmoType);
pPlayer->RemoveAmmo( iBulletsToFire, m_iPrimaryAmmoType );
}
// Not time to fire any bullets yet?
if ( !iBulletsToFire )
return;
// Fire the bullets
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
// Factor in the view kick
AddViewKick();
float range = m_pRangeCVar ? m_pRangeCVar->GetFloat() : 1024.0f;
if ( !m_pRangeCVar )
{
Msg( "Weapon missing m_pRangeCVar!!!\n" );
}
FireBullets( this, iBulletsToFire, vecSrc, vecAiming, GetBulletSpread(), range, m_iPrimaryAmmoType, 2 );
if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
{
// HEV suit - indicate out of ammo condition
pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
}
PlayAttackAnimation( GetPrimaryAttackActivity() );
// Register a muzzleflash for the AI
pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
CheckRemoveDisguise();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const Vector& CTFMachineGun::GetBulletSpread( void )
{
static Vector cone = VECTOR_CONE_3DEGREES;
return cone;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFMachineGun::FireBullets( CBaseTFCombatWeapon *pWeapon, int cShots, const Vector &vecSrc, const Vector &vecDirShooting, const Vector &vecSpread, float flDistance, int iBulletType, int iTracerFreq)
{
if ( CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner() )
{
float damage = m_pDamageCVar ? m_pDamageCVar->GetFloat() : 1;
if ( !m_pDamageCVar )
{
Msg( "Weapon missing m_pDamageCVar!!!!\n" );
}
TFGameRules()->FireBullets( CTakeDamageInfo( this, pPlayer, damage, DMG_BULLET ), cShots, vecSrc, vecDirShooting, vecSpread, flDistance, iBulletType, 4, entindex(), 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTFMachineGun::GetFireRate( void )
{
return 1.0;
}

View File

@@ -0,0 +1,39 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: TF's derived BaseCombatWeapon
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_BASECOMBATWEAPON_H
#define TF_BASECOMBATWEAPON_H
#ifdef _WIN32
#pragma once
#endif
#include "baseplayer_shared.h"
#include "basetfplayer_shared.h"
#include "basetfcombatweapon_shared.h"
class CBaseObject;
class CBaseTechnology;
class CBaseTFPlayer;
//-----------------------------------------------------------------------------
// Purpose: Base TF Machinegun
//-----------------------------------------------------------------------------
class CTFMachineGun : public CBaseTFCombatWeapon
{
DECLARE_CLASS( CTFMachineGun, CBaseTFCombatWeapon );
public:
DECLARE_SERVERCLASS();
virtual void PrimaryAttack( void );
virtual void FireBullets( CBaseTFCombatWeapon *pWeapon, int cShots, const Vector &vecSrc, const Vector &vecDirShooting, const Vector &vecSpread, float flDistance, int iBulletType, int iTracerFreq);
virtual const Vector& GetBulletSpread( void );
virtual float GetFireRate( void );
};
#endif // TF_BASECOMBATWEAPON_H

View File

@@ -0,0 +1,762 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A moving vehicle that is used as a battering ram
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_basefourwheelvehicle.h"
#include "engine/IEngineSound.h"
#include "soundenvelope.h"
#include "vcollide_parse.h"
#include "in_buttons.h"
#include "tf_movedata.h"
#define BASEFOURWHEELEDVEHICLE_THINK_CONTEXT "BaseFourWheeledThink"
#define BASEFOURWHEELEDVEHICLE_DEPLOYTHINK_CONTEXT "BaseFourWheeledDeployThink"
// HACK HAC
#define BASEFOURWHEELEDVEHICLE_STOPTHERODEO_CONTEXT "BaseFourWheeledVehicleStopTheRodeoMadnessThink"
ConVar road_feel( "road_feel", "0.1", FCVAR_NOTIFY | FCVAR_REPLICATED );
extern ConVar tf_fastbuild;
BEGIN_DATADESC( CBaseTFFourWheelVehicle )
DEFINE_EMBEDDED( m_VehiclePhysics ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Throttle", InputThrottle ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Steer", InputSteering ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "Action", InputAction ),
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
END_DATADESC()
// Used for debugging to make vehicle deploying go really fast.
ConVar tf_fastdeploy( "tf_fastdeploy", "0", FCVAR_CHEAT );
IMPLEMENT_SERVERCLASS_ST(CBaseTFFourWheelVehicle, DT_BaseTFFourWheelVehicle)
SendPropFloat( SENDINFO( m_flDeployFinishTime ), 0, SPROP_NOSCALE ),
SendPropInt( SENDINFO( m_eDeployMode ), NUM_VEHICLE_DEPLOYMODE_BITS, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_bBoostUpgrade ), 1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_nBoostTimeLeft ), 8, SPROP_UNSIGNED ),
END_SEND_TABLE()
ConVar fourwheelvehicle_hit_damage( "fourwheelvehicle_hit_damage","40", FCVAR_NONE, "Four-wheel vehicle hit damage" );
ConVar fourwheelvehicle_hit_damage_boostmod( "fourwheelvehicle_hit_damage_boostmod", "1.5", FCVAR_NONE, "Four-wheel vehicle boosted hit damage modifier" );
ConVar fourwheelvehicle_hit_mindamagevel( "fourwheelvehicle_hit_mindamagevel","100", FCVAR_NONE, "Four-wheel vehciel hit velocity for min damage" );
ConVar fourwheelvehicle_hit_maxdamagevel( "fourwheelvehicle_hit_maxdamagevel","230", FCVAR_NONE, "Four-wheel vehicle hit velocity for max damage" );
ConVar fourwheelvehicle_impact_time( "fourwheelvehicle_impact_time", "1.0", FCVAR_NONE, "Four-wheel vehicle impact wait time." );
ConVar fourwheelvehicle_hit_damage_player( "fourwheelvehicle_hit_damage_player", "30.0f", FCVAR_NONE, "Four-wheel vehicle hit player damage." );
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
#pragma warning( disable: 4355 )
CBaseTFFourWheelVehicle::CBaseTFFourWheelVehicle() : m_VehiclePhysics(this)
{
m_flDeployFinishTime = -1;
}
#pragma warning( default: 4355 )
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
CBaseTFFourWheelVehicle::~CBaseTFFourWheelVehicle ()
{
}
//-----------------------------------------------------------------------------
// Purpose: Put a vehicle in a deploy state. Turn off the engine and run a
// deploy animation.
//-----------------------------------------------------------------------------
bool CBaseTFFourWheelVehicle::Deploy( void )
{
// Make sure we're allowed to deploy here
if ( !IsReadyToDrive() )
return false;
// Check to see if we are already in a deploy mode.
if ( m_eDeployMode != VEHICLE_MODE_NORMAL )
return false;
// Disable the vehicle's motion.
DisableMotion();
// Save pre-deploy activity.
m_PreDeployActivity = GetActivity();
// Set the deploying activity - ACT_DEPLOY
SetActivity( ACT_DEPLOY );
// Get the deployment time.
float flDeployTime = SequenceDuration();
if ( tf_fastdeploy.GetBool() )
flDeployTime = 1;
m_flDeployFinishTime = gpGlobals->curtime + flDeployTime;
SetContextThink( BaseFourWheeledVehicleDeployThink, gpGlobals->curtime + flDeployTime,
BASEFOURWHEELEDVEHICLE_DEPLOYTHINK_CONTEXT );
// Set the deploy mode.
m_eDeployMode = VEHICLE_MODE_DEPLOYING;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Release a vehicle from a deployed state. Run a de-coupling
// animation and turn on the vehicle.
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::UnDeploy( void )
{
// Make sure we are deployed.
if ( !IsDeployed() )
return;
// Enable motion and turn on the vehicle.
EnableMotion();
// Set the undeploying activity - ACT_UNDEPLOY
SetActivity( ACT_UNDEPLOY );
// Get the undeployment time.
float flUnDeployTime = SequenceDuration();
if ( tf_fastdeploy.GetBool() )
flUnDeployTime = 1;
m_flDeployFinishTime = gpGlobals->curtime + flUnDeployTime;
SetContextThink( BaseFourWheeledVehicleDeployThink, gpGlobals->curtime + flUnDeployTime,
BASEFOURWHEELEDVEHICLE_DEPLOYTHINK_CONTEXT );
// Set the deploy mode.
m_eDeployMode = VEHICLE_MODE_UNDEPLOYING;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::CancelDeploy( void )
{
// Check for the deploying state.
if ( !IsDeploying() )
return;
// Re-enable the motion.
EnableMotion();
// Reset the activity to the previous activity.
SetActivity( m_PreDeployActivity );
// Reset the deploy mode.
m_eDeployMode = VEHICLE_MODE_NORMAL;
m_flDeployFinishTime = -1;
// Turn off the context think.
SetContextThink( NULL, 0, BASEFOURWHEELEDVEHICLE_DEPLOYTHINK_CONTEXT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::BaseFourWheeledVehicleDeployThink( void )
{
// Called from deploy.
if ( IsDeploying() )
{
OnFinishedDeploy();
m_flDeployFinishTime = -1;
}
// Called from undeploy.
else if ( IsUndeploying() )
{
OnFinishedUnDeploy();
m_flDeployFinishTime = -1;
}
// Turn off the context think.
SetContextThink( NULL, 0, BASEFOURWHEELEDVEHICLE_DEPLOYTHINK_CONTEXT );
}
void CBaseTFFourWheelVehicle::BaseFourWheeledVehicleStopTheRodeoMadnessThink( void )
{
// HACK HACK: See note above at FinishBuilding call
// This resets the handbrake, so the newly placed object doesn't roll down any hills.
m_VehiclePhysics.ResetControls();
// Turn off the context think.
SetContextThink( NULL, 0, BASEFOURWHEELEDVEHICLE_STOPTHERODEO_CONTEXT );
// Start our base think
SetContextThink( BaseFourWheeledVehicleThink, gpGlobals->curtime + 0.1, BASEFOURWHEELEDVEHICLE_THINK_CONTEXT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::OnFinishedDeploy( void )
{
SetActivity( ACT_DEPLOY_IDLE );
m_eDeployMode = VEHICLE_MODE_DEPLOYED;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::OnFinishedUnDeploy( void )
{
m_eDeployMode = VEHICLE_MODE_NORMAL;
}
//-----------------------------------------------------------------------------
// Purpose: Allow the vehicle to move.
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::EnableMotion( void )
{
// Enable vehicle chasis motion.
IPhysicsObject *pVehicleObject = VPhysicsGetObject();
if ( pVehicleObject )
{
pVehicleObject->EnableMotion( true );
}
// Enable motion on the tires.
m_VehiclePhysics.EnableMotion();
}
//-----------------------------------------------------------------------------
// Purpose: Dis-allow the vehicle to move.
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::DisableMotion( void )
{
// Disable vehicle chasis motion.
IPhysicsObject *pVehicleObject = VPhysicsGetObject();
if ( pVehicleObject )
{
pVehicleObject->EnableMotion( false );
}
// Disable motion on the tires.
m_VehiclePhysics.DisableMotion();
}
//-----------------------------------------------------------------------------
// Precache
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::Precache()
{
BaseClass::Precache();
PrecacheScriptSound( "BaseTFFourWheelVehicle.EMP" );
PrecacheScriptSound( "BaseTFFourWheelVehicle.RamSound" );
}
//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::Spawn( )
{
SetModel( STRING( GetModelName() ) );
// CFourWheelServerVehicle *pServerVehicle = dynamic_cast<CFourWheelServerVehicle*>(GetServerVehicle());
// m_VehiclePhysics.SetOuter( this, pServerVehicle );
m_VehiclePhysics.Spawn();
BaseClass::Spawn();
// The base class spawn sets a default collision group, so this needs to
// be called post.
SetCollisionGroup( COLLISION_GROUP_VEHICLE );
m_eDeployMode = VEHICLE_MODE_NORMAL;
SetBoostUpgrade( false );
m_flNextHitTime = 0.0f;
}
//-----------------------------------------------------------------------------
// Teleport
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
{
// We basically just have to make sure the wheels are in the right place
// after teleportation occurs
matrix3x4_t startMatrixInv;
MatrixInvert( EntityToWorldTransform(), startMatrixInv );
BaseClass::Teleport( newPosition, newAngles, newVelocity );
// Teleport the vehicle physics from the starting position to the ending one
matrix3x4_t relativeTransform;
ConcatTransforms( EntityToWorldTransform(), startMatrixInv, relativeTransform );
m_VehiclePhysics.Teleport( relativeTransform );
}
//-----------------------------------------------------------------------------
// Debugging methods
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::DrawDebugGeometryOverlays()
{
if (m_debugOverlays & OVERLAY_BBOX_BIT)
{
m_VehiclePhysics.DrawDebugGeometryOverlays();
}
BaseClass::DrawDebugGeometryOverlays();
}
int CBaseTFFourWheelVehicle::DrawDebugTextOverlays()
{
int nOffset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
nOffset = m_VehiclePhysics.DrawDebugTextOverlays( nOffset );
}
return nOffset;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseTFFourWheelVehicle::StartBuilding( CBaseEntity *pPlayer )
{
if (!BaseClass::StartBuilding(pPlayer))
return false;
// Until we're finished building, turn off vphysics-based motion
SetSolid( SOLID_VPHYSICS );
VPhysicsInitStatic();
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::FinishedBuilding( void )
{
BaseClass::FinishedBuilding();
char pScriptName[128];
Q_snprintf( pScriptName, sizeof( pScriptName ), "scripts/vehicles/%s.txt", GetClassname() );
m_VehiclePhysics.Initialize( pScriptName, true );
// HACK HACK: This is a hack to avoid physics spazzing out on a newly created vehicle with the handbrake
// set. We create and activate it, but then release the handbrake for a single Think function call and
// then zero out the controls right then. This seems to stabilize something in the physics simulator.
m_VehiclePhysics.ReleaseHandbrake();
SetContextThink( BaseFourWheeledVehicleStopTheRodeoMadnessThink, gpGlobals->curtime, BASEFOURWHEELEDVEHICLE_STOPTHERODEO_CONTEXT );
ResetDeteriorationTime();
}
//-----------------------------------------------------------------------------
// Input methods
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::InputThrottle( inputdata_t &inputdata )
{
m_VehiclePhysics.SetThrottle( inputdata.value.Float() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::InputSteering( inputdata_t &inputdata )
{
m_VehiclePhysics.SetSteering( inputdata.value.Float(), 2*gpGlobals->frametime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::InputAction( inputdata_t &inputdata )
{
m_VehiclePhysics.SetAction( inputdata.value.Float() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::InputTurnOn( inputdata_t &inputdata )
{
if (!m_VehiclePhysics.IsOn())
{
SetContextThink( BaseFourWheeledVehicleThink, gpGlobals->curtime + 0.1, BASEFOURWHEELEDVEHICLE_THINK_CONTEXT );
m_VehiclePhysics.TurnOn( );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::InputTurnOff( inputdata_t &inputdata )
{
if ( m_VehiclePhysics.IsOn() )
{
m_VehiclePhysics.TurnOff( );
}
}
//-----------------------------------------------------------------------------
// Input methods
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::BaseFourWheeledVehicleThink()
{
if (m_VehiclePhysics.Think())
{
SetNextThink( gpGlobals->curtime, BASEFOURWHEELEDVEHICLE_THINK_CONTEXT );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::VPhysicsUpdate( IPhysicsObject *pPhysics )
{
// must be a wheel
if (!m_VehiclePhysics.VPhysicsUpdate(pPhysics))
return;
BaseClass::VPhysicsUpdate( pPhysics );
}
//-----------------------------------------------------------------------------
// Methods related to getting in and out of the vehicle
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::SetPassenger( int nRole, CBasePlayer *pEnt )
{
if ( nRole == VEHICLE_ROLE_DRIVER )
{
if (pEnt)
{
PlayerControlInit( ToBasePlayer(pEnt) );
}
else
{
PlayerControlShutdown( );
}
}
BaseClass::SetPassenger( nRole, pEnt );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::PlayerControlInit( CBasePlayer *pPlayer )
{
// Blat out the view offset
m_savedViewOffset = pPlayer->GetViewOffset();
pPlayer->SetViewOffset( vec3_origin );
m_playerOn.FireOutput( pPlayer, this, 0 );
InputTurnOn( inputdata_t() );
// Release the handbrake.
if ( !IsDeployed() )
{
m_VehiclePhysics.ReleaseHandbrake();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::ResetUseKey( CBasePlayer *pPlayer )
{
pPlayer->m_afButtonPressed &= ~IN_USE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::PlayerControlShutdown()
{
CBasePlayer *pPlayer = GetDriverPlayer();
if ( !pPlayer )
return;
ResetUseKey( pPlayer );
pPlayer->SetViewOffset( m_savedViewOffset );
m_playerOff.FireOutput( pPlayer, this, 0 );
// clear out the fire buttons
m_attackaxis.Set( 0, pPlayer, this );
m_attack2axis.Set( 0, pPlayer, this );
InputTurnOff( inputdata_t() );
}
//-----------------------------------------------------------------------------
// Purpose: Powerup has just started
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::PowerupStart( int iPowerup, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier )
{
switch( iPowerup )
{
case POWERUP_EMP:
m_VehiclePhysics.SetMaxThrottle( 0.1 );
break;
default:
break;
}
BaseClass::PowerupStart( iPowerup, flAmount, pAttacker, pDamageModifier );
}
//-----------------------------------------------------------------------------
// Purpose: Powerup has just started
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::PowerupEnd( int iPowerup )
{
switch ( iPowerup )
{
case POWERUP_EMP:
m_VehiclePhysics.SetMaxThrottle( 1.0 );
break;
default:
break;
}
BaseClass::PowerupEnd( iPowerup );
}
//-----------------------------------------------------------------------------
// Methods related to actually driving the vehicle
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::DriveVehicle( CBasePlayer *pPlayer, CUserCmd *ucmd )
{
// Lose control when the player dies
if ( pPlayer->IsAlive() == false )
return;
// Only the driver gets to drive.
int nRole = GetPassengerRole( pPlayer );
if ( nRole != VEHICLE_ROLE_DRIVER )
return;
// No driving in the mothership, kids
if ( !tf_fastbuild.GetInt() && !IsReadyToDrive() )
{
m_VehiclePhysics.SetHandbrake( true );
m_VehiclePhysics.SetThrottle( 0 );
m_VehiclePhysics.SetSteering( 0, 0 );
m_attackaxis.Set( 0, pPlayer, this );
m_attack2axis.Set( 0, pPlayer, this );
return;
}
// Update the boost time.
m_nBoostTimeLeft = m_VehiclePhysics.BoostTimeLeft();
// If the vehicle's emped, it can't drive
if ( HasPowerup( POWERUP_EMP ) )
{
// Play sounds if they're trying to drive
if ( ucmd->buttons & (IN_MOVELEFT | IN_MOVERIGHT | IN_FORWARD | IN_BACK) )
{
if ( m_flNextEmpSound < gpGlobals->curtime )
{
EmitSound( "BaseTFFourWheelVehicle.EMP" );
m_flNextEmpSound = gpGlobals->curtime + 2.0;
}
}
}
ResetDeteriorationTime();
m_VehiclePhysics.UpdateDriverControls( ucmd, TICK_INTERVAL );
float attack = 0, attack2 = 0;
if ( pPlayer->m_afButtonPressed & IN_ATTACK )
{
m_pressedAttack.FireOutput( pPlayer, this, 0 );
}
if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
{
m_pressedAttack2.FireOutput( pPlayer, this, 0 );
}
if ( ucmd->buttons & IN_ATTACK )
{
attack = 1;
}
if ( ucmd->buttons & IN_ATTACK2 )
{
attack2 = 1;
}
m_attackaxis.Set( attack, pPlayer, this );
m_attack2axis.Set( attack2, pPlayer, this );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::SetupMove( CBasePlayer *pPlayer, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
{
BaseClass::SetupMove( pPlayer, ucmd, pHelper, move );
if ( IsDeployed() )
return;
DriveVehicle( pPlayer, ucmd );
m_nMovementRole = GetPassengerRole( pPlayer );
Assert( m_nMovementRole >= 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::SetBoostUpgrade( bool bBoostUpgrade )
{
m_bBoostUpgrade = bBoostUpgrade;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseTFFourWheelVehicle::IsBoostable( void )
{
return m_bBoostUpgrade;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::StartBoost( void )
{
if ( IsBoostable() )
{
m_VehiclePhysics.SetBoost( 1.0f );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseTFFourWheelVehicle::IsBoosting( void )
{
return m_VehiclePhysics.IsBoosting();
}
//-----------------------------------------------------------------------------
// Purpose: Vehicle damage!
//-----------------------------------------------------------------------------
void CBaseTFFourWheelVehicle::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
{
BaseClass::VPhysicsCollision( index, pEvent );
int otherIndex = !index;
CBaseEntity *pEntity = pEvent->pEntities[otherIndex];
// We only damage objects...
// And only if we're travelling fast enough...
Assert( pEntity );
if ( !pEntity->IsSolid() )
return;
// Ignore shields..
if ( pEntity->GetCollisionGroup() == TFCOLLISION_GROUP_SHIELD )
return;
// Ignore anything that's not an object
if ( pEntity->Classify() != CLASS_MILITARY && !pEntity->IsPlayer() )
return;
// Ignore teammates
if ( InSameTeam( pEntity ))
return;
// Ignore invulnerable stuff
if ( pEntity->m_takedamage == DAMAGE_NO )
return;
// See if we can damage again? (Time-based)
if ( m_flNextHitTime > gpGlobals->curtime )
return;
// Do damage based on velocity.
Vector vecVelocity = pEvent->preVelocity[index];
CTakeDamageInfo info;
info.SetInflictor( this );
info.SetAttacker( GetDriverPlayer() );
info.SetDamageType( DMG_CLUB );
float flMaxDamage = fourwheelvehicle_hit_damage.GetFloat();
float flMaxDamageVel = fourwheelvehicle_hit_maxdamagevel.GetFloat();
float flMinDamageVel = fourwheelvehicle_hit_mindamagevel.GetFloat();
float flVel = vecVelocity.Length();
if ( flVel < flMinDamageVel )
return;
EmitSound( "BaseTFFourWheelVehicle.RamSound" );
float flDamageFactor = flMaxDamage;
// Special damage for players.
if ( pEntity->IsPlayer() )
{
flDamageFactor = fourwheelvehicle_hit_damage_player.GetFloat();
if ( IsBoosting() )
{
flDamageFactor *= 4.0f;
}
// Knock the player up into the air
float flForceScale = (flVel*0.5) * 75 * 4;
Vector vecForce = vecVelocity;
VectorNormalize( vecForce );
vecForce.z += 0.7;
vecForce *= flForceScale;
info.SetDamageForce( vecForce );
}
// Damage to objects.
else
{
if ( IsBoosting() )
{
flDamageFactor *= fourwheelvehicle_hit_damage_boostmod.GetFloat();
}
if ( ( flMaxDamageVel > flMinDamageVel ) && ( flVel < flMaxDamageVel ) )
{
// Use less damage when we're not moving fast enough
float flVelocityFactor = ( flVel - flMinDamageVel ) / ( flMaxDamageVel - flMinDamageVel );
flVelocityFactor *= flVelocityFactor;
flDamageFactor *= flVelocityFactor;
}
}
info.SetDamage( flDamageFactor );
Vector damagePos;
pEvent->pInternalData->GetContactPoint( damagePos );
Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
info.SetDamageForce( damageForce );
info.SetDamagePosition( damagePos );
PhysCallbackDamage( pEntity, info, *pEvent, index );
// Set next time hit time
m_flNextHitTime = gpGlobals->curtime + fourwheelvehicle_impact_time.GetFloat();
}

View File

@@ -0,0 +1,138 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A base class that deals with four-wheel vehicles
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_BASE_FOUR_WHEEL_VEHICLE_H
#define TF_BASE_FOUR_WHEEL_VEHICLE_H
#ifdef _WIN32
#pragma once
#endif
#include "basetfvehicle.h"
#include "vphysics/vehicles.h"
#include "fourwheelvehiclephysics.h"
#include "tf_vehicleshared.h"
class CMoveData;
class CBaseTFFourWheelVehicle : public CBaseTFVehicle
{
public:
DECLARE_CLASS( CBaseTFFourWheelVehicle, CBaseTFVehicle );
DECLARE_SERVERCLASS();
public:
CBaseTFFourWheelVehicle();
~CBaseTFFourWheelVehicle ();
// CBaseEntity
void Spawn();
void Precache();
void VPhysicsUpdate( IPhysicsObject *pPhysics );
void DrawDebugGeometryOverlays();
int DrawDebugTextOverlays();
void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );
void BaseFourWheeledVehicleThink();
void BaseFourWheeledVehicleDeployThink( void );
// HACK HACK: This is a hack to avoid physics spazzing out on a newly created vehicle with the handbrake
// set. We create and activate it, but then release the handbrake for a single Think function call and
// then zero out the controls right then. This seems to stabilize something in the physics simulator.
void BaseFourWheeledVehicleStopTheRodeoMadnessThink( void );
virtual bool StartBuilding( CBaseEntity *pPlayer );
virtual void FinishedBuilding( void );
virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData );
virtual void SetPassenger( int nRole, CBasePlayer *pEnt );
// Powerup handling
virtual void PowerupStart( int iPowerup, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier );
virtual void PowerupEnd( int iPowerup );
// Collision
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
// Inputs
void InputThrottle( inputdata_t &inputdata );
void InputSteering( inputdata_t &inputdata );
void InputAction( inputdata_t &inputdata );
void InputTurnOn( inputdata_t &inputdata );
void InputTurnOff( inputdata_t &inputdata );
// Boost
void SetBoostUpgrade( bool bBoostUpgrade );
bool IsBoostable( void );
bool IsBoosting( void );
void StartBoost( void );
bool IsDeployed( void ) { return ( m_eDeployMode == VEHICLE_MODE_DEPLOYED ); }
bool IsDeploying( void ) { return ( m_eDeployMode == VEHICLE_MODE_DEPLOYING ); }
bool IsUndeploying( void ) { return ( m_eDeployMode == VEHICLE_MODE_UNDEPLOYING ); }
bool InDeployMode( void ) { return ( m_eDeployMode != VEHICLE_MODE_NORMAL ); }
DECLARE_DATADESC();
// locals
protected:
// engine sounds
void SoundInit();
void SoundShutdown();
void SoundUpdate( const vehicle_operatingparams_t &params, const vehicleparams_t &vehicle );
void CalcWheelData( vehicleparams_t &vehicle );
void ResetControls();
// Deploy
bool Deploy( void );
void UnDeploy( void );
void CancelDeploy( void );
virtual void OnFinishedDeploy( void );
virtual void OnFinishedUnDeploy( void );
private:
void DriveVehicle( CBasePlayer *pPlayer, CUserCmd *ucmd );
void PlayerControlInit( CBasePlayer *pPlayer );
void PlayerControlShutdown();
void ResetUseKey( CBasePlayer *pPlayer );
void InitializePoseParameters();
bool ParseVehicleScript( solid_t &solid, vehicleparams_t &vehicle );
void EnableMotion( void );
void DisableMotion( void );
private:
CFourWheelVehiclePhysics m_VehiclePhysics;
COutputEvent m_playerOn;
COutputEvent m_playerOff;
COutputEvent m_pressedAttack;
COutputEvent m_pressedAttack2;
COutputFloat m_attackaxis;
COutputFloat m_attack2axis;
int m_nMovementRole;
Vector m_savedViewOffset; //[MAX_PASSENGERS];
float m_flNextEmpSound;
// Deploy
CNetworkVar( VehicleModeDeploy_e, m_eDeployMode );
Activity m_PreDeployActivity;
// Used for vgui screens on the client.
CNetworkVar( float, m_flDeployFinishTime );
CNetworkVar( bool, m_bBoostUpgrade );
CNetworkVar( int, m_nBoostTimeLeft );
float m_flNextHitTime;
};
#endif // TF_BASE_FOUR_WHEEL_VEHICLE_H

View File

@@ -0,0 +1 @@
//========= Copyright Valve Corporation, All rights reserved. ============//

View File

@@ -0,0 +1 @@
//========= Copyright Valve Corporation, All rights reserved. ============//

View File

@@ -0,0 +1,586 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Commando Player Class
//
// $Workfile: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_class_commando.h"
#include "tf_vehicle_teleport_station.h"
#include "EntityList.h"
#include "basecombatweapon.h"
#include "weapon_builder.h"
#include "tf_obj.h"
#include "tf_obj_rallyflag.h"
#include "tf_team.h"
#include "order_assist.h"
#include "engine/IEngineSound.h"
#include "weapon_twohandedcontainer.h"
#include "weapon_combatshield.h"
ConVar tf_knockdowntime( "tf_knockdowntime", "3", FCVAR_NONE, "Length of time knocked-down players remain on the ground." );
// Adrenalin
ConVar class_commando_speed( "class_commando_speed","200", FCVAR_NONE, "Commando movement speed." );
ConVar class_commando_rush_length( "class_commando_rush_length","10", FCVAR_NONE, "Commando's adrenalin rush length in seconds." );
ConVar class_commando_rush_recharge( "class_commando_rush_recharge","60", FCVAR_NONE, "Commando's adrenalin rush recharge time in seconds." );
ConVar class_commando_battlecry_radius( "class_commando_battlecry_radius","512", FCVAR_NONE, "Commando's battlecry radius." );
ConVar class_commando_battlecry_length( "class_commando_battlecry_length","10", FCVAR_NONE, "Length of adrenalin rush given by the Commando's battlecry in seconds." );
//=============================================================================
//
// Commando Data Table
//
BEGIN_SEND_TABLE_NOBASE( CPlayerClassCommando, DT_PlayerClassCommandoData )
SendPropInt ( SENDINFO_STRUCTELEM( m_ClassData.m_bCanBullRush ), 1, SPROP_UNSIGNED ),
SendPropInt ( SENDINFO_STRUCTELEM( m_ClassData.m_bBullRush ), 1, SPROP_UNSIGNED ),
SendPropVector ( SENDINFO_STRUCTELEM( m_ClassData.m_vecBullRushDir ), -1, SPROP_COORD ),
SendPropVector ( SENDINFO_STRUCTELEM( m_ClassData.m_vecBullRushViewDir ), -1, SPROP_COORD ),
SendPropVector ( SENDINFO_STRUCTELEM( m_ClassData.m_vecBullRushViewGoalDir ), -1, SPROP_COORD ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flBullRushTime ), 32, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flDoubleTapForwardTime ), 32, SPROP_NOSCALE ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassCommando::GetClassModelString( int nTeam )
{
if (nTeam == TEAM_HUMANS)
return "models/player/human_commando.mdl";
else
return "models/player/alien_commando.mdl";
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassCommando::CPlayerClassCommando( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassCommando::~CPlayerClassCommando()
{
m_aHitPlayers.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::ClassActivate( void )
{
BaseClass::ClassActivate();
// Setup movement data.
SetupMoveData();
// Initialize the shared class data.
m_ClassData.m_bCanBullRush = false;
m_ClassData.m_bBullRush = false;
m_ClassData.m_vecBullRushDir.Init();
m_ClassData.m_vecBullRushViewDir.Init();
m_ClassData.m_vecBullRushViewGoalDir.Init();
m_ClassData.m_flBullRushTime = COMMANDO_TIME_INVALID;
m_ClassData.m_flDoubleTapForwardTime = COMMANDO_TIME_INVALID;
m_bCanRush = false;
m_bPersonalRush = false;
m_bHasBattlecry = false;
m_bCanBoot = false;
m_flNextBootCheck = 0.0f; // Time at which to recheck for the automatic melee attack
m_bOldBullRush = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::ClassDeactivate( void )
{
m_hWpnShield = NULL;
m_hWpnPlasma = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::CreateClass( void )
{
BaseClass::CreateClass();
// Create our two handed weapon layout
m_hWpnShield = m_pPlayer->GetCombatShield();
CWeaponTwoHandedContainer *p = ( CWeaponTwoHandedContainer * )m_pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
if ( !p )
{
p = static_cast< CWeaponTwoHandedContainer * >( m_pPlayer->GiveNamedItem( "weapon_twohandedcontainer" ) );
}
if ( p && m_hWpnShield.Get() )
{
m_hWpnShield->SetReflectViewModelAnimations( true );
p->SetWeapons( NULL, m_hWpnShield );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::RespawnClass( void )
{
BaseClass::RespawnClass();
m_flNextBootCheck = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Supply the player with Ammo. Return true if some ammo was given.
//-----------------------------------------------------------------------------
bool CPlayerClassCommando::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_GRENADES_FROM_STATION))
{
if (ResupplyAmmoType( 3 * flFraction, "Grenades" ))
bGiven = true;
if (ResupplyAmmoType( 1, "RallyFlags" ))
bGiven = true;
if (ResupplyAmmoType( 3, "Rockets" ))
bGiven = true;
}
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
if (ResupplyAmmoType( 3, "Rockets" ))
bGiven = true;
}
// On respawn, resupply base weapon ammo
if ( reason == RESUPPLY_RESPAWN )
{
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set commando class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassCommando::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_commando_speed.GetFloat();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( COMMANDOCLASS_HULL_STAND_MIN, COMMANDOCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( COMMANDOCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = COMMANDOCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this player's allowed to build another one of the specified objects
//-----------------------------------------------------------------------------
int CPlayerClassCommando::CanBuild( int iObjectType )
{
if ( iObjectType == OBJ_RALLYFLAG )
{
if ( !m_pPlayer->HasNamedTechnology( "com_obj_rallyflag" ) )
return CB_NOT_RESEARCHED;
}
return BaseClass::CanBuild( iObjectType );
}
//-----------------------------------------------------------------------------
// Purpose: Object has been built by this player
//-----------------------------------------------------------------------------
void CPlayerClassCommando::FinishedObject( CBaseObject *pObject )
{
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the Commando's Adrenalin Rush from available technologies
//-----------------------------------------------------------------------------
void CPlayerClassCommando::CalculateRush( void )
{
// Adrenalin Rush
if ( m_pPlayer->HasNamedTechnology( "com_adrenalin_rush" ) )
{
m_bCanRush = true;
}
else
{
m_bCanRush = false;
}
// Battlecry
m_bHasBattlecry = m_pPlayer->HasNamedTechnology( "com_adrenalin_battlecry" );
// Boot
// ROBIN: Removed for now
m_bCanBoot = false;//m_pPlayer->HasNamedTechnology( "com_automatic_boot" );
// Killing Rush
m_pPlayer->SetRampage( m_pPlayer->HasNamedTechnology( "com_adrenalin_rampage" ) );
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the player is bullrushing.
//-----------------------------------------------------------------------------
bool CPlayerClassCommando::InBullRush( void )
{
return m_ClassData.m_bBullRush;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the player's able to bull rush
//-----------------------------------------------------------------------------
bool CPlayerClassCommando::CanBullRush( void )
{
return m_ClassData.m_bCanBullRush;
}
//-----------------------------------------------------------------------------
// Should we take damage-based force?
//-----------------------------------------------------------------------------
bool CPlayerClassCommando::ShouldApplyDamageForce( const CTakeDamageInfo &info )
{
return !InBullRush();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::BullRushTouch( CBaseEntity *pTouched )
{
if ( pTouched->IsPlayer() && !pTouched->InSameTeam( m_pPlayer ) )
{
// Get the player.
CBaseTFPlayer *pTFPlayer = ( CBaseTFPlayer* )pTouched;
// Check to see if we have "touched" this player already this bullrush cycle.
if ( m_aHitPlayers.Find( pTFPlayer ) != -1 )
return;
// Hitting the player now.
m_aHitPlayers.AddToTail( pTFPlayer );
// ROBIN: Bullrush now instantly kills again
float flDamage = 200;
// Calculate the damage a player takes based on distance(time).
//float flDamage = 1.0f - ( ( COMMANDO_BULLRUSH_TIME - m_ClassData.m_flBullRushTime ) * ( 1.0f / COMMANDO_BULLRUSH_TIME ) );
//flDamage *= 115.0f; // max bullrush damage
const trace_t &tr = m_pPlayer->GetTouchTrace();
CTakeDamageInfo info( m_pPlayer, m_pPlayer, flDamage, DMG_CLUB, DMG_KILL_BULLRUSH );
CalculateMeleeDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos );
pTFPlayer->TakeDamage( info );
CPASAttenuationFilter filter( m_pPlayer, "Commando.BullRushFlesh" );
CBaseEntity::EmitSound( filter, m_pPlayer->entindex(), "Commando.BullRushFlesh" );
pTFPlayer->Touch( m_pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose: New technology has been gained. Recalculate any class specific technology dependencies.
//-----------------------------------------------------------------------------
void CPlayerClassCommando::GainedNewTechnology( CBaseTechnology *pTechnology )
{
// Technology handling
CalculateRush();
BaseClass::GainedNewTechnology( pTechnology );
}
//-----------------------------------------------------------------------------
// Purpose: Called when we are about to bullrush.
//-----------------------------------------------------------------------------
void CPlayerClassCommando::PreBullRush( void )
{
// Set the touch function to look for collisions!
SetClassTouch( m_pPlayer, BullRushTouch );
// Clear the player hit list.
m_aHitPlayers.RemoveAll();
// Start the bull rush sound.
CPASAttenuationFilter filter( m_pPlayer, "Commando.BullRushScream" );
filter.MakeReliable();
CBaseEntity::EmitSound( filter, m_pPlayer->entindex(), "Commando.BullRushScream" );
// Force the shield down, if it is up.
CWeaponTwoHandedContainer *pContainer = dynamic_cast<CWeaponTwoHandedContainer*>( m_pPlayer->GetActiveWeapon() );
if ( pContainer )
{
CWeaponCombatShield *pShield = dynamic_cast<CWeaponCombatShield*>( pContainer->GetRightWeapon() );
if ( pShield )
{
pShield->SetShieldUsable( false );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Called when we finish bullrushing.
//-----------------------------------------------------------------------------
void CPlayerClassCommando::PostBullRush( void )
{
SetClassTouch( m_pPlayer, NULL );
// Force the shield down, if it is up.
CWeaponTwoHandedContainer *pContainer = dynamic_cast<CWeaponTwoHandedContainer*>( m_pPlayer->GetActiveWeapon() );
if ( pContainer )
{
CWeaponCombatShield *pShield = dynamic_cast<CWeaponCombatShield*>( pContainer->GetRightWeapon() );
if ( pShield )
{
pShield->SetShieldUsable( true );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Called every frame by postthink
//-----------------------------------------------------------------------------
void CPlayerClassCommando::ClassThink( void )
{
// Check bullrush
m_ClassData.m_bCanBullRush = true;
// Do the init thing here!
if ( m_bOldBullRush != m_ClassData.m_bBullRush )
{
if ( m_ClassData.m_bBullRush )
{
PreBullRush();
}
else
{
PostBullRush();
}
m_bOldBullRush = (bool)m_ClassData.m_bBullRush;
}
// Check for melee attack
if ( m_bCanBoot && m_pPlayer->IsAlive() && m_flNextBootCheck < gpGlobals->curtime )
{
m_flNextBootCheck = gpGlobals->curtime + 0.2;
CBaseEntity *pEntity = NULL;
Vector vecSrc = m_pPlayer->Weapon_ShootPosition( );
Vector vecDir = m_pPlayer->BodyDirection2D( );
Vector vecTarget = vecSrc + (vecDir * 48);
for ( CEntitySphereQuery sphere( vecTarget, 16 ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
{
if ( pEntity->IsPlayer() && (pEntity != m_pPlayer) )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEntity;
// Target needs to be on the enemy team
if ( !pPlayer->IsClass( TFCLASS_UNDECIDED ) && pPlayer->IsAlive() && pPlayer->InSameTeam( m_pPlayer ) == false )
{
Boot( pPlayer );
m_flNextBootCheck = gpGlobals->curtime + 1.5;
}
}
}
}
BaseClass::ClassThink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::StartAdrenalinRush( void )
{
// Am I actually alive?
if ( !m_pPlayer->IsAlive() )
return;
// Do I have rush capability?
if ( !m_bCanRush )
return;
m_bPersonalRush = true;
// Start adrenalin rushing
m_pPlayer->AttemptToPowerup( POWERUP_RUSH, class_commando_rush_length.GetFloat() );
// If I have battlecry, adrenalin up all my nearby teammates
if ( m_bHasBattlecry )
{
// Find nearby teammates
for ( int i = 0; i < m_pPlayer->GetTFTeam()->GetNumPlayers(); i++ )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_pPlayer->GetTFTeam()->GetPlayer(i);
assert(pPlayer);
// Is it within range?
if ( pPlayer != m_pPlayer && (pPlayer->GetAbsOrigin() - m_pPlayer->GetAbsOrigin()).Length() < class_commando_battlecry_radius.GetFloat() )
{
// Can I see it?
trace_t tr;
UTIL_TraceLine( m_pPlayer->EyePosition(), pPlayer->EyePosition(), MASK_SOLID_BRUSHONLY, m_pPlayer, COLLISION_GROUP_NONE, &tr);
CBaseEntity *pEntity = tr.m_pEnt;
if ( (tr.fraction == 1.0) || ( pEntity == pPlayer ) )
{
pPlayer->AttemptToPowerup( POWERUP_RUSH, class_commando_battlecry_length.GetFloat() );
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Automatic Melee Attack
//-----------------------------------------------------------------------------
void CPlayerClassCommando::Boot( CBaseTFPlayer *pTarget )
{
CPASAttenuationFilter filter( m_pPlayer, "Commando.BootSwing" );
CBaseEntity::EmitSound( filter, m_pPlayer->entindex(), "Commando.BootSwing" );
CBaseEntity::EmitSound( filter, m_pPlayer->entindex(), "Commando.BootHit" );
// Damage the target
CTakeDamageInfo info( m_pPlayer, m_pPlayer, 25, DMG_CLUB );
CalculateMeleeDamageForce( &info, (pTarget->GetAbsOrigin() - m_pPlayer->GetAbsOrigin()), pTarget->GetAbsOrigin() );
pTarget->TakeDamage( info );
Vector vecForward;
AngleVectors( m_pPlayer->GetLocalAngles(), &vecForward );
// Give it a lot of "in the air"
vecForward.z = MAX( 0.8, vecForward.z );
VectorNormalize( vecForward );
// Knock the target to the ground for a few seconds (use default duration)
pTarget->KnockDownPlayer( vecForward, 500.0f, tf_knockdowntime.GetFloat() );
}
//-----------------------------------------------------------------------------
// Purpose: Handle custom commands for this playerclass
//-----------------------------------------------------------------------------
bool CPlayerClassCommando::ClientCommand( const char *pcmd )
{
return BaseClass::ClientCommand( pcmd );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( COMMANDOCLASS_HULL_DUCK_MIN, COMMANDOCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( COMMANDOCLASS_HULL_STAND_MIN, COMMANDOCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax )
{
if ( bDucking )
{
VectorCopy( COMMANDOCLASS_HULL_DUCK_MIN, vecMin );
VectorCopy( COMMANDOCLASS_HULL_DUCK_MAX, vecMax );
}
else
{
VectorCopy( COMMANDOCLASS_HULL_STAND_MIN, vecMin );
VectorCopy( COMMANDOCLASS_HULL_STAND_MAX, vecMax );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::CreatePersonalOrder( void )
{
if ( CreateInitialOrder() )
return;
if ( COrderAssist::CreateOrder( this ) )
return;
BaseClass::CreatePersonalOrder();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( COMMANDOCLASS_VIEWOFFSET_STAND );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassCommando::InitVCollision( void )
{
CPhysCollide *pStandModel = PhysCreateBbox( COMMANDOCLASS_HULL_STAND_MIN, COMMANDOCLASS_HULL_STAND_MAX );
CPhysCollide *pCrouchModel = PhysCreateBbox( COMMANDOCLASS_HULL_DUCK_MIN, COMMANDOCLASS_HULL_DUCK_MAX );
m_pPlayer->SetupVPhysicsShadow( pStandModel, "tfplayer_commando_stand", pCrouchModel, "tfplayer_commando_crouch" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassCommando::CanGetInVehicle( void )
{
if ( InBullRush() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CPlayerClassCommando::ClassCostAdjustment( ResupplyBuyType_t nType )
{
int nCost = 0;
if ( nType != RESUPPLY_BUY_HEALTH )
{
nCost = RESUPPLY_ROCKET_COST;
}
return nCost;
}

View File

@@ -0,0 +1,112 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_COMMANDO_H
#define TF_CLASS_COMMANDO_H
#ifdef _WIN32
#pragma once
#endif
#include "tf_playerclass.h"
#include "TFClassData_Shared.h"
#include "basetfcombatweapon_shared.h"
//=====================================================================
// Commando
class CPlayerClassCommando : public CPlayerClass
{
DECLARE_CLASS( CPlayerClassCommando, CPlayerClass );
public:
CPlayerClassCommando( CBaseTFPlayer *pPlayer, TFClass iClass );
virtual ~CPlayerClassCommando();
virtual void ClassActivate( void );
virtual void ClassDeactivate( void );
virtual const char* GetClassModelString( int nTeam );
// Class Initialization
virtual void CreateClass( void ); // Create the class upon initial spawn
virtual void RespawnClass( void ); // Called upon all respawns
virtual bool ResupplyAmmo( float flPercentage, ResupplyReason_t reason );
virtual void SetupMoveData( void ); // Override class specific movement data here.
virtual void SetupSizeData( void ); // Override class specific size data here.
virtual void ResetViewOffset( void );
// Should we take damage-based force?
virtual bool ShouldApplyDamageForce( const CTakeDamageInfo &info );
PlayerClassCommandoData_t *GetClassData( void ) { return &m_ClassData; }
// Class Abilities
virtual void ClassThink( void );
// Resources
int ClassCostAdjustment( ResupplyBuyType_t nType );
// Objects
virtual int CanBuild( int iObjectType );
virtual void FinishedObject( CBaseObject *pObject );
virtual bool ClientCommand( const CCommand &args );
virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
// Adrenalin Rush
virtual void CalculateRush( void );
virtual void StartAdrenalinRush( void );
// Automatic Melee Attack
virtual void Boot( CBaseTFPlayer *pTarget );
// Hooks
virtual void SetPlayerHull( void );
virtual void GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax );
// Orders.
virtual void CreatePersonalOrder( void );
// Bull Rush.
bool InBullRush( void );
bool CanBullRush( void );
void BullRushTouch( CBaseEntity *pTouched );
CNetworkVarEmbedded( PlayerClassCommandoData_t, m_ClassData );
// Player physics shadow.
void InitVCollision( void );
// Vehicle
bool CanGetInVehicle( void );
protected:
// BullRush
void PreBullRush( void );
void PostBullRush( void );
protected:
// Adrenalin Rush
bool m_bCanRush; // True if he has the ability to rush
bool m_bPersonalRush; // True if this he started his current rush, or outside effect
bool m_bHasBattlecry; // True if he has the ability to battlecry
// Weapons
CHandle<CBaseTFCombatWeapon> m_hWpnPlasma;
// CHandle<CBaseTFCombatWeapon> m_hWpnGrenade;
// Automatic Melee Attack
bool m_bCanBoot; // True if he has the ability to boot
float m_flNextBootCheck; // Time at which to recheck for the automatic melee attack
bool m_bOldBullRush;
CUtlVector<CBaseTFPlayer*> m_aHitPlayers; // Player I have hit during this bullrush.
};
EXTERN_SEND_TABLE( DT_PlayerClassCommandoData )
#endif // TF_CLASS_COMMANDO_H

View File

@@ -0,0 +1,352 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Defender Player Class
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_class_defender.h"
#include "tf_obj.h"
#include "tf_obj_sentrygun.h"
#include "basecombatweapon.h"
#include "weapon_builder.h"
#include "weapon_limpetmine.h"
#include "tf_team.h"
#include "orders.h"
#include "order_repair.h"
#include "order_buildsentrygun.h"
#include "weapon_twohandedcontainer.h"
#include "weapon_combatshield.h"
#include "tf_vehicle_teleport_station.h"
ConVar class_defender_speed( "class_defender_speed","200", FCVAR_NONE, "Defender movement speed" );
// An object must be this close to a sentry gun to be considered covered by it.
#define DEFENDER_SENTRY_COVERED_DIST 1000
//=============================================================================
//
// Defender Data Table
//
BEGIN_SEND_TABLE_NOBASE( CPlayerClassDefender, DT_PlayerClassDefenderData )
END_SEND_TABLE()
bool OrderCreator_BuildSentryGun( CPlayerClassDefender *pClass )
{
return COrderBuildSentryGun::CreateOrder( pClass );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassDefender::GetClassModelString( int nTeam )
{
if (nTeam == TEAM_HUMANS)
return "models/player/human_defender.mdl";
else
return "models/player/defender.mdl";
}
//-----------------------------------------------------------------------------
// Purpose: Defender
//-----------------------------------------------------------------------------
CPlayerClassDefender::CPlayerClassDefender( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
CPlayerClassDefender::~CPlayerClassDefender()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::ClassActivate( void )
{
BaseClass::ClassActivate();
// Setup movement data.
SetupMoveData();
m_iNumberOfSentriesAllowed = 0;
m_bHasSmarterSentryguns = false;
m_bHasSensorSentryguns = false;
m_bHasMachinegun = false;
m_bHasRocketlauncher = false;
m_bHasAntiair = false;
m_hWpnShield = NULL;
m_hWpnPlasma = NULL;
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::ClassDeactivate( void )
{
BaseClass::ClassDeactivate();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::CreateClass( void )
{
BaseClass::CreateClass();
// Create our two handed weapon layout
m_hWpnPlasma = static_cast< CBaseTFCombatWeapon * >( m_pPlayer->GiveNamedItem( "weapon_combat_burstrifle" ) );
m_hWpnShield = m_pPlayer->GetCombatShield();
CWeaponTwoHandedContainer *p = ( CWeaponTwoHandedContainer * )m_pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
if ( !p )
{
p = static_cast< CWeaponTwoHandedContainer * >( m_pPlayer->GiveNamedItem( "weapon_twohandedcontainer" ) );
}
if ( p && m_hWpnShield.Get() && m_hWpnPlasma.Get() )
{
m_hWpnShield->SetReflectViewModelAnimations( true );
p->SetWeapons( m_hWpnPlasma, m_hWpnShield );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassDefender::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
if (ResupplyAmmoType( 20 * flFraction, "Limpets" ))
bGiven = true;
// Defender doesn't use rockets, but his sentryguns do
if (ResupplyAmmoType( 50 * flFraction, "Rockets" ))
bGiven = true;
}
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_GRENADES_FROM_STATION))
{
}
// On respawn, resupply base weapon ammo
if ( reason == RESUPPLY_RESPAWN )
{
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set defender class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassDefender::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_defender_speed.GetFloat();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( DEFENDERCLASS_HULL_STAND_MIN, DEFENDERCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( DEFENDERCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = DEFENDERCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose: New technology has been gained. Recalculate any class specific technology dependencies.
//-----------------------------------------------------------------------------
void CPlayerClassDefender::GainedNewTechnology( CBaseTechnology *pTechnology )
{
// Calculate the number of sentryguns allowed
if ( m_pPlayer->HasNamedTechnology( "sentrygun_three" ) )
{
m_iNumberOfSentriesAllowed = 3;
}
else if ( m_pPlayer->HasNamedTechnology( "sentrygun_two" ) )
{
m_iNumberOfSentriesAllowed = 2;
}
else
{
m_iNumberOfSentriesAllowed = 1;
}
m_bHasSmarterSentryguns = false;
m_bHasSensorSentryguns = false;
m_bHasRocketlauncher = false;
// Sentrygun levels
if ( m_pPlayer->HasNamedTechnology( "sentrygun_ai" ) )
{
m_bHasSmarterSentryguns = true;
}
if ( m_pPlayer->HasNamedTechnology( "sentrygun_sensors" ) )
{
m_bHasSensorSentryguns = true;
}
// Sentrygun types
if ( m_pPlayer->HasNamedTechnology( "sentrygun_rocket" ) )
{
m_bHasRocketlauncher = true;
}
UpdateSentrygunTechnology();
BaseClass::GainedNewTechnology( pTechnology );
}
//-----------------------------------------------------------------------------
// Purpose: Tell all sentryguns what level of technology the Defender has
//-----------------------------------------------------------------------------
void CPlayerClassDefender::UpdateSentrygunTechnology( void )
{
for (int i = 0; i < m_pPlayer->GetObjectCount(); i++)
{
CBaseObject *pObj = m_pPlayer->GetObject(i);
if ( pObj && pObj->IsSentrygun() )
{
CObjectSentrygun *pSentry = static_cast<CObjectSentrygun *>(pObj);
pSentry->SetTechnology( m_bHasSmarterSentryguns, m_bHasSensorSentryguns );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this player's allowed to build another one of the specified objects
//-----------------------------------------------------------------------------
int CPlayerClassDefender::CanBuild( int iObjectType )
{
// First, check to see if we've got the technology
if ( iObjectType == OBJ_SENTRYGUN_ROCKET_LAUNCHER )
{
if ( !m_bHasRocketlauncher )
return CB_NOT_RESEARCHED;
}
return BaseClass::CanBuild( iObjectType );
}
int CPlayerClassDefender::CanBuildSentryGun()
{
return
CanBuild( OBJ_SENTRYGUN_ROCKET_LAUNCHER ) == CB_CAN_BUILD ||
CanBuild( OBJ_SENTRYGUN_PLASMA ) == CB_CAN_BUILD;
}
//-----------------------------------------------------------------------------
// Purpose: Object has been built by this player
//-----------------------------------------------------------------------------
void CPlayerClassDefender::FinishedObject( CBaseObject *pObject )
{
UpdateSentrygunTechnology();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::PlayerDied( CBaseEntity *pAttacker )
{
CWeaponLimpetmine *weapon = (CWeaponLimpetmine*)m_pPlayer->Weapon_OwnsThisType( "weapon_limpetmine" );
if ( weapon )
{
weapon->RemoveDeployedLimpets();
}
BaseClass::PlayerDied( pAttacker );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( DEFENDERCLASS_HULL_DUCK_MIN, DEFENDERCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( DEFENDERCLASS_HULL_STAND_MIN, DEFENDERCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax )
{
if ( bDucking )
{
VectorCopy( DEFENDERCLASS_HULL_DUCK_MIN, vecMin );
VectorCopy( DEFENDERCLASS_HULL_DUCK_MAX, vecMax );
}
else
{
VectorCopy( DEFENDERCLASS_HULL_STAND_MIN, vecMin );
VectorCopy( DEFENDERCLASS_HULL_STAND_MAX, vecMax );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( DEFENDERCLASS_VIEWOFFSET_STAND );
}
}
void CPlayerClassDefender::CreatePersonalOrder()
{
if ( CreateInitialOrder() )
return;
if( COrderRepair::CreateOrder_RepairFriendlyObjects( this ) )
return;
// Alternate between sentrygun and sandbag orders.
if ( OrderCreator_BuildSentryGun( this ) )
{
return;
}
BaseClass::CreatePersonalOrder();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassDefender::InitVCollision( void )
{
CPhysCollide *pStandModel = PhysCreateBbox( DEFENDERCLASS_HULL_STAND_MIN, DEFENDERCLASS_HULL_STAND_MAX );
CPhysCollide *pCrouchModel = PhysCreateBbox( DEFENDERCLASS_HULL_DUCK_MIN, DEFENDERCLASS_HULL_DUCK_MAX );
m_pPlayer->SetupVPhysicsShadow( pStandModel, "tfplayer_defender_stand", pCrouchModel, "tfplayer_defender_crouch" );
}

View File

@@ -0,0 +1,77 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Defender player class
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_DEFENDER_H
#define TF_CLASS_DEFENDER_H
#pragma once
#include "TFClassData_Shared.h"
//=====================================================================
// Defender
class CPlayerClassDefender : public CPlayerClass
{
public:
DECLARE_CLASS( CPlayerClassDefender, CPlayerClass );
CPlayerClassDefender( CBaseTFPlayer *pPlayer, TFClass iClass );
~CPlayerClassDefender();
virtual void ClassActivate( void );
virtual void ClassDeactivate( void );
virtual const char* GetClassModelString( int nTeam );
// Class Initialization
virtual void CreateClass( void ); // Create the class upon initial spawn
virtual bool ResupplyAmmo( float flPercentage, ResupplyReason_t reason );
virtual void SetupMoveData( void ); // Override class specific movement data here.
virtual void SetupSizeData( void ); // Override class specific size data here.
virtual void ResetViewOffset( void );
PlayerClassDefenderData_t *GetClassData( void ) { return &m_ClassData; }
virtual void PlayerDied( CBaseEntity *pAttacker );
virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
virtual void UpdateSentrygunTechnology( void );
// Sentrygun building
virtual int CanBuild( int iObjectType );
int CanBuildSentryGun();
virtual void FinishedObject( CBaseObject *pObject );
// Orders.
virtual void CreatePersonalOrder();
// Hooks
virtual void SetPlayerHull( void );
virtual void GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax );
// Player physics shadow.
void InitVCollision( void );
public:
// Sentrygun building
int m_iNumberOfSentriesAllowed;
bool m_bHasSmarterSentryguns;
bool m_bHasSensorSentryguns;
// Sentrygun types
bool m_bHasMachinegun;
bool m_bHasRocketlauncher;
bool m_bHasAntiair;
// Weapons
CHandle<CBaseTFCombatWeapon> m_hWpnPlasma;
private:
PlayerClassDefenderData_t m_ClassData;
};
EXTERN_SEND_TABLE( DT_PlayerClassDefenderData )
#endif // TF_CLASS_DEFENDER_H

View File

@@ -0,0 +1,274 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Escort Player Class
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_class_escort.h"
#include "basecombatweapon.h"
#include "tf_obj_shieldwall.h"
#include "weapon_builder.h"
#include "tf_shareddefs.h"
#include "tf_team.h"
#include "orders.h"
#include "order_buildshieldwall.h"
#include "order_mortar_attack.h"
#include "weapon_twohandedcontainer.h"
#include "tf_shield.h"
#include "iservervehicle.h"
#include "weapon_combatshield.h"
#include "in_buttons.h"
#include "tf_vehicle_teleport_station.h"
#include "weapon_shield.h"
ConVar class_escort_speed( "class_escort_speed","200", FCVAR_NONE, "Escort movement speed" );
//=============================================================================
//
// Escort Data Table
//
BEGIN_SEND_TABLE_NOBASE( CPlayerClassEscort, DT_PlayerClassEscortData )
END_SEND_TABLE()
bool OrderCreator_Mortar( CPlayerClassEscort *pClass )
{
return COrderMortarAttack::CreateOrder( pClass );
}
bool OrderCreator_ShieldWall( CPlayerClassEscort *pClass )
{
return COrderBuildShieldWall::CreateOrder( pClass );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassEscort::GetClassModelString( int nTeam )
{
static const char *string = "models/player/alien_escort.mdl";
return string;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassEscort::CPlayerClassEscort( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassEscort::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( ESCORTCLASS_HULL_STAND_MIN, ESCORTCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( ESCORTCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = ESCORTCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassEscort::~CPlayerClassEscort()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassEscort::ClassActivate( void )
{
BaseClass::ClassActivate();
// Setup movement data.
m_hWeaponProjectedShield = NULL;
SetupMoveData();
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CWeaponShield *CPlayerClassEscort::GetProjectedShield( void )
{
if ( !m_hWeaponProjectedShield )
{
m_hWeaponProjectedShield = static_cast< CWeaponShield * >( m_pPlayer->Weapon_OwnsThisType( "weapon_shield" ) );
if ( !m_hWeaponProjectedShield )
{
m_hWeaponProjectedShield = static_cast< CWeaponShield * >( m_pPlayer->GiveNamedItem( "weapon_shield" ) );
}
}
if ( m_hWeaponProjectedShield )
{
m_hWeaponProjectedShield->SetReflectViewModelAnimations( true );
}
return m_hWeaponProjectedShield;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassEscort::CreateClass( void )
{
BaseClass::CreateClass();
CWeaponTwoHandedContainer *p = ( CWeaponTwoHandedContainer * )m_pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
if ( !p )
{
p = static_cast< CWeaponTwoHandedContainer * >( m_pPlayer->GiveNamedItem( "weapon_twohandedcontainer" ) );
}
CWeaponShield *pShield = GetProjectedShield();
if ( p && pShield )
{
p->SetWeapons( NULL, pShield );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassEscort::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
if ( ResupplyAmmoType( 200 * flFraction, "Bullets" ) )
bGiven = true;
}
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_GRENADES_FROM_STATION))
{
if (ResupplyAmmoType( 5 * flFraction, "ShieldGrenades" ))
bGiven = true;
}
// On respawn, resupply base weapon ammo
if ( reason == RESUPPLY_RESPAWN )
{
if ( ResupplyAmmoType( 200, "Bullets" ) )
bGiven = true;
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set escort class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassEscort::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_escort_speed.GetFloat();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassEscort::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( ESCORTCLASS_HULL_DUCK_MIN, ESCORTCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( ESCORTCLASS_HULL_STAND_MIN, ESCORTCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassEscort::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( ESCORTCLASS_VIEWOFFSET_STAND );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassEscort::PowerupStart( int iPowerup, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier )
{
/*
switch( iPowerup )
{
case POWERUP_BOOST:
// Increase our shield's energy
if ( m_hDeployedShield )
{
m_hDeployedShield->SetPower( m_hDeployedShield->GetPower() + (flAmount * 10) );
}
break;
case POWERUP_EMP:
if ( m_hDeployedShield )
{
m_hDeployedShield->SetEMPed( true );
}
break;
default:
break;
}
*/
BaseClass::PowerupStart( iPowerup, flAmount, pAttacker, pDamageModifier );
}
//-----------------------------------------------------------------------------
// Purpose: Powerup has just started
//-----------------------------------------------------------------------------
void CPlayerClassEscort::PowerupEnd( int iPowerup )
{
/*
switch( iPowerup )
{
case POWERUP_EMP:
if ( m_hDeployedShield )
{
m_hDeployedShield->SetEMPed( false );
}
break;
default:
break;
}
*/
BaseClass::PowerupEnd( iPowerup );
}

View File

@@ -0,0 +1,64 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_ESCORT_H
#define TF_CLASS_ESCORT_H
#ifdef _WIN32
#pragma once
#endif
#include "tf_playerclass.h"
#include "TFClassData_Shared.h"
#include "tf_shieldshared.h"
class CShield;
class CWeaponShield;
//=====================================================================
// Indirect
class CPlayerClassEscort : public CPlayerClass
{
public:
DECLARE_CLASS( CPlayerClassEscort, CPlayerClass );
CPlayerClassEscort( CBaseTFPlayer *pPlayer, TFClass iClass );
~CPlayerClassEscort();
virtual void ClassActivate( void );
virtual const char* GetClassModelString( int nTeam );
// Class Initialization
virtual void CreateClass( void ); // Create the class upon initial spawn
virtual bool ResupplyAmmo( float flPercentage, ResupplyReason_t reason );
virtual void SetupMoveData( void ); // Override class specific movement data here.
virtual void SetupSizeData( void ); // Override class specific size data here.
virtual void ResetViewOffset( void );
PlayerClassEscortData_t *GetClassData( void ) { return &m_ClassData; }
// Hooks
virtual void SetPlayerHull( void );
// Powerups
virtual void PowerupStart( int iPowerup, float flAmount, CBaseEntity *pAttacker, CDamageModifier *pDamageModifier );
virtual void PowerupEnd( int iPowerup );
private:
// Purpose:
CWeaponShield *GetProjectedShield( void );
protected:
PlayerClassEscortData_t m_ClassData;
CHandle<CWeaponShield> m_hWeaponProjectedShield;
};
EXTERN_SEND_TABLE( DT_PlayerClassEscortData )
#endif // TF_CLASS_ESCORT_H

View File

@@ -0,0 +1,275 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Infiltrator Player Class
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "orders.h"
#include "tf_player.h"
#include "tf_class_infiltrator.h"
#include "EntityList.h"
#include "basecombatweapon.h"
#include "menu_base.h"
#include "tf_obj.h"
#include "tf_team.h"
#include "weapon_builder.h"
ConVar class_infiltrator_speed( "class_infiltrator_speed","200", FCVAR_NONE, "Infiltrator movement speed" );
//=============================================================================
//
// Infiltrator Data Table
//
BEGIN_SEND_TABLE_NOBASE( CPlayerClassInfiltrator, DT_PlayerClassInfiltratorData )
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassInfiltrator::GetClassModelString( int nTeam )
{
static const char *string = "models/player/spy.mdl";
return string;
}
// Infiltrator
CPlayerClassInfiltrator::CPlayerClassInfiltrator( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassInfiltrator::~CPlayerClassInfiltrator()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::ClassActivate( void )
{
BaseClass::ClassActivate();
// Setup movement data.
SetupMoveData();
//m_hAssassinationWeapon = NULL;
m_hSwappedWeapon = NULL;
m_bCanConsumeCorpses = false;
m_flStartCamoAt = 0.0f;
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
//-----------------------------------------------------------------------------
// Purpose: Register for precaching.
//-----------------------------------------------------------------------------
void PrecacheInfiltrator(void *pUser)
{
}
PRECACHE_REGISTER_FN(PrecacheInfiltrator);
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::RespawnClass( void )
{
BaseClass::RespawnClass();
m_flStartCamoAt = gpGlobals->curtime + INFILTRATOR_CAMOTIME_AFTER_SPAWN;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassInfiltrator::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
if (ResupplyAmmoType( 200 * flFraction, "Bullets" ))
bGiven = true;
if (ResupplyAmmoType( 20 * flFraction, "Limpets" ))
bGiven = true;
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set infiltrator class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_infiltrator_speed.GetFloat();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( INFILTRATORCLASS_HULL_STAND_MIN, INFILTRATORCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( INFILTRATORCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = INFILTRATORCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose: New technology has been gained. Recalculate any class specific technology dependencies.
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::GainedNewTechnology( CBaseTechnology *pTechnology )
{
// Consume corpse technology?
m_bCanConsumeCorpses = m_pPlayer->HasNamedTechnology( "inf_consume_corpse" );
BaseClass::GainedNewTechnology( pTechnology );
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this player's allowed to build another one of the specified objects
//-----------------------------------------------------------------------------
int CPlayerClassInfiltrator::CanBuild( int iObjectType )
{
return BaseClass::CanBuild( iObjectType );
}
//-----------------------------------------------------------------------------
// Purpose: Called every frame by postthink
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::ClassThink( void )
{
BaseClass::ClassThink();
CheckForAssassination();
}
//-----------------------------------------------------------------------------
// Purpose: The player's just had his camo cleared
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::ClearCamouflage( void )
{
float flNewTime = gpGlobals->curtime + INFILTRATOR_RECAMO_TIME;
if ( flNewTime > m_flStartCamoAt )
{
m_flStartCamoAt = flNewTime;
}
}
//-----------------------------------------------------------------------------
// Purpose: Player's finished disguising.
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::FinishedDisguising( void )
{
// Remove my camo
m_pPlayer->ClearCamouflage();
}
//-----------------------------------------------------------------------------
// Purpose: Player's lost his disguise.
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::StopDisguising( void )
{
// Remove & restart my camo
m_pPlayer->ClearCamouflage();
}
//-----------------------------------------------------------------------------
// Purpose: Handle custom commands for this playerclass
//-----------------------------------------------------------------------------
bool CPlayerClassInfiltrator::ClientCommand( const char *pcmd )
{
// Toggle thermal vision
if ( FStrEq( pcmd, "special" ) )
{
Assert( m_pPlayer );
// Toggle
m_pPlayer->SetUsingThermalVision( !m_pPlayer->IsUsingThermalVision() );
return true;
}
return BaseClass::ClientCommand( pcmd );
}
//-----------------------------------------------------------------------------
// Purpose: Check to see if I can assassinate anyone
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::CheckForAssassination( void )
{
/*
// Find my assassination weapon if I haven't already
if ( m_hAssassinationWeapon == NULL )
{
m_hAssassinationWeapon = (CWeaponInfiltrator*)m_pPlayer->Weapon_OwnsThisType( "weapon_infiltrator" );
}
// No Assasination weapon?
if ( m_hAssassinationWeapon == NULL )
return;
// If we have a target, bring the assassination weapon up.
if ( m_hAssassinationWeapon->GetAssassinationTarget() )
{
if ( m_pPlayer->GetActiveWeapon() != m_hAssassinationWeapon.Get() )
{
m_hSwappedWeapon = m_pPlayer->GetActiveWeapon();
m_pPlayer->Weapon_Switch( m_hAssassinationWeapon );
}
}
else
{
// We have no target. If the assassination weapon's up, put it away.
if ( m_pPlayer->GetActiveWeapon() == m_hAssassinationWeapon.Get() )
{
// Don't switch until the weapon's finished killing people
if ( m_pPlayer->GetActiveWeapon()->m_flNextPrimaryAttack <= gpGlobals->curtime )
{
m_pPlayer->Weapon_Switch( m_hSwappedWeapon );
m_hSwappedWeapon = NULL;
}
}
}
*/
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( INFILTRATORCLASS_HULL_DUCK_MIN, INFILTRATORCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( INFILTRATORCLASS_HULL_STAND_MIN, INFILTRATORCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassInfiltrator::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( INFILTRATORCLASS_VIEWOFFSET_STAND );
}
}

View File

@@ -0,0 +1,78 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Infiltrator
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_INFILTRATOR_H
#define TF_CLASS_INFILTRATOR_H
#ifdef _WIN32
#pragma once
#endif
#define INFILTRATOR_EAVESDROP_RADIUS 256.0f
#define INFILTRATOR_DISGUISE_TIME 3.0f
// Time after losing camo before the infiltrator can re-camo
#define INFILTRATOR_RECAMO_TIME 5.0
// Time after spawning that the infilitrator's camo kicks in
#define INFILTRATOR_CAMOTIME_AFTER_SPAWN 3.0
#include "TFClassData_Shared.h"
class CLootableCorpse;
//=====================================================================
// Infiltrator
class CPlayerClassInfiltrator : public CPlayerClass
{
DECLARE_CLASS( CPlayerClassInfiltrator, CPlayerClass );
public:
CPlayerClassInfiltrator( CBaseTFPlayer *pPlayer, TFClass iClass );
~CPlayerClassInfiltrator();
virtual void ClassActivate( void );
virtual const char* GetClassModelString( int nTeam );
// Class Initialization
virtual void RespawnClass( void ); // Called upon all respawns
virtual bool ResupplyAmmo( float flPercentage, ResupplyReason_t reason );
virtual void SetupMoveData( void ); // Override class specific movement data here.
virtual void SetupSizeData( void ); // Override class specific size data here.
virtual void ResetViewOffset( void );
PlayerClassInfiltratorData_t *GetClassData( void ) { return &m_ClassData; }
virtual void ClassThink( void );
virtual void ClearCamouflage( void );
virtual int CanBuild( int iObjectType );
virtual bool ClientCommand( const CCommand &args );
virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
void CheckForAssassination( void );
// Disguise
virtual void FinishedDisguising( void );
virtual void StopDisguising( void );
// Hooks
virtual void SetPlayerHull( void );
protected:
bool m_bCanConsumeCorpses;
float m_flStartCamoAt;
// Assassination weapon
//CHandle<CWeaponInfiltrator> m_hAssassinationWeapon;
CHandle<CBaseCombatWeapon> m_hSwappedWeapon;
PlayerClassInfiltratorData_t m_ClassData;
};
EXTERN_SEND_TABLE( DT_PlayerClassInfiltratorData )
#endif // TF_CLASS_INFILTRATOR_H

View File

@@ -0,0 +1,305 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Medic Player Class
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "basecombatweapon.h"
#include "tf_player.h"
#include "tf_class_medic.h"
#include "tf_obj.h"
#include "tf_obj_resupply.h"
#include "tf_obj_resourcepump.h"
#include "weapon_builder.h"
#include "tf_team.h"
#include "orders.h"
#include "entitylist.h"
#include "tf_func_resource.h"
#include "order_resupply.h"
#include "order_resourcepump.h"
#include "order_heal.h"
#include "weapon_twohandedcontainer.h"
#include "weapon_combatshield.h"
// Radius buff defines
#define RADIUS_BUFF_RANGE 512.0
#define MEDIC_HEAL_TIME_AFTER_DAMAGE 10.0
// Medic
ConVar class_medic_speed( "class_medic_speed","200", FCVAR_NONE, "Medic movement speed" );
//=============================================================================
//
// Medic Data Table
//
BEGIN_SEND_TABLE_NOBASE( CPlayerClassMedic, DT_PlayerClassMedicData )
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassMedic::GetClassModelString( int nTeam )
{
if (nTeam == TEAM_HUMANS)
return "models/player/human_medic.mdl";
else
return "models/player/recon.mdl";
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassMedic::CPlayerClassMedic( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( MEDICCLASS_HULL_STAND_MIN, MEDICCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( MEDICCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = MEDICCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassMedic::~CPlayerClassMedic()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::ClassActivate( void )
{
BaseClass::ClassActivate();
// Setup movement data.
SetupMoveData();
m_hWpnShield = NULL;
m_hWpnPlasma = NULL;
m_bHasAutoRepair = false;
m_flNextHealTime = 0;
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::ClassDeactivate( void )
{
BaseClass::ClassDeactivate();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::CreateClass( void )
{
BaseClass::CreateClass();
// Create our two handed weapon layout
m_hWpnPlasma = static_cast< CBaseTFCombatWeapon * >( m_pPlayer->GiveNamedItem( "weapon_combat_burstrifle" ) );
m_hWpnShield = m_pPlayer->GetCombatShield();
CWeaponTwoHandedContainer *p = ( CWeaponTwoHandedContainer * )m_pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
if ( !p )
{
p = static_cast< CWeaponTwoHandedContainer * >( m_pPlayer->GiveNamedItem( "weapon_twohandedcontainer" ) );
}
if ( p && m_hWpnShield.Get() && m_hWpnPlasma.Get() )
{
m_hWpnShield->SetReflectViewModelAnimations( true );
p->SetWeapons( m_hWpnPlasma, m_hWpnShield );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::RespawnClass( void )
{
BaseClass::RespawnClass();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassMedic::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_GRENADES_FROM_STATION))
{
if (ResupplyAmmoType( 3 * flFraction, "Grenades" ))
bGiven = true;
}
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
}
if ( reason == RESUPPLY_RESPAWN )
{
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set medic class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassMedic::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_medic_speed.GetFloat();
}
//-----------------------------------------------------------------------------
// Purpose: Called every frame by postthink
//-----------------------------------------------------------------------------
void CPlayerClassMedic::ClassThink( void )
{
// Heal me if it's time (and I'm not dead!)
if ( m_pPlayer->IsAlive() && m_flNextHealTime && m_flNextHealTime < gpGlobals->curtime )
{
if ( m_pPlayer->GetHealth() < m_pPlayer->GetMaxHealth() )
{
// Heal the player
m_pPlayer->TakeHealth( 1.0, DMG_GENERIC );
m_flNextHealTime = gpGlobals->curtime + 0.1;
}
else
{
m_flNextHealTime = 0;
}
}
BaseClass::ClassThink();
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this player's allowed to build another one of the specified objects
//-----------------------------------------------------------------------------
int CPlayerClassMedic::CanBuild( int iObjectType )
{
return BaseClass::CanBuild( iObjectType );
}
//-----------------------------------------------------------------------------
// Purpose: Update abilities based on new technologies gained
//-----------------------------------------------------------------------------
void CPlayerClassMedic::GainedNewTechnology( CBaseTechnology *pTechnology )
{
// Autorepair
m_bHasAutoRepair = m_pPlayer->HasNamedTechnology( "obj_autorepair" );
BaseClass::GainedNewTechnology( pTechnology );
}
//-----------------------------------------------------------------------------
// Purpose: Create a personal order for this player
//-----------------------------------------------------------------------------
void CPlayerClassMedic::CreatePersonalOrder( void )
{
if ( CreateInitialOrder() )
return;
// Check for healing orders.
if ( COrderHeal::CreateOrder( this ) )
return;
// Should they build a resource pump?
if ( COrderResourcePump::CreateOrder( this ) )
return;
// Build a resupply station?
if ( COrderResupply::CreateOrder( this ) )
return;
BaseClass::CreatePersonalOrder();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( MEDICCLASS_HULL_DUCK_MIN, MEDICCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( MEDICCLASS_HULL_STAND_MIN, MEDICCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax )
{
if ( bDucking )
{
VectorCopy( MEDICCLASS_HULL_DUCK_MIN, vecMin );
VectorCopy( MEDICCLASS_HULL_DUCK_MAX, vecMax );
}
else
{
VectorCopy( MEDICCLASS_HULL_STAND_MIN, vecMin );
VectorCopy( MEDICCLASS_HULL_STAND_MAX, vecMax );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( MEDICCLASS_VIEWOFFSET_STAND );
}
}
//-----------------------------------------------------------------------------
// Purpose: The player has taken damage. Return the damage done.
//-----------------------------------------------------------------------------
float CPlayerClassMedic::OnTakeDamage( const CTakeDamageInfo &info )
{
// Stop healing when taking damage
m_flNextHealTime = gpGlobals->curtime + MEDIC_HEAL_TIME_AFTER_DAMAGE;
return BaseClass::OnTakeDamage( info );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassMedic::InitVCollision( void )
{
CPhysCollide *pStandModel = PhysCreateBbox( MEDICCLASS_HULL_STAND_MIN, MEDICCLASS_HULL_STAND_MAX );
CPhysCollide *pCrouchModel = PhysCreateBbox( MEDICCLASS_HULL_DUCK_MIN, MEDICCLASS_HULL_DUCK_MAX );
m_pPlayer->SetupVPhysicsShadow( pStandModel, "tfplayer_medic_stand", pCrouchModel, "tfplayer_medic_crouch" );
}

View File

@@ -0,0 +1,73 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Medic playerclass
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_MEDIC_H
#define TF_CLASS_MEDIC_H
#ifdef _WIN32
#pragma once
#endif
#include "TFClassData_Shared.h"
//=====================================================================
// Medic
class CPlayerClassMedic : public CPlayerClass
{
DECLARE_CLASS( CPlayerClassMedic, CPlayerClass );
public:
CPlayerClassMedic( CBaseTFPlayer *pPlayer, TFClass iClass );
virtual ~CPlayerClassMedic();
virtual void ClassActivate( void );
virtual void ClassDeactivate( void );
virtual const char* GetClassModelString( int nTeam );
// Class Initialization
virtual void CreateClass( void ); // Create the class upon initial spawn
virtual void RespawnClass( void ); // Called upon all respawns
virtual bool ResupplyAmmo( float flPercentage, ResupplyReason_t reason );
virtual void SetupMoveData( void ); // Override class specific movement data here.
virtual void SetupSizeData( void ); // Override class specific size data here.
virtual void ResetViewOffset( void );
PlayerClassMedicData_t *GetClassData( void ) { return &m_ClassData; }
virtual void ClassThink( void );
virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
// Objects
int CanBuild( int iObjectType );
// Orders
virtual void CreatePersonalOrder( void );
// Hooks
virtual void SetPlayerHull( void );
virtual void GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax );
virtual float OnTakeDamage( const CTakeDamageInfo &info );
// Player physics shadow.
void InitVCollision( void );
protected:
// Weapons
CHandle<CBaseTFCombatWeapon> m_hWpnPlasma;
bool m_bHasAutoRepair;
float m_flNextHealTime;
PlayerClassMedicData_t m_ClassData;
};
EXTERN_SEND_TABLE( DT_PlayerClassMedicData )
#endif // TF_CLASS_MEDIC_H

View File

@@ -0,0 +1,164 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "basecombatweapon.h"
#include "tf_player.h"
#include "tf_class_pyro.h"
#include "weapon_twohandedcontainer.h"
#include "weapon_gas_can.h"
#include "weapon_flame_thrower.h"
#include "gasoline_shared.h"
#include "weapon_combatshield.h"
BEGIN_SEND_TABLE_NOBASE( CPlayerClassPyro, DT_PlayerClassPyroData )
END_SEND_TABLE()
CPlayerClassPyro::CPlayerClassPyro( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
CPlayerClassPyro::~CPlayerClassPyro()
{
}
void CPlayerClassPyro::SetupSizeData()
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( PYROCLASS_HULL_STAND_MIN, PYROCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( PYROCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = PYROCLASS_STEPSIZE;
}
void CPlayerClassPyro::ClassActivate()
{
BaseClass::ClassActivate();
// Setup movement data.
SetupMoveData();
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
const char *CPlayerClassPyro::GetClassModelString( int nTeam )
{
if (nTeam == TEAM_HUMANS)
return "models/player/medic.mdl";
else
return "models/player/recon.mdl";
}
void CPlayerClassPyro::CreateClass()
{
BaseClass::CreateClass();
// Create our two handed weapon layout
m_hWpnFlameThrower = static_cast< CWeaponFlameThrower * >( m_pPlayer->GiveNamedItem( "weapon_flame_thrower" ) );
m_hWpnGasCan = static_cast< CWeaponGasCan * >( m_pPlayer->GiveNamedItem( "weapon_gas_can" ) );
m_hWpnShield = m_pPlayer->GetCombatShield();
CWeaponTwoHandedContainer *p = ( CWeaponTwoHandedContainer * )m_pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
if ( !p )
{
p = static_cast< CWeaponTwoHandedContainer * >( m_pPlayer->GiveNamedItem( "weapon_twohandedcontainer" ) );
}
if ( p && m_hWpnShield.Get() && m_hWpnGasCan.Get() )
{
m_hWpnShield->SetReflectViewModelAnimations( true );
p->SetWeapons( m_hWpnGasCan, m_hWpnShield );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassPyro::SetPlayerHull()
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( PYROCLASS_HULL_DUCK_MIN, PYROCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( PYROCLASS_HULL_STAND_MIN, PYROCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassPyro::GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax )
{
if ( bDucking )
{
VectorCopy( PYROCLASS_HULL_DUCK_MIN, vecMin );
VectorCopy( PYROCLASS_HULL_DUCK_MAX, vecMax );
}
else
{
VectorCopy( PYROCLASS_HULL_STAND_MIN, vecMin );
VectorCopy( PYROCLASS_HULL_STAND_MAX, vecMax );
}
}
void CPlayerClassPyro::ResetViewOffset()
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( PYROCLASS_VIEWOFFSET_STAND );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassPyro::InitVCollision()
{
CPhysCollide *pStandModel = PhysCreateBbox( PYROCLASS_HULL_STAND_MIN, PYROCLASS_HULL_STAND_MAX );
CPhysCollide *pCrouchModel = PhysCreateBbox( PYROCLASS_HULL_DUCK_MIN, PYROCLASS_HULL_DUCK_MAX );
m_pPlayer->SetupVPhysicsShadow( pStandModel, "tfplayer_medic_stand", pCrouchModel, "tfplayer_medic_crouch" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassPyro::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
if (ResupplyAmmoType( 80 * flFraction, PYRO_AMMO_TYPE ))
bGiven = true;
}
// On respawn, resupply base weapon ammo
if ( reason == RESUPPLY_RESPAWN )
{
if ( ResupplyAmmoType( 30, PYRO_AMMO_TYPE ) )
bGiven = true;
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}

View File

@@ -0,0 +1,57 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_PYRO_H
#define TF_CLASS_PYRO_H
#ifdef _WIN32
#pragma once
#endif
#include "TFClassData_Shared.h"
class CWeaponFlameThrower;
class CWeaponGasCan;
//=====================================================================
// Medic
class CPlayerClassPyro : public CPlayerClass
{
DECLARE_CLASS( CPlayerClassPyro, CPlayerClass );
public:
CPlayerClassPyro( CBaseTFPlayer *pPlayer, TFClass iClass );
virtual ~CPlayerClassPyro();
// Overrides.
public:
virtual void SetupSizeData();
virtual void CreateClass();
virtual void SetPlayerHull();
virtual void GetPlayerHull( bool bDucking, Vector &vecMin, Vector &vecMax );
virtual void ResetViewOffset();
virtual void InitVCollision();
virtual bool ResupplyAmmo( float flFraction, ResupplyReason_t reason );
virtual void ClassActivate();
virtual const char* GetClassModelString( int nTeam );
private:
PlayerClassPyroData_t m_ClassData;
CHandle<CWeaponFlameThrower> m_hWpnFlameThrower;
CHandle<CWeaponGasCan> m_hWpnGasCan;
};
#endif // TF_CLASS_PYRO_H

View File

@@ -0,0 +1,221 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Recon Player Class
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_class_recon.h"
#include "tf_reconvars.h"
#include "in_buttons.h"
#include "basecombatweapon.h"
#include "tf_obj.h"
#include "weapon_builder.h"
#include "tf_team.h"
#include "tf_func_resource.h"
#include "orders.h"
#include "engine/IEngineSound.h"
extern short g_sModelIndexFireball;
ConVar class_recon_speed( "class_recon_speed","250", FCVAR_NONE, "Recon movement speed" );
// ------------------------------------------------------------------------ //
// Globals.
// ------------------------------------------------------------------------ //
//=============================================================================
//
// Recon Data Table
//
BEGIN_SEND_TABLE_NOBASE( CPlayerClassRecon, DT_PlayerClassReconData )
SendPropInt ( SENDINFO_STRUCTELEM( m_ClassData.m_nJumpCount ), 3, SPROP_UNSIGNED ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flSuppressionJumpTime ), 32, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flSuppressionImpactTime ), 32, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flStickTime ), 32, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flActiveJumpTime ), 32, SPROP_NOSCALE ),
SendPropFloat ( SENDINFO_STRUCTELEM( m_ClassData.m_flImpactDist ), 32, SPROP_NOSCALE ),
SendPropVector ( SENDINFO_STRUCTELEM( m_ClassData.m_vecImpactNormal ), -1, SPROP_NORMAL ),
SendPropVector ( SENDINFO_STRUCTELEM( m_ClassData.m_vecUnstickVelocity ), -1, SPROP_COORD ),
SendPropInt ( SENDINFO_STRUCTELEM( m_ClassData.m_bTrailParticles ), 1, SPROP_UNSIGNED ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassRecon::GetClassModelString( int nTeam )
{
static const char *string = "models/player/recon.mdl";
return string;
}
// ------------------------------------------------------------------------ //
// CPlayerClassRecon
// ------------------------------------------------------------------------ //
CPlayerClassRecon::CPlayerClassRecon( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassRecon::~CPlayerClassRecon()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::ClassActivate( void )
{
BaseClass::ClassActivate();
// Setup movement data.
SetupMoveData();
m_bHasRadarScanner = false;
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::ClassDeactivate( void )
{
BaseClass::ClassDeactivate();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassRecon::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_GRENADES_FROM_STATION))
{
}
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
}
if ( BaseClass::ResupplyAmmo(flFraction, reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set recon class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassRecon::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_recon_speed.GetFloat();
m_ClassData.m_nJumpCount = 0;
m_ClassData.m_flSuppressionJumpTime = -99999;
m_ClassData.m_flSuppressionImpactTime = -99999;
m_ClassData.m_flActiveJumpTime = -99999;
m_ClassData.m_flStickTime = -99999;
m_ClassData.m_flImpactDist = -99999;
m_ClassData.m_vecImpactNormal.Init();
m_ClassData.m_vecUnstickVelocity.Init();
m_ClassData.m_bTrailParticles = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( RECONCLASS_HULL_STAND_MIN, RECONCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( RECONCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = RECONCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this player's allowed to build another one of the specified objects
//-----------------------------------------------------------------------------
int CPlayerClassRecon::CanBuild( int iObjectType )
{
return BaseClass::CanBuild( iObjectType );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::ClassThink()
{
BaseClass::ClassThink();
m_ClassData.m_bTrailParticles = (m_pPlayer->IsAlive() && !(m_pPlayer->GetFlags() & FL_ONGROUND));
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::CreatePersonalOrder()
{
if ( CreateInitialOrder() )
return;
BaseClass::CreatePersonalOrder();
}
//-----------------------------------------------------------------------------
// Purpose: New technology has been gained. Recalculate any class specific technology dependencies.
//-----------------------------------------------------------------------------
void CPlayerClassRecon::GainedNewTechnology( CBaseTechnology *pTechnology )
{
// Radar Scanner
if ( m_pPlayer->HasNamedTechnology( "rec_b_radar_scanners" ) )
{
m_bHasRadarScanner = true;
}
else
{
m_bHasRadarScanner = false;
}
BaseClass::GainedNewTechnology( pTechnology );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( RECONCLASS_HULL_DUCK_MIN, RECONCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( RECONCLASS_HULL_STAND_MIN, RECONCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassRecon::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( RECONCLASS_VIEWOFFSET_STAND );
}
}

View File

@@ -0,0 +1,60 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TF_CLASS_RECON_H
#define TF_CLASS_RECON_H
#pragma once
#include "TFClassData_Shared.h"
#include "tf_playerclass.h"
class CTFJetpackSteam;
class CReconJetpackLevel;
//=====================================================================
// Recon
class CPlayerClassRecon : public CPlayerClass
{
public:
DECLARE_CLASS( CPlayerClassRecon, CPlayerClass );
CPlayerClassRecon( CBaseTFPlayer *pPlayer, TFClass iClass );
~CPlayerClassRecon();
virtual void ClassActivate( void );
virtual void ClassDeactivate( void );
virtual const char* GetClassModelString( int nTeam );
// Class Initialization
virtual bool ResupplyAmmo( float flPercentage, ResupplyReason_t reason );
virtual void SetupMoveData( void ); // Override class specific movement data here.
virtual void SetupSizeData( void ); // Override class specific size data here.
virtual void ResetViewOffset( void );
PlayerClassReconData_t *GetClassData( void ) { return &m_ClassData; }
virtual int CanBuild( int iObjectType );
virtual void ClassThink( void );
virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
virtual void CreatePersonalOrder();
// Hooks
virtual void SetPlayerHull( void );
CNetworkVarEmbedded( PlayerClassReconData_t, m_ClassData );
protected:
bool m_bHasRadarScanner;
};
EXTERN_SEND_TABLE( DT_PlayerClassReconData )
#endif // TF_CLASS_RECON_H

View File

@@ -0,0 +1,328 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Sapper Player Class
//
// $Workfile: $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_class_sapper.h"
#include "basecombatweapon.h"
#include "weapon_builder.h"
#include "tf_team.h"
#include "entitylist.h"
#include "tf_obj.h"
#include "weapon_mortar.h"
#include "orders.h"
#include "engine/IEngineSound.h"
#include "weapon_twohandedcontainer.h"
#include "weapon_combatshield.h"
#include "tf_vehicle_teleport_station.h"
#define EMP_RADIUS_NORMAL ( 512.0f )
#define EMP_RADIUS_MEDIUM ( 1024.0f )
#define EMP_RADIUS_HUGE ( 4096.0f )
#define EMP_RECHARGETIME_NORMAL ( 20.0f )
#define EMP_RECHARGETIME_FAST ( 15.0f )
#define EMP_EFFECTTIME_NORMAL ( 10.0f )
#define EMP_EFFECTTIME_LONG ( 15.0f )
ConVar class_sapper_speed( "class_sapper_speed","225", FCVAR_NONE, "Sapper movement speed" );
ConVar class_sapper_boost_amount( "class_sapper_boost_amount","35", FCVAR_NONE, "Amount of energy drained for the Sapper to get a boost." );
ConVar class_sapper_boost_time( "class_sapper_boost_time","15", FCVAR_NONE, "Sapper's boost time after draining a full charge of boost." );
ConVar class_sapper_boost_rate( "class_sapper_boost_rate","1", FCVAR_NONE, "Sapper's boost rate on his self-boost." );
ConVar class_sapper_damage_modifier( "class_sapper_damage_modifier", "1.5", 0, "Scales the damage a Sapper does while he's self-boosted." );
BEGIN_SEND_TABLE_NOBASE( CPlayerClassSapper, DT_PlayerClassSapperData )
SendPropFloat( SENDINFO( m_flDrainedEnergy ), 8, SPROP_UNSIGNED, 0.0f, 1.0f ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CPlayerClassSapper::GetClassModelString( int nTeam )
{
static const char *string = "models/player/technician.mdl";
return string;
}
//-----------------------------------------------------------------------------
// Purpose: Sapper
//-----------------------------------------------------------------------------
CPlayerClassSapper::CPlayerClassSapper( CBaseTFPlayer *pPlayer, TFClass iClass ) : CPlayerClass( pPlayer, iClass )
{
for (int i = 0; i < MAX_TF_TEAMS; ++i)
{
SetClassModel( MAKE_STRING(GetClassModelString(i)), i );
}
// Setup movement data.
SetupMoveData();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClassSapper::~CPlayerClassSapper()
{
}
//-----------------------------------------------------------------------------
// Purpose: Any objects created/owned by class should be allocated and destroyed here
//-----------------------------------------------------------------------------
void CPlayerClassSapper::ClassActivate( void )
{
BaseClass::ClassActivate();
m_bHasMediumRangeEMP = false;
m_bHasFasterRechargingEMP = false;
m_bHasHugeRadiusEMP = false;
m_bHasLongerLastingEMPEffect = false;
m_vecLastOrigin.Init();
m_flLastMoveTime = 0.0f;
memset( &m_ClassData, 0, sizeof( m_ClassData ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::ClassDeactivate( void )
{
m_DamageModifier.RemoveModifier();
BaseClass::ClassDeactivate();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::CreateClass( void )
{
BaseClass::CreateClass();
// Create our two handed weapon layout
m_hWpnPlasma = static_cast< CBaseTFCombatWeapon * >( m_pPlayer->GiveNamedItem( "weapon_combat_shotgun" ) );
m_hWpnShield = m_pPlayer->GetCombatShield();
CWeaponTwoHandedContainer *p = ( CWeaponTwoHandedContainer * )m_pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
if ( !p )
{
p = static_cast< CWeaponTwoHandedContainer * >( m_pPlayer->GiveNamedItem( "weapon_twohandedcontainer" ) );
}
if ( p && m_hWpnShield.Get() && m_hWpnPlasma.Get() )
{
m_hWpnShield->SetReflectViewModelAnimations( true );
p->SetWeapons( m_hWpnPlasma, m_hWpnShield );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::RespawnClass( void )
{
BaseClass::RespawnClass();
// Reset time of last move
m_vecLastOrigin.Init();
m_flLastMoveTime = 0.0f;
m_flDrainedEnergy = 0;
m_flBoostEndTime = 0;
m_DamageModifier.SetModifier( class_sapper_damage_modifier.GetFloat() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassSapper::ResupplyAmmo( float flFraction, ResupplyReason_t reason )
{
bool bGiven = false;
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_GRENADES_FROM_STATION))
{
if (ResupplyAmmoType( 3 * flFraction, "Grenades" ))
bGiven = true;
}
if ((reason == RESUPPLY_ALL_FROM_STATION) || (reason == RESUPPLY_AMMO_FROM_STATION))
{
}
// On respawn, resupply base weapon ammo
if ( reason == RESUPPLY_RESPAWN )
{
}
if ( BaseClass::ResupplyAmmo(flFraction,reason) )
bGiven = true;
return bGiven;
}
//-----------------------------------------------------------------------------
// Purpose: Set sapper class specific movement data here.
//-----------------------------------------------------------------------------
void CPlayerClassSapper::SetupMoveData( void )
{
// Setup Class statistics
m_flMaxWalkingSpeed = class_sapper_speed.GetFloat();
m_vecLastOrigin.Init();
m_flLastMoveTime = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( SAPPERCLASS_HULL_STAND_MIN, SAPPERCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( SAPPERCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = SAPPERCLASS_STEPSIZE;
}
//-----------------------------------------------------------------------------
// Purpose: New technology has been gained. Recalculate any class specific technology dependencies.
//-----------------------------------------------------------------------------
void CPlayerClassSapper::GainedNewTechnology( CBaseTechnology *pTechnology )
{
m_bHasMediumRangeEMP = false;
m_bHasFasterRechargingEMP = false;
m_bHasHugeRadiusEMP = false;
m_bHasLongerLastingEMPEffect = false;
if ( m_pPlayer->HasNamedTechnology( "emp1" ) )
{
m_bHasFasterRechargingEMP = true;
}
if ( m_pPlayer->HasNamedTechnology( "emp3" ) )
{
m_bHasMediumRangeEMP = true;
}
if ( m_pPlayer->HasNamedTechnology( "emp4" ) )
{
m_bHasLongerLastingEMPEffect = true;
}
if ( m_pPlayer->HasNamedTechnology( "emp5" ) )
{
m_bHasHugeRadiusEMP = true;
}
BaseClass::GainedNewTechnology( pTechnology );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::ClassThink( void )
{
if ( m_pPlayer->GetLocalOrigin() != m_vecLastOrigin )
{
m_vecLastOrigin = m_pPlayer->GetLocalOrigin();
m_flLastMoveTime = gpGlobals->curtime;
}
// Am I boosting myself?
if ( m_pPlayer->IsAlive() && (m_flBoostEndTime > gpGlobals->curtime) )
{
m_pPlayer->AttemptToPowerup( POWERUP_BOOST, class_sapper_boost_time.GetFloat(), class_sapper_boost_rate.GetFloat() * 0.1, m_pPlayer, &m_DamageModifier );
}
else
{
m_DamageModifier.RemoveModifier();
}
BaseClass::ClassThink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPlayerClassSapper::CheckStationaryTime( float time_required )
{
// Has enough time passed?
if ( gpGlobals->curtime >= m_flLastMoveTime + time_required )
{
return true;
}
// Not enough time yet
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::CreatePersonalOrder( void )
{
if ( CreateInitialOrder() )
return;
BaseClass::CreatePersonalOrder();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::SetPlayerHull( void )
{
if ( m_pPlayer->GetFlags() & FL_DUCKING )
{
m_pPlayer->SetCollisionBounds( SAPPERCLASS_HULL_DUCK_MIN, SAPPERCLASS_HULL_DUCK_MAX );
}
else
{
m_pPlayer->SetCollisionBounds( SAPPERCLASS_HULL_STAND_MIN, SAPPERCLASS_HULL_STAND_MAX );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::ResetViewOffset( void )
{
if ( m_pPlayer )
{
m_pPlayer->SetViewOffset( SAPPERCLASS_VIEWOFFSET_STAND );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::AddDrainedEnergy( float flEnergy )
{
// Convert to 0->1
flEnergy = ( flEnergy / class_sapper_boost_amount.GetFloat() );
m_flDrainedEnergy = MIN( 1.0, m_flDrainedEnergy + flEnergy );
// Did we hit max?
if ( m_flDrainedEnergy >= 1.0 )
{
m_flDrainedEnergy = 0;
// Boost the player
m_flBoostEndTime = gpGlobals->curtime + class_sapper_boost_time.GetFloat();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CPlayerClassSapper::GetDrainedEnergy( void )
{
return m_flDrainedEnergy;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClassSapper::DeductDrainedEnergy( float flEnergy )
{
m_flDrainedEnergy = MAX( 0, m_flDrainedEnergy - flEnergy );
}

Some files were not shown because too many files have changed in this diff Show More