You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
11 KiB
383 lines
11 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Medic's resupply beacon |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
|
|
#include "tf_obj_resupply.h" |
|
#include "engine/IEngineSound.h" |
|
#include "tf_player.h" |
|
#include "tf_team.h" |
|
#include "VGuiScreen.h" |
|
#include "world.h" |
|
|
|
#define RESUPPLY_HEAL_AMT 100 |
|
#define RESUPPLY_AMMO_AMT 0.25f |
|
|
|
// Wall mounted version |
|
#define RESUPPLY_WALL_MODEL "models/objects/obj_resupply.mdl" |
|
#define RESUPPLY_WALL_MODEL_ALIEN "models/objects/alien_obj_resupply.mdl" |
|
#define RESUPPLY_WALL_MINS Vector(-10, -10, -40) |
|
#define RESUPPLY_WALL_MAXS Vector( 10, 10, 40) |
|
|
|
// Ground placed version |
|
#define RESUPPLY_GROUND_MODEL "models/objects/obj_resupply_ground.mdl" |
|
#define RESUPPLY_GROUND_MODEL_HUMAN "models/objects/human_obj_resupply_ground.mdl" |
|
#define RESUPPLY_GROUND_MINS Vector(-20, -20, 0) |
|
#define RESUPPLY_GROUND_MAXS Vector( 20, 20, 55) |
|
|
|
IMPLEMENT_SERVERCLASS_ST( CObjectResupply, DT_ObjectResupply ) |
|
END_SEND_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS(obj_resupply, CObjectResupply); |
|
PRECACHE_REGISTER(obj_resupply); |
|
|
|
ConVar obj_resupply_health( "obj_resupply_health","100", FCVAR_NONE, "Resupply Station health" ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CObjectResupply::CObjectResupply() |
|
{ |
|
m_iHealth = obj_resupply_health.GetInt(); |
|
UseClientSideAnimation(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectResupply::Spawn() |
|
{ |
|
SetModel( RESUPPLY_WALL_MODEL ); |
|
SetSolid( SOLID_BBOX ); |
|
|
|
UTIL_SetSize(this, RESUPPLY_WALL_MINS, RESUPPLY_WALL_MAXS); |
|
m_takedamage = DAMAGE_YES; |
|
|
|
SetType( OBJ_RESUPPLY ); |
|
m_fObjectFlags |= OF_DONT_PREVENT_BUILD_NEAR_OBJ; |
|
|
|
BaseClass::Spawn(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Spawn the vgui control screens on the object |
|
//----------------------------------------------------------------------------- |
|
void CObjectResupply::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) |
|
{ |
|
pPanelName = "screen_obj_resupply"; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectResupply::Precache() |
|
{ |
|
BaseClass::Precache(); |
|
PrecacheModel( RESUPPLY_WALL_MODEL ); |
|
PrecacheModel( RESUPPLY_WALL_MODEL_ALIEN ); |
|
PrecacheModel( RESUPPLY_GROUND_MODEL ); |
|
PrecacheModel( RESUPPLY_GROUND_MODEL_HUMAN ); |
|
PrecacheVGuiScreen( "screen_obj_resupply" ); |
|
|
|
PrecacheScriptSound( "ObjectResupply.InsufficientFunds" ); |
|
PrecacheScriptSound( "BaseCombatCharacter.AmmoPickup" ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Resupply Health |
|
//----------------------------------------------------------------------------- |
|
bool CObjectResupply::ResupplyHealth( CBaseTFPlayer *pPlayer, float flFraction ) |
|
{ |
|
// Calculate the amount to heal |
|
float flAmountToHeal = flFraction * RESUPPLY_HEAL_AMT; |
|
if (flAmountToHeal > (pPlayer->m_iMaxHealth - pPlayer->m_iHealth)) |
|
{ |
|
flAmountToHeal = (pPlayer->m_iMaxHealth - pPlayer->m_iHealth); |
|
} |
|
|
|
if ( flAmountToHeal > 0 ) |
|
{ |
|
pPlayer->TakeHealth( flAmountToHeal, 0 ); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Handle commands sent from vgui panels on the client |
|
//----------------------------------------------------------------------------- |
|
bool CObjectResupply::ClientCommand( CBaseTFPlayer *pPlayer, const char *pCmd, ICommandArguments *pArg ) |
|
{ |
|
// NOTE: Must match ResupplyBuyType_t |
|
static float s_Costs[] = |
|
{ |
|
RESUPPLY_AMMO_COST, |
|
RESUPPLY_HEALTH_COST, |
|
RESUPPLY_GRENADES_COST, |
|
RESUPPLY_ALL_COST |
|
}; |
|
|
|
COMPILE_TIME_ASSERT( RESUPPLY_BUY_TYPE_COUNT == 4 ); |
|
|
|
if ( FStrEq( pCmd, "buy" ) ) |
|
{ |
|
if ( pArg->Argc() < 2 ) |
|
return true; |
|
|
|
// I can't do anything if I'm not active |
|
if ( !ShouldBeActive() ) |
|
return true; |
|
|
|
// Do we have enough resources to activate it? |
|
if (pPlayer->GetBankResources() <= 0) |
|
{ |
|
// Play a sound indicating it didn't work... |
|
CSingleUserRecipientFilter filter( pPlayer ); |
|
EmitSound( filter, pPlayer->entindex(), "ObjectResupply.InsufficientFunds" ); |
|
return true; |
|
} |
|
|
|
bool bUsedResupply = false; |
|
ResupplyBuyType_t type = (ResupplyBuyType_t)atoi( pArg->Argv(1) ); |
|
if (type >= RESUPPLY_BUY_TYPE_COUNT) |
|
return true; |
|
|
|
// Get the potential cost. |
|
float flCost = s_Costs[type]; |
|
// flCost += pPlayer->ClassCostAdjustment( type ); |
|
|
|
float flFraction = pPlayer->GetBankResources() / flCost; |
|
if (flFraction > 1.0f) |
|
flFraction = 1.0f; |
|
|
|
switch( type ) |
|
{ |
|
case RESUPPLY_BUY_HEALTH: |
|
// Calculate the amount to heal |
|
if (ResupplyHealth(pPlayer, flFraction)) |
|
{ |
|
bUsedResupply = true; |
|
} |
|
break; |
|
|
|
case RESUPPLY_BUY_AMMO: |
|
// Refill the player's ammo too |
|
if (pPlayer->ResupplyAmmo( flFraction * RESUPPLY_AMMO_AMT, RESUPPLY_AMMO_FROM_STATION )) |
|
{ |
|
bUsedResupply = true; |
|
} |
|
break; |
|
|
|
case RESUPPLY_BUY_GRENADES: |
|
// Refill the player's ammo too |
|
if (pPlayer->ResupplyAmmo( flFraction * RESUPPLY_AMMO_AMT, RESUPPLY_GRENADES_FROM_STATION )) |
|
{ |
|
bUsedResupply = true; |
|
} |
|
break; |
|
|
|
case RESUPPLY_BUY_ALL: |
|
// Calculate the amount to heal |
|
if (ResupplyHealth(pPlayer, flFraction)) |
|
{ |
|
bUsedResupply = true; |
|
} |
|
|
|
// Refill the player's ammo too |
|
if (pPlayer->ResupplyAmmo( flFraction * RESUPPLY_AMMO_AMT, RESUPPLY_ALL_FROM_STATION )) |
|
{ |
|
bUsedResupply = true; |
|
} |
|
break; |
|
} |
|
|
|
if (bUsedResupply) |
|
{ |
|
// Play an ammo pickup just to this player |
|
CSingleUserRecipientFilter filter( pPlayer ); |
|
pPlayer->EmitSound( filter, pPlayer->entindex(), "BaseCombatCharacter.AmmoPickup" ); |
|
|
|
pPlayer->RemoveBankResources( flFraction * flCost ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
return BaseClass::ClientCommand( pPlayer, pCmd, pArg ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectResupply::DestroyObject( void ) |
|
{ |
|
if ( GetTeam() ) |
|
{ |
|
((CTFTeam*)GetTeam())->RemoveResupply( this ); |
|
} |
|
BaseClass::DestroyObject(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pTeam - |
|
//----------------------------------------------------------------------------- |
|
void CObjectResupply::ChangeTeam( int iTeamNum ) |
|
{ |
|
CTFTeam *pExisting = (CTFTeam*)GetTeam(); |
|
CTFTeam *pTeam = (CTFTeam*)GetGlobalTeam( iTeamNum ); |
|
|
|
// Already on this team |
|
if ( GetTeamNumber() == iTeamNum ) |
|
return; |
|
|
|
if ( pExisting ) |
|
{ |
|
// Remove it from current team ( if it's in one ) and give it to new team |
|
pExisting->RemoveResupply( this ); |
|
} |
|
|
|
// Change to new team |
|
BaseClass::ChangeTeam( iTeamNum ); |
|
|
|
// Add this object to the team's list |
|
if (pTeam) |
|
{ |
|
pTeam->AddResupply( this ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Resupply always wants to use the wall mount for attachment points |
|
//----------------------------------------------------------------------------- |
|
void CObjectResupply::SetupAttachedVersion( void ) |
|
{ |
|
BaseClass::SetupAttachedVersion(); |
|
|
|
if ( GetTeamNumber() == TEAM_ALIENS ) |
|
{ |
|
SetModel( RESUPPLY_WALL_MODEL_ALIEN ); |
|
} |
|
else |
|
{ |
|
SetModel( RESUPPLY_WALL_MODEL ); |
|
} |
|
|
|
UTIL_SetSize(this, RESUPPLY_WALL_MINS, RESUPPLY_WALL_MAXS); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CObjectResupply::CalculatePlacement( CBaseTFPlayer *pPlayer ) |
|
{ |
|
trace_t tr; |
|
Vector vecAiming; |
|
// Get an aim vector. Don't use GetAimVector() because we don't want autoaiming. |
|
Vector vecSrc = pPlayer->Weapon_ShootPosition( ); |
|
pPlayer->EyeVectors( &vecAiming ); |
|
Vector vecTarget; |
|
VectorMA( vecSrc, 90, vecAiming, vecTarget ); |
|
m_vecBuildOrigin = vecTarget; |
|
|
|
// Angle it towards me |
|
Vector vecForward = pPlayer->WorldSpaceCenter() - m_vecBuildOrigin; |
|
SetLocalAngles( QAngle( 0, UTIL_VecToYaw( vecForward ), 0 ) ); |
|
|
|
// Is there something to attach to? |
|
// Use my bounding box, not the build box, so I fit to the wall |
|
UTIL_TraceLine( vecSrc, vecTarget, MASK_SOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr); |
|
//UTIL_TraceHull( vecSrc, vecTarget, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, pPlayer, TFCOLLISION_GROUP_OBJECT, &tr ); |
|
m_vecBuildOrigin = tr.endpos; |
|
bool bTryToPlaceGroundVersion = false; |
|
if ( tr.allsolid || (tr.fraction == 1.0) ) |
|
{ |
|
bTryToPlaceGroundVersion = true; |
|
} |
|
else |
|
{ |
|
// Make sure we're planting on the world |
|
CBaseEntity *pEntity = tr.m_pEnt; |
|
if ( pEntity != GetWorldEntity() ) |
|
{ |
|
bTryToPlaceGroundVersion = true; |
|
} |
|
} |
|
|
|
// Make sure the wall we've touched is vertical |
|
if ( !bTryToPlaceGroundVersion && fabs(tr.plane.normal.z) > 0.3 ) |
|
{ |
|
bTryToPlaceGroundVersion = true; |
|
} |
|
|
|
// Aborting? |
|
if ( bTryToPlaceGroundVersion ) |
|
{ |
|
// We couldn't find a wall, so try and place a ground version instead |
|
if ( GetTeamNumber() == TEAM_HUMANS ) |
|
{ |
|
SetModel( RESUPPLY_GROUND_MODEL_HUMAN ); |
|
} |
|
else |
|
{ |
|
SetModel( RESUPPLY_GROUND_MODEL ); |
|
} |
|
UTIL_SetSize(this, RESUPPLY_GROUND_MINS, RESUPPLY_GROUND_MAXS); |
|
m_vecBuildMins = WorldAlignMins() - Vector( 4,4,0 ); |
|
m_vecBuildMaxs = WorldAlignMaxs() + Vector( 4,4,0 ); |
|
return BaseClass::CalculatePlacement( pPlayer ); |
|
} |
|
|
|
SetupAttachedVersion(); |
|
m_vecBuildMins = WorldAlignMins() - Vector( 4,4,0 ); |
|
m_vecBuildMaxs = WorldAlignMaxs() + Vector( 4,4,0 ); |
|
|
|
// Set the angles |
|
vecForward = tr.plane.normal; |
|
SetLocalAngles( QAngle( 0, UTIL_VecToYaw( vecForward ), 0 ) ); |
|
|
|
// Trace back from the corners |
|
Vector vecMins, vecMaxs, vecModelMins, vecModelMaxs; |
|
const model_t *pModel = GetModel(); |
|
modelinfo->GetModelBounds( pModel, vecModelMins, vecModelMaxs ); |
|
|
|
// Check the four build points |
|
Vector vecPointCheck = (vecForward * 32); |
|
Vector vecUp = Vector(0,0,1); |
|
Vector vecRight; |
|
CrossProduct( vecUp, vecForward, vecRight ); |
|
float flWidth = fabs(vecModelMaxs.y - vecModelMins.y) * 0.5; |
|
float flHeight = fabs(vecModelMaxs.z - vecModelMins.z) * 0.5; |
|
|
|
bool bResult = true; |
|
if ( bResult ) |
|
{ |
|
bResult = CheckBuildPoint( m_vecBuildOrigin + (vecRight * flWidth) + (vecUp * flHeight), vecPointCheck ); |
|
} |
|
if ( bResult ) |
|
{ |
|
bResult = CheckBuildPoint( m_vecBuildOrigin + (vecRight * flWidth) - (vecUp * flHeight), vecPointCheck ); |
|
} |
|
if ( bResult ) |
|
{ |
|
bResult = CheckBuildPoint( m_vecBuildOrigin - (vecRight * flWidth) + (vecUp * flHeight), vecPointCheck ); |
|
} |
|
if ( bResult ) |
|
{ |
|
bResult = CheckBuildPoint( m_vecBuildOrigin - (vecRight * flWidth) - (vecUp * flHeight), vecPointCheck ); |
|
} |
|
|
|
AttemptToFindPower(); |
|
|
|
return bResult; |
|
}
|
|
|