1413 lines
45 KiB
C++
1413 lines
45 KiB
C++
#include "cbase.h"
|
|
#include "asw_shareddefs.h"
|
|
#include "gamestringpool.h"
|
|
#ifdef GAME_DLL
|
|
#include "asw_player.h"
|
|
#include "asw_marine.h"
|
|
#include "asw_marine_resource.h"
|
|
#include "asw_gamerules.h"
|
|
#include "asw_game_resource.h"
|
|
#include "props.h"
|
|
#include "vphysics_interface.h"
|
|
#include "physics.h"
|
|
#include "vphysics/friction.h"
|
|
#include "asw_computer_area.h"
|
|
#include "point_camera.h"
|
|
#include "asw_remote_turret_shared.h"
|
|
#include "asw_computer_area.h"
|
|
#include "asw_button_area.h"
|
|
#include "fogcontroller.h"
|
|
#include "asw_point_camera.h"
|
|
#else
|
|
#include "asw_gamerules.h"
|
|
#include "c_asw_drone_advanced.h"
|
|
#include "c_asw_fx.h"
|
|
#include "c_asw_marine.h"
|
|
#include "c_asw_game_resource.h"
|
|
#include "c_asw_marine_resource.h"
|
|
#include "c_asw_computer_area.h"
|
|
#include "c_point_camera.h"
|
|
#include "asw_remote_turret_shared.h"
|
|
#include "c_asw_player.h"
|
|
#include "c_playerresource.h"
|
|
#include "c_asw_computer_area.h"
|
|
#include "c_asw_button_area.h"
|
|
#include "vgui/cursor.h"
|
|
#include "iinput.h"
|
|
#include <vgui/ISurface.h>
|
|
#include "vguimatsurface/imatsystemsurface.h"
|
|
#include "vgui_controls\Controls.h"
|
|
#include <vgui/IVGUI.h>
|
|
#include "ivieweffects.h"
|
|
#include "asw_input.h"
|
|
#include "c_asw_point_camera.h"
|
|
#include "baseparticleentity.h"
|
|
#include "vgui/ILocalize.h"
|
|
#include "asw_hud_floating_number.h"
|
|
#include "takedamageinfo.h"
|
|
#include "clientmode_asw.h"
|
|
#include "engine/IVDebugOverlay.h"
|
|
#include "c_user_message_register.h"
|
|
#include "prediction.h"
|
|
#define CASW_Marine C_ASW_Marine
|
|
#define CASW_Game_Resource C_ASW_Game_Resource
|
|
#define CASW_Marine_Resource C_ASW_Marine_Resource
|
|
#define CASW_Computer_Area C_ASW_Computer_Area
|
|
#define CPointCamera C_PointCamera
|
|
#define CASW_PointCamera C_ASW_PointCamera
|
|
#define CASW_Remote_Turret C_ASW_Remote_Turret
|
|
#define CASW_Button_Area C_ASW_Button_Area
|
|
#define CASW_Computer_Area C_ASW_Computer_Area
|
|
#endif
|
|
#include "shake.h"
|
|
#include "asw_util_shared.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#ifndef CLIENT_DLL
|
|
ConVar asw_debug_marine_can_see("asw_debug_marine_can_see", "0", FCVAR_CHEAT, "Display lines for waking up aliens");
|
|
#else
|
|
extern int g_asw_iGUIWindowsOpen;
|
|
#endif
|
|
|
|
ConVar asw_marine_view_cone_dist("asw_marine_view_cone_dist", "700", FCVAR_REPLICATED, "Distance for marine view cone checks");
|
|
ConVar asw_marine_view_cone_dot("asw_marine_view_cone_dot", "0.5", FCVAR_REPLICATED, "Dot for marine view cone checks");
|
|
extern ConVar asw_rts_controls;
|
|
|
|
|
|
ConVar asw_shake_test_punch_dirx("asw_shake_test_punch_dirx","0", FCVAR_REPLICATED|FCVAR_HIDDEN );
|
|
ConVar asw_shake_test_punch_diry("asw_shake_test_punch_diry","0", FCVAR_REPLICATED|FCVAR_HIDDEN );
|
|
ConVar asw_shake_test_punch_dirz("asw_shake_test_punch_dirz","1", FCVAR_REPLICATED|FCVAR_HIDDEN );
|
|
ConVar asw_shake_test_punch_freq("asw_shake_test_punch_freq","1.5", FCVAR_REPLICATED|FCVAR_HIDDEN );
|
|
ConVar asw_shake_test_punch_amp("asw_shake_test_punch_amp","60", FCVAR_REPLICATED|FCVAR_HIDDEN );
|
|
ConVar asw_shake_test_punch_dura("asw_shake_test_punch_dura","0.75", FCVAR_REPLICATED|FCVAR_HIDDEN );
|
|
|
|
// rotates one angle towards another, with a fixed turning rate over the time
|
|
float ASW_ClampYaw( float yawSpeedPerSec, float current, float target, float time )
|
|
{
|
|
if (current != target)
|
|
{
|
|
float speed = yawSpeedPerSec * time;
|
|
float move = target - current;
|
|
|
|
if (target > current)
|
|
{
|
|
if (move >= 180)
|
|
move = move - 360;
|
|
}
|
|
else
|
|
{
|
|
if (move <= -180)
|
|
move = move + 360;
|
|
}
|
|
|
|
if (move > 0)
|
|
{// turning to the npc's left
|
|
if (move > speed)
|
|
move = speed;
|
|
}
|
|
else
|
|
{// turning to the npc's right
|
|
if (move < -speed)
|
|
move = -speed;
|
|
}
|
|
|
|
return anglemod(current + move);
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
float ASW_Linear_Approach( float current, float target, float delta)
|
|
{
|
|
if (current < target)
|
|
current = MIN(current + delta, target);
|
|
else if (current > target)
|
|
current = MAX(current - delta, target);
|
|
|
|
return current;
|
|
}
|
|
|
|
// time independent movement of one angle to a fraction of the desired
|
|
float ASW_ClampYaw_Fraction( float fraction, float current, float target, float time )
|
|
{
|
|
if (current != target)
|
|
{
|
|
float move = target - current;
|
|
|
|
if (target > current)
|
|
{
|
|
if (move >= 180)
|
|
move = move - 360;
|
|
}
|
|
else
|
|
{
|
|
if (move <= -180)
|
|
move = move + 360;
|
|
}
|
|
move = move * pow(fraction, time);
|
|
float r = anglemod(target - move);
|
|
|
|
return r;
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
bool ASW_LineCircleIntersection(
|
|
const Vector2D ¢er,
|
|
const float radius,
|
|
const Vector2D &vLinePt,
|
|
const Vector2D &vLineDir,
|
|
float *fIntersection1,
|
|
float *fIntersection2)
|
|
{
|
|
// Line = P + Vt
|
|
// Sphere = r (assume we've translated to origin)
|
|
// (P + Vt)^2 = r^2
|
|
// VVt^2 + 2PVt + (PP - r^2)
|
|
// Solve as quadratic: (-b +/- sqrt(b^2 - 4ac)) / 2a
|
|
// If (b^2 - 4ac) is < 0 there is no solution.
|
|
// If (b^2 - 4ac) is = 0 there is one solution (a case this function doesn't support).
|
|
// If (b^2 - 4ac) is > 0 there are two solutions.
|
|
Vector2D P;
|
|
float a, b, c, sqr, insideSqr;
|
|
|
|
|
|
// Translate circle to origin.
|
|
P[0] = vLinePt[0] - center[0];
|
|
P[1] = vLinePt[1] - center[1];
|
|
|
|
a = vLineDir.Dot(vLineDir);
|
|
b = 2.0f * P.Dot(vLineDir);
|
|
c = P.Dot(P) - (radius * radius);
|
|
|
|
insideSqr = b*b - 4*a*c;
|
|
if(insideSqr <= 0.000001f)
|
|
return false;
|
|
|
|
// Ok, two solutions.
|
|
sqr = (float)FastSqrt(insideSqr);
|
|
|
|
float denom = 1.0 / (2.0f * a);
|
|
|
|
*fIntersection1 = (-b - sqr) * denom;
|
|
*fIntersection2 = (-b + sqr) * denom;
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
// a local helper to normalize some code below -- gets inlined
|
|
static void ASW_WriteScreenShakeToMessage( CBasePlayer *pPlayer, ShakeCommand_t eCommand, float amplitude, float frequency, float duration, const Vector &direction )
|
|
{
|
|
CSingleUserRecipientFilter user( pPlayer );
|
|
user.MakeReliable();
|
|
if ( direction.IsZeroFast() ) // nondirectional shake
|
|
{
|
|
UserMessageBegin( user, "Shake" );
|
|
WRITE_BYTE( eCommand ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
|
|
WRITE_FLOAT( amplitude ); // shake magnitude/amplitude
|
|
WRITE_FLOAT( frequency ); // shake noise frequency
|
|
WRITE_FLOAT( duration ); // shake lasts this long
|
|
MessageEnd();
|
|
}
|
|
else // directional shake
|
|
{
|
|
UserMessageBegin( user, "ShakeDir" );
|
|
WRITE_BYTE( eCommand ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
|
|
WRITE_FLOAT( amplitude ); // shake magnitude/amplitude
|
|
WRITE_FLOAT( frequency ); // shake noise frequency
|
|
WRITE_FLOAT( duration ); // shake lasts this long
|
|
WRITE_VEC3NORMAL( direction );
|
|
MessageEnd();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Transmits the actual shake event
|
|
//-----------------------------------------------------------------------------
|
|
void ASW_TransmitShakeEvent( CBasePlayer *pPlayer, float localAmplitude, float frequency, float duration, ShakeCommand_t eCommand, const Vector &direction )
|
|
{
|
|
if (( localAmplitude > 0 ) || ( eCommand == SHAKE_STOP ))
|
|
{
|
|
if ( eCommand == SHAKE_STOP )
|
|
localAmplitude = 0;
|
|
|
|
#ifdef GAME_DLL
|
|
ASW_WriteScreenShakeToMessage( pPlayer, eCommand, localAmplitude, frequency, duration, direction );
|
|
#else
|
|
ScreenShake_t shake;
|
|
|
|
shake.command = eCommand;
|
|
shake.amplitude = localAmplitude;
|
|
shake.frequency = frequency;
|
|
shake.duration = duration;
|
|
shake.direction = direction;
|
|
|
|
ASW_TransmitShakeEvent( pPlayer, shake );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void ASW_TransmitShakeEvent( CBasePlayer *pPlayer, const ScreenShake_t &shake )
|
|
{
|
|
if ( shake.command == SHAKE_STOP && shake.amplitude != 0 )
|
|
{
|
|
// create a corrected screenshake and recursively call myself
|
|
AssertMsg1( false, "A ScreenShake_t had a SHAKE_STOP command but a nonzero amplitude %.1f; this is meaningless.\n", shake.amplitude);
|
|
ScreenShake_t localShake = shake;
|
|
localShake.amplitude = 0;
|
|
ASW_TransmitShakeEvent( pPlayer, localShake );
|
|
}
|
|
#ifdef GAME_DLL
|
|
ASW_WriteScreenShakeToMessage( pPlayer, shake.command, shake.amplitude, shake.frequency, shake.duration, shake.direction );
|
|
#else
|
|
if ( !( prediction && prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) )
|
|
{
|
|
GetViewEffects()->Shake( shake );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// Compute shake amplitude
|
|
//-----------------------------------------------------------------------------
|
|
inline float ASW_ComputeShakeAmplitude( const Vector ¢er, const Vector &shakePt, float amplitude, float radius )
|
|
{
|
|
if ( radius <= 0 )
|
|
return amplitude;
|
|
|
|
float localAmplitude = -1;
|
|
Vector delta = center - shakePt;
|
|
float distance = delta.Length();
|
|
|
|
if ( distance <= radius )
|
|
{
|
|
// Make the amplitude fall off over distance
|
|
float flPerc = 1.0 - (distance / radius);
|
|
localAmplitude = amplitude * flPerc;
|
|
}
|
|
|
|
return localAmplitude;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Shake the screen of all clients within radius.
|
|
// radius == 0, shake all clients
|
|
// UNDONE: Fix falloff model (disabled)?
|
|
// UNDONE: Affect user controls?
|
|
// Input : center - Center of screen shake, radius is measured from here.
|
|
// amplitude - Amplitude of shake
|
|
// frequency -
|
|
// duration - duration of shake in seconds.
|
|
// radius - Radius of effect, 0 shakes all clients.
|
|
// command - One of the following values:
|
|
// SHAKE_START - starts the screen shake for all players within the radius
|
|
// SHAKE_STOP - stops the screen shake for all players within the radius
|
|
// SHAKE_AMPLITUDE - modifies the amplitude of the screen shake
|
|
// for all players within the radius
|
|
// SHAKE_FREQUENCY - modifies the frequency of the screen shake
|
|
// for all players within the radius
|
|
// bAirShake - completely ignored
|
|
//-----------------------------------------------------------------------------
|
|
const float ASW_MAX_SHAKE_AMPLITUDE = 16.0f;
|
|
void UTIL_ASW_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake )
|
|
{
|
|
int i;
|
|
float localAmplitude;
|
|
|
|
if ( amplitude > ASW_MAX_SHAKE_AMPLITUDE )
|
|
{
|
|
amplitude = ASW_MAX_SHAKE_AMPLITUDE;
|
|
}
|
|
for ( i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
//
|
|
// Only start shakes for players that are on the ground unless doing an air shake.
|
|
//
|
|
if ( !pPlayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// find the player's marine
|
|
CASW_Player *pASWPlayer = dynamic_cast<CASW_Player*>(pPlayer);
|
|
if (!pASWPlayer || !pASWPlayer->GetMarine())
|
|
continue;
|
|
|
|
Vector vecMarinePos = pASWPlayer->GetMarine()->WorldSpaceCenter();
|
|
if (pASWPlayer->GetMarine()->IsControllingTurret() && pASWPlayer->GetMarine()->GetRemoteTurret())
|
|
vecMarinePos = pASWPlayer->GetMarine()->GetRemoteTurret()->GetAbsOrigin();
|
|
|
|
localAmplitude = ASW_ComputeShakeAmplitude( center, vecMarinePos, amplitude, radius );
|
|
|
|
// This happens if the player is outside the radius, in which case we should ignore
|
|
// all commands
|
|
if (localAmplitude < 0)
|
|
continue;
|
|
|
|
ASW_TransmitShakeEvent( (CBasePlayer *)pPlayer, localAmplitude, frequency, duration, eCommand );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Perform a directional "punch" on the screen of all clients within radius.
|
|
// radius == 0, shake all clients
|
|
// Input : center - Center of screen shake, radius is measured from here.
|
|
// direction - (world space) direction in which to punch camera. punching down makes it look like the world is moving up. must be normal.
|
|
// amplitude - Amplitude of shake, in world units for the camera
|
|
// frequency - controls number of bounces before shake settles; a frequency of 1 means three peaks (forward, back, little forward, settle)
|
|
// duration - duration of shake in seconds.
|
|
// radius - Radius of effect, 0 shakes all clients.
|
|
//-----------------------------------------------------------------------------
|
|
void UTIL_ASW_ScreenPunch( const Vector ¢er, const Vector &direction, float amplitude, float frequency, float duration, float radius )
|
|
{
|
|
ScreenShake_t shake;
|
|
shake.command = SHAKE_START;
|
|
shake.direction = direction;
|
|
shake.amplitude = amplitude;
|
|
shake.frequency = frequency;
|
|
shake.duration = duration;
|
|
|
|
UTIL_ASW_ScreenPunch( center, radius, shake );
|
|
}
|
|
|
|
void UTIL_ASW_ScreenPunch( const Vector ¢er, float radius, const ScreenShake_t &shake )
|
|
{
|
|
int i;
|
|
const float radiusSqr = radius * radius;
|
|
|
|
AssertMsg( CloseEnough(shake.direction.LengthSqr(), 1), "Direction param to ASW_ScreenPunch is abnormal\n" );
|
|
|
|
for ( i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
//
|
|
// Only start shakes for players that are on the ground unless doing an air shake.
|
|
//
|
|
if ( !pPlayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// find the player's marine
|
|
CASW_Player *pASWPlayer = assert_cast<CASW_Player*>(pPlayer);
|
|
if (!pASWPlayer || !pASWPlayer->GetMarine())
|
|
continue;
|
|
|
|
Vector vecMarinePos = pASWPlayer->GetMarine()->WorldSpaceCenter();
|
|
if (pASWPlayer->GetMarine()->IsControllingTurret() && pASWPlayer->GetMarine()->GetRemoteTurret())
|
|
vecMarinePos = pASWPlayer->GetMarine()->GetRemoteTurret()->GetAbsOrigin();
|
|
|
|
if ( vecMarinePos.DistToSqr(center) > radiusSqr )
|
|
continue;
|
|
|
|
ASW_TransmitShakeEvent( (CBasePlayer *)pPlayer, shake );
|
|
}
|
|
}
|
|
|
|
|
|
// returns the nearest marine to this point
|
|
CASW_Marine* UTIL_ASW_NearestMarine( const Vector &pos, float &marine_distance, ASW_Marine_Class marineClass, bool bAIOnly )
|
|
{
|
|
// check through all marines, finding the closest that we're aware of
|
|
CASW_Game_Resource* pGameResource = ASWGameResource();
|
|
float distance = 0.0f;
|
|
marine_distance = -1.0f;
|
|
CASW_Marine *pNearest = NULL;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
|
|
if (pMR!=NULL && pMR->GetMarineEntity()!=NULL && pMR->GetMarineEntity()->GetHealth() > 0)
|
|
{
|
|
if ( bAIOnly && pMR->IsInhabited() )
|
|
continue;
|
|
if ( marineClass != MARINE_CLASS_UNDEFINED && pMR->GetProfile() && pMR->GetProfile()->GetMarineClass() != marineClass )
|
|
continue;
|
|
|
|
distance = pMR->GetMarineEntity()->GetAbsOrigin().DistTo(pos);
|
|
if (marine_distance == -1.0f || distance < marine_distance)
|
|
{
|
|
marine_distance = distance;
|
|
pNearest = pMR->GetMarineEntity();
|
|
}
|
|
}
|
|
}
|
|
return pNearest;
|
|
}
|
|
|
|
CASW_Marine* UTIL_ASW_NearestMarine( const CASW_Marine *pMarine, float &marine_distance )
|
|
{
|
|
// check through all marines, finding the closest that we're aware of
|
|
CASW_Game_Resource* pGameResource = ASWGameResource();
|
|
float distance = 0;
|
|
marine_distance = -1.0f;
|
|
CASW_Marine *pNearest = NULL;
|
|
for ( int i = 0; i < pGameResource->GetMaxMarineResources(); i++ )
|
|
{
|
|
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
|
|
if ( pMR != NULL && pMR->GetMarineEntity() != NULL && pMR->GetMarineEntity() != pMarine && pMR->GetMarineEntity()->GetHealth() > 0 )
|
|
{
|
|
distance = pMR->GetMarineEntity()->GetAbsOrigin().DistTo( pMarine->GetAbsOrigin() );
|
|
if ( marine_distance == -1.0f || distance < marine_distance )
|
|
{
|
|
marine_distance = distance;
|
|
pNearest = pMR->GetMarineEntity();
|
|
}
|
|
}
|
|
}
|
|
return pNearest;
|
|
}
|
|
|
|
int UTIL_ASW_NumCommandedMarines( const CASW_Player *pPlayer )
|
|
{
|
|
int nNumMarines = 0;
|
|
|
|
// check through all marines
|
|
CASW_Game_Resource* pGameResource = ASWGameResource();
|
|
for ( int i = 0; i < pGameResource->GetMaxMarineResources(); i++ )
|
|
{
|
|
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
|
|
if ( pMR && pMR->GetMarineEntity() && pMR->GetMarineEntity()->GetHealth() > 0 )
|
|
{
|
|
if ( pMR->GetMarineEntity()->GetCommander() == pPlayer )
|
|
{
|
|
nNumMarines++;
|
|
}
|
|
}
|
|
}
|
|
return nNumMarines;
|
|
}
|
|
|
|
#else
|
|
// make a specific clientside entity gib
|
|
bool UTIL_ASW_ClientsideGib(C_BaseAnimating* pEnt)
|
|
{
|
|
if (!pEnt)
|
|
return false;
|
|
C_BaseAnimating* pAnimating = dynamic_cast<C_BaseAnimating*>(pEnt);
|
|
if (!pAnimating)
|
|
return false;
|
|
if (!stricmp(STRING(pAnimating->GetModelName()), SWARM_DRONE_MODEL))
|
|
{
|
|
Vector vMins, vMaxs, vGibOrigin, vGibVelocity(0,0,1);
|
|
if ( pEnt->m_pRagdoll )
|
|
{
|
|
pEnt->m_pRagdoll->GetRagdollBounds( vMins, vMaxs );
|
|
vGibOrigin =pEnt->m_pRagdoll->GetRagdollOrigin() + ( ( vMins + vMaxs ) / 2.0f );
|
|
pEnt->m_pRagdoll->GetElement(0)->GetVelocity( &vGibVelocity, NULL );
|
|
}
|
|
else
|
|
{
|
|
vGibOrigin = pEnt->WorldSpaceCenter();
|
|
}
|
|
|
|
FX_DroneGib( vGibOrigin, Vector(0,0,1), 0.5f, pAnimating->GetSkin(), pEnt->IsOnFire() );
|
|
return true;
|
|
}
|
|
else if (!stricmp(STRING(pAnimating->GetModelName()), SWARM_HARVESTER_MODEL))
|
|
{
|
|
FX_HarvesterGib( pEnt->WorldSpaceCenter(), Vector(0,0,1), 0.5f, pAnimating->GetSkin(), pEnt->IsOnFire() );
|
|
return true;
|
|
}
|
|
else if (!stricmp(STRING(pAnimating->GetModelName()), SWARM_SHIELDBUG_MODEL))
|
|
{
|
|
FX_HarvesterGib( pEnt->WorldSpaceCenter(), Vector(0,0,1), 0.5f, 1, pEnt->IsOnFire() );
|
|
return true;
|
|
}
|
|
// todo: code to gib other types of things clientside?
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
//void UTIL_ASW_ValidateSoundName( string_t &name, const char *defaultStr )
|
|
void UTIL_ASW_ValidateSoundName( char *szString, int stringlength, const char *defaultStr )
|
|
{
|
|
Assert(szString);
|
|
if (szString[0] == '\0')
|
|
{
|
|
Q_snprintf(szString, stringlength, "%s", defaultStr);
|
|
}
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
void UTIL_ASW_PoisonBlur(CBaseEntity *pEntity, float duration)
|
|
{
|
|
if ( !pEntity || !pEntity->IsNetClient() )
|
|
return;
|
|
|
|
CSingleUserRecipientFilter user( (CBasePlayer *)pEntity );
|
|
user.MakeReliable();
|
|
|
|
UserMessageBegin( user, "ASWBlur" ); // use the magic #1 for "one client"
|
|
WRITE_SHORT( (int) (duration * 10.0f) ); // blur lasts this long / 10
|
|
MessageEnd();
|
|
}
|
|
|
|
// tests if a particular entity is blocking any marines (used by phys props to see if they should leave pushaway mode)
|
|
bool UTIL_ASW_BlockingMarine( CBaseEntity *pEntity )
|
|
{
|
|
CASW_Game_Resource* pGameResource = ASWGameResource();
|
|
if (!pGameResource)
|
|
return false;
|
|
|
|
int iCurrentGroup = pEntity->GetCollisionGroup();
|
|
pEntity->SetCollisionGroup(COLLISION_GROUP_NONE);
|
|
|
|
bool bBlockedMarine = false;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource* pMR = pGameResource->GetMarineResource(i);
|
|
if (pMR!=NULL && pMR->GetMarineEntity()!=NULL && pMR->GetMarineEntity()->GetHealth() > 0)
|
|
{
|
|
CASW_Marine *pMarine = pMR->GetMarineEntity();
|
|
// check if this marine's bounding box trace collides with the specified entity
|
|
Ray_t ray;
|
|
trace_t tr;
|
|
ray.Init( pMarine->GetAbsOrigin(), pMarine->GetAbsOrigin() - Vector(0,0,1),
|
|
pMarine->CollisionProp()->OBBMins(), pMarine->CollisionProp()->OBBMaxs() );
|
|
if (pEntity->TestCollision( ray, MASK_PLAYERSOLID, tr ))
|
|
{
|
|
bBlockedMarine = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pEntity->SetCollisionGroup(iCurrentGroup);
|
|
|
|
return bBlockedMarine;
|
|
}
|
|
|
|
CASW_Marine* UTIL_ASW_Marine_Can_Chatter_Spot(CBaseEntity *pEntity, float fDist)
|
|
{
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
if (!pGameResource)
|
|
return NULL;
|
|
|
|
// find how many marines can see us
|
|
int iFound = 0;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource* pMarineResource = pGameResource->GetMarineResource(i);
|
|
if (!pMarineResource)
|
|
continue;
|
|
|
|
CASW_Marine *pMarine = pMarineResource->GetMarineEntity();
|
|
if (!pMarine)
|
|
continue;
|
|
|
|
if (pMarine->GetAbsOrigin().DistTo(pEntity->GetAbsOrigin()) < fDist)
|
|
{
|
|
Vector vecFacing;
|
|
AngleVectors(pMarine->GetAbsAngles(), &vecFacing);
|
|
Vector vecDir = pEntity->GetAbsOrigin() - pMarine->GetAbsOrigin();
|
|
vecDir.NormalizeInPlace();
|
|
if (vecFacing.Dot(vecDir) > 0.5f)
|
|
iFound++;
|
|
}
|
|
}
|
|
if (iFound <= 0)
|
|
return NULL;
|
|
|
|
// randomly pick one
|
|
int iChosen = random->RandomInt(0, iFound-1);
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource* pMarineResource = pGameResource->GetMarineResource(i);
|
|
if (!pMarineResource)
|
|
continue;
|
|
|
|
CASW_Marine *pMarine = pMarineResource->GetMarineEntity();
|
|
if (!pMarine)
|
|
continue;
|
|
|
|
if (pMarine->GetAbsOrigin().DistTo(pEntity->GetAbsOrigin()) < 600.0f)
|
|
{
|
|
Vector vecFacing;
|
|
AngleVectors(pMarine->GetAbsAngles(), &vecFacing);
|
|
Vector vecDir = pEntity->GetAbsOrigin() - pMarine->GetAbsOrigin();
|
|
vecDir.NormalizeInPlace();
|
|
if (vecFacing.Dot(vecDir) > 0.5f)
|
|
{
|
|
if (iChosen <= 0)
|
|
return pMarine;
|
|
iChosen--;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Trace filter that only hits aliens (all NPCS but the marines, eggs, goo)
|
|
//-----------------------------------------------------------------------------
|
|
bool CTraceFilterAliensEggsGoo::ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
|
|
{
|
|
if ( CTraceFilterSimple::ShouldHitEntity(pServerEntity, contentsMask) )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
|
|
if ( pEntity->Classify() == CLASS_ASW_MARINE ) // we dont hit marines
|
|
return false;
|
|
|
|
if ( IsAlienClass( pEntity->Classify() ) )
|
|
return true;
|
|
#endif // !CLIENT_DLL
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// NOTE: This function assumes 75 fov and 4:3 ratio (todo: support widescreen all the time?)
|
|
bool CanFrustumSee(const Vector &vecCameraCenter, const QAngle &angCameraFacing,
|
|
const Vector &pos, const int padding, const int forward_limit, float fov=75.0f)
|
|
{
|
|
Vector vForward, vRight, vUp;
|
|
AngleVectors(angCameraFacing, &vForward, &vRight, &vUp);
|
|
|
|
Vector vecTestPos = pos;
|
|
// bring in the x coord by the padding
|
|
if (vecTestPos.x < vecCameraCenter.x)
|
|
vecTestPos.x = MIN(vecCameraCenter.x, vecTestPos.x + padding);
|
|
else if (vecTestPos.x > vecCameraCenter.x)
|
|
vecTestPos.x = MAX(vecCameraCenter.x, vecTestPos.x - padding);
|
|
// bring in the y coord by the padding
|
|
if (vecTestPos.y < vecCameraCenter.y)
|
|
vecTestPos.y = MIN(vecCameraCenter.y, vecTestPos.y + padding);
|
|
else if (vecTestPos.y > vecCameraCenter.y)
|
|
vecTestPos.y = MAX(vecCameraCenter.y, vecTestPos.y - padding);
|
|
|
|
float ratio = 4.0f / 3.0f; // assume 4:3 res
|
|
float fov_tangent = tan(DEG2RAD(fov) * 0.5f);
|
|
|
|
Vector vDiff = vecTestPos - vecCameraCenter; // vector from camera to testing position
|
|
|
|
float forward_diff = vDiff.Dot(vForward);
|
|
if (forward_diff < 0) // behind the camera
|
|
return false;
|
|
if (forward_limit > 0 && forward_diff > forward_limit)
|
|
return false; // too far away
|
|
|
|
//int padding_at_this_distance = padding * (forward_diff / 405.0f); // adjust padding by ratio of distance to default camera height
|
|
float up_diff = vDiff.Dot(vUp);
|
|
float max_up_diff = forward_diff * fov_tangent;
|
|
|
|
if (up_diff > max_up_diff || up_diff < -max_up_diff)
|
|
return false;
|
|
|
|
float right_diff = vDiff.Dot(vRight);
|
|
float max_right_diff = max_up_diff * ratio;
|
|
|
|
if (right_diff > max_right_diff || right_diff < -max_right_diff)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
CASW_Marine* UTIL_ASW_MarineCanSee(CASW_Marine_Resource* pMarineResource, const Vector &pos, const int padding, bool &bCorpseCanSee, const int forward_limit)
|
|
{
|
|
if (!pMarineResource)
|
|
return NULL;
|
|
|
|
Vector vecMarinePos;
|
|
CASW_Marine* pMarine = NULL;
|
|
#ifndef CLIENT_DLL
|
|
if (pMarineResource->GetHealthPercent() <=0 || !pMarineResource->IsAlive()) // if we're dead, take the corpse position
|
|
{
|
|
vecMarinePos = pMarineResource->m_vecDeathPosition;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pMarine = pMarineResource->GetMarineEntity();
|
|
if (!pMarine)
|
|
return NULL;
|
|
|
|
vecMarinePos = pMarine->GetAbsOrigin();
|
|
}
|
|
|
|
// note: assumes 60 degree pitch camera and 405 dist (actual convars for these are on the client...)
|
|
QAngle angCameraFacing(60, 90, 0);
|
|
Vector vForward, vRight, vUp;
|
|
AngleVectors(angCameraFacing, &vForward, &vRight, &vUp);
|
|
Vector vecCameraCenter = vecMarinePos - vForward * 405;
|
|
|
|
// see if they're beyond the fog plane
|
|
#ifdef CLIENT_DLL
|
|
C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer();
|
|
if (pPlayer)
|
|
{
|
|
if (pPlayer->GetPlayerFog().m_hCtrl->m_fog.enable)
|
|
{
|
|
float dist = (vecCameraCenter - pos).Length();
|
|
if (dist > pPlayer->GetPlayerFog().m_hCtrl->m_fog.end)
|
|
return NULL;
|
|
}
|
|
}
|
|
#else
|
|
// ASWTODO - no worldfogparams anymore?
|
|
/*
|
|
fogparams_t fog;
|
|
GetWorldFogParams(fog);
|
|
if (fog.enable.Get())
|
|
{
|
|
float dist = (vecCameraCenter - pos).Length();
|
|
if (dist > fog.end.Get())
|
|
return NULL;
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
// check for plain near the marine
|
|
bool bNearby = CanFrustumSee(vecCameraCenter, angCameraFacing, pos, padding, forward_limit);
|
|
// check if he's looking through a remote turret and can possibly see us from that
|
|
if (!bNearby && pMarine && pMarine->IsControllingTurret() && pMarine->GetRemoteTurret())
|
|
{
|
|
// a turret can look in any direction, so let's just do a radius check (would match up with the fog anyways)
|
|
bNearby = (pMarine->GetRemoteTurret()->GetAbsOrigin().DistTo(pos) <= 1024); // assume fog distance of 1024 when in first person
|
|
}
|
|
// check if he's looking through a security cam
|
|
if (!bNearby && pMarine && pMarine->m_hUsingEntity.Get())
|
|
{
|
|
CASW_Computer_Area *pComputer = dynamic_cast<CASW_Computer_Area*>(pMarine->m_hUsingEntity.Get());
|
|
if (pComputer)
|
|
{
|
|
if (pComputer->m_iActiveCam == 1 && pComputer->m_hSecurityCam1.Get())
|
|
{
|
|
CPointCamera* pCam = dynamic_cast<CPointCamera*>(pComputer->m_hSecurityCam1.Get());
|
|
if (pCam)
|
|
{
|
|
Vector vecCamFacing;
|
|
AngleVectors(pCam->GetAbsAngles(), &vecCamFacing);
|
|
bNearby = (vecCamFacing.Dot(pos - pCam->GetAbsOrigin()) > 0) && // check facing
|
|
(pCam->GetAbsOrigin().DistTo(pos) <= 1024); // assume fog distance of 1024 when in first person
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifndef CLIENT_DLL
|
|
if (asw_debug_marine_can_see.GetBool())
|
|
{
|
|
if (bNearby)
|
|
{
|
|
NDebugOverlay::Line(pos, vecMarinePos + Vector(0,0,10), 255, 255, 0, true, 0.1f);
|
|
}
|
|
else
|
|
{
|
|
NDebugOverlay::Line(pos, vecMarinePos + Vector(0,0,10), 255, 0, 0, true, 0.1f);
|
|
}
|
|
}
|
|
#endif
|
|
if (bNearby)
|
|
{
|
|
if (!pMarine)
|
|
bCorpseCanSee = true;
|
|
return pMarine;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CASW_Marine* UTIL_ASW_AnyMarineCanSee(const Vector &pos, const int padding, bool &bCorpseCanSee, const int forward_limit)
|
|
{
|
|
// find the closest marine
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
if (!pGameResource)
|
|
return NULL;
|
|
|
|
bCorpseCanSee = false;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource* pMarineResource = pGameResource->GetMarineResource(i);
|
|
bool bCorpse = false;
|
|
CASW_Marine *pMarine = (UTIL_ASW_MarineCanSee(pMarineResource, pos, padding, bCorpse, forward_limit));
|
|
bCorpseCanSee |= bCorpse;
|
|
if (pMarine)
|
|
return pMarine;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool UTIL_ASW_MarineViewCone(const Vector &pos)
|
|
{
|
|
// find the closest marine
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
if (!pGameResource)
|
|
return false;
|
|
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource* pMarineResource = pGameResource->GetMarineResource(i);
|
|
if (!pMarineResource)
|
|
continue;
|
|
|
|
Vector vecMarinePos;
|
|
CASW_Marine* pMarine = pMarineResource->GetMarineEntity();
|
|
if (!pMarine)
|
|
continue;
|
|
|
|
// check it's not too far away
|
|
Vector vecDiff = pos - pMarine->GetAbsOrigin();
|
|
if (vecDiff.LengthSqr() > asw_marine_view_cone_dist.GetFloat())
|
|
continue;
|
|
|
|
// check dot
|
|
Vector vecFacing;
|
|
AngleVectors(pMarine->EyeAngles(), &vecFacing);
|
|
float dot = vecDiff.Dot(vecFacing);
|
|
if (dot < asw_marine_view_cone_dot.GetFloat())
|
|
continue;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
extern ConVar asw_cam_mode;
|
|
extern ConVar joy_pan_camera;
|
|
#else
|
|
extern ConVar asw_debug_medals;
|
|
extern ConVar asw_wire_full_random;
|
|
#endif
|
|
|
|
float UTIL_ASW_CalcFastDoorHackTime(int iNumRows, int iNumColumns, int iNumWires, int iHackLevel, float fSpeedScale)
|
|
{
|
|
float ideal_time = 1.0f;
|
|
// assume 0.5 seconds per row
|
|
float seconds_per_column = 0.5f;
|
|
if (iNumRows == 2)
|
|
seconds_per_column = 1.0f;
|
|
else if (iNumRows == 3)
|
|
seconds_per_column = 1.5f;
|
|
|
|
float time_to_assemble_wire = seconds_per_column * iNumColumns;
|
|
#ifndef CLIENT_DLL
|
|
if (!asw_wire_full_random.GetBool())
|
|
{
|
|
time_to_assemble_wire = 3.0f; // assumes 5 mistakes per wire
|
|
}
|
|
if (asw_debug_medals.GetBool())
|
|
Msg("time_to_assemble_wire = %f\n", time_to_assemble_wire);
|
|
#endif
|
|
// ok so after this amount of time, the wire would be charging
|
|
if (iNumWires <= 0)
|
|
iNumWires = 1;
|
|
float speed_per_wire = 1.0f / iNumWires;
|
|
speed_per_wire *= fSpeedScale;
|
|
|
|
float charge_before_assembling_wire_2 = speed_per_wire * time_to_assemble_wire;
|
|
if (charge_before_assembling_wire_2 >= iHackLevel || iNumWires < 2)
|
|
{
|
|
// if we're here, it means we would have finished the hack before wire two was assembled
|
|
// so the ideal time is just how long it takes to charge up with 1 wire, plus the time it took us to assemble
|
|
ideal_time = (float(iHackLevel) / speed_per_wire) + time_to_assemble_wire;
|
|
}
|
|
else
|
|
{
|
|
// if we're in here, then wire 1 and 2 will be charging
|
|
float charge_before_assembling_wire_3 = charge_before_assembling_wire_2
|
|
+ speed_per_wire * time_to_assemble_wire * 2; // wire 2's contribution
|
|
if (charge_before_assembling_wire_3 >= iHackLevel || iNumWires < 3)
|
|
{
|
|
// if we're here, it means we would have finished the hack before wire three was assembled
|
|
float first_wire_time = time_to_assemble_wire + time_to_assemble_wire;
|
|
float duo_charge = float(iHackLevel) - charge_before_assembling_wire_2; // how much charge up to do with both wires
|
|
ideal_time = (duo_charge / (speed_per_wire * 2)) + first_wire_time;
|
|
}
|
|
else
|
|
{
|
|
// if we're in here, then wires 1, 2 and 3 will be charging
|
|
float charge_before_assembling_wire_4 = charge_before_assembling_wire_3
|
|
+ speed_per_wire * time_to_assemble_wire * 3; // wire 3's contribution
|
|
if (charge_before_assembling_wire_3 >= iHackLevel || iNumWires < 4)
|
|
{
|
|
// if we're here, it means we would have finished the hack before wire 4 was assembled
|
|
float first_wire_time = time_to_assemble_wire + time_to_assemble_wire;
|
|
float two_wire_time = time_to_assemble_wire + first_wire_time;
|
|
float triple_charge = float(iHackLevel) - charge_before_assembling_wire_3; // how much charge do we with 3 wires
|
|
ideal_time = (triple_charge / (speed_per_wire * 3)) + two_wire_time;
|
|
}
|
|
else
|
|
{
|
|
// if we're in here, then wires 1,2,3 and 4 will be charging
|
|
float first_wire_time = time_to_assemble_wire + time_to_assemble_wire;
|
|
float two_wire_time = time_to_assemble_wire + first_wire_time;
|
|
float three_wire_time = time_to_assemble_wire + two_wire_time;
|
|
float quad_charge = float(iHackLevel) - charge_before_assembling_wire_4; // how much charge do we with 3 wires
|
|
ideal_time = (quad_charge / (speed_per_wire * 4)) + three_wire_time;
|
|
}
|
|
}
|
|
}
|
|
|
|
int iSkill = ASWGameRules() ? ASWGameRules()->GetSkillLevel() : 2;
|
|
if (iSkill == 1)
|
|
ideal_time *= 1.05f; // 5% slower on easy mode
|
|
else if (iSkill == 3)
|
|
ideal_time *= 0.95f; // 5% faster on hard mode
|
|
else if (iSkill == 4)
|
|
ideal_time *= 0.90f; // 10% faster on insane mode
|
|
|
|
return ideal_time;
|
|
}
|
|
|
|
int UTIL_ASW_GetNumPlayers()
|
|
{
|
|
int count = 0;
|
|
for (int i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
// found a connected player who isn't ready?
|
|
#ifdef CLIENT_DLL
|
|
if (g_PR->IsConnected(i+1))
|
|
count++;
|
|
#else
|
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex(i + 1);
|
|
// if they're not connected, skip them
|
|
if (pPlayer && pPlayer->IsConnected())
|
|
count++;
|
|
#endif
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
bool UTIL_ASW_MissionHasBriefing(const char* mapname)
|
|
{
|
|
bool bSpecialMap = (!Q_strnicmp(mapname, "intro_", 6) ||
|
|
!Q_strnicmp(mapname, "outro_", 6) ||
|
|
!Q_strnicmp(mapname, "tutorial", 8) ||
|
|
!Q_strnicmp(mapname, "swarmselectionscreen", 20));
|
|
|
|
return !bSpecialMap;
|
|
}
|
|
|
|
bool ASW_IsSecurityCam(CPointCamera *pCameraEnt)
|
|
{
|
|
CASW_PointCamera *pASW_Cam = dynamic_cast<CASW_PointCamera*>(pCameraEnt);
|
|
return pASW_Cam && pASW_Cam->m_bSecurityCam;
|
|
}
|
|
|
|
// copies a string
|
|
char* ASW_AllocString( const char *szString )
|
|
{
|
|
if ( !szString )
|
|
return NULL;
|
|
|
|
int len = Q_strlen( szString ) + 1;
|
|
if ( len <= 1 )
|
|
return NULL;
|
|
|
|
char *text = new char[ len ];
|
|
Q_strncpy( text, szString, len );
|
|
return text;
|
|
}
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
CNewParticleEffect *UTIL_ASW_CreateFireEffect( C_BaseEntity *pEntity )
|
|
{
|
|
CNewParticleEffect *pBurningEffect = pEntity->ParticleProp()->Create( "ent_on_fire", PATTACH_ABSORIGIN_FOLLOW );
|
|
if (pBurningEffect)
|
|
{
|
|
Vector vecOffest1 = (pEntity->WorldSpaceCenter() - pEntity->GetAbsOrigin()) + Vector( 0, 0, 16 );
|
|
pEntity->ParticleProp()->AddControlPoint( pBurningEffect, 1, pEntity, PATTACH_ABSORIGIN_FOLLOW, NULL, vecOffest1 );
|
|
|
|
// all bounding boxes are the same, skip this for now
|
|
Vector vecSurroundMins, vecSurroundMaxs;
|
|
vecSurroundMins = pEntity->CollisionProp()->OBBMins();
|
|
vecSurroundMaxs = pEntity->CollisionProp()->OBBMaxs();
|
|
|
|
// this sets the maximum bounds for scaling up or down the fire
|
|
float flMaxBounds = 34.0;
|
|
flMaxBounds = MAX( flMaxBounds, vecSurroundMaxs.x - vecSurroundMins.x );
|
|
flMaxBounds = MAX( flMaxBounds, vecSurroundMaxs.y - vecSurroundMins.y );
|
|
flMaxBounds = MAX( flMaxBounds, vecSurroundMaxs.z - vecSurroundMins.z );
|
|
|
|
float flScalar = 1.0f;
|
|
flMaxBounds /= 115.0f;
|
|
flMaxBounds = clamp( flMaxBounds, 0.75f, 1.75f );
|
|
|
|
// position 0 of CP2 controls the scale of the flames, we want to scale them a bit based on how big the creature is
|
|
// position 1 is the number generated
|
|
if ( flMaxBounds > 220 )
|
|
flScalar = 2.0f;
|
|
|
|
pBurningEffect->SetControlPoint( 2, Vector( flMaxBounds, flMaxBounds * flScalar, 0 ) );
|
|
}
|
|
return pBurningEffect;
|
|
}
|
|
|
|
// attempts to localize a string
|
|
// if it fails, it just fills the destination with the token name
|
|
void TryLocalize(const char *token, wchar_t *unicode, int unicodeBufferSizeInBytes)
|
|
{
|
|
if ( token[0] == '#' )
|
|
{
|
|
wchar_t *pLocalized = g_pVGuiLocalize->Find( token );
|
|
if ( pLocalized )
|
|
{
|
|
_snwprintf( unicode, unicodeBufferSizeInBytes, L"%s", pLocalized );
|
|
return;
|
|
}
|
|
}
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( token, unicode, unicodeBufferSizeInBytes);
|
|
}
|
|
|
|
ConVar asw_floating_number_type( "asw_floating_number_type", "0", FCVAR_NONE, "1 = vgui, 2 = particles" );
|
|
|
|
void UTIL_ASW_ClientFloatingDamageNumber( const CTakeDamageInfo &info )
|
|
{
|
|
// TODO: Move this to some rendering step?
|
|
if ( asw_floating_number_type.GetInt() == 1 )
|
|
{
|
|
Vector screenPos;
|
|
|
|
Vector vecPos = info.GetDamagePosition(); // WorldSpaceCenter()
|
|
//debugoverlay->ScreenPosition( vecPos, screenPos );
|
|
|
|
floating_number_params_t params;
|
|
params.x = 0;//screenPos.x;
|
|
params.y = 0;//screenPos.y;
|
|
params.bShowPlus = false;
|
|
//params.hFont = m_fontLargeFloatingText;
|
|
params.flMoveDuration = 0.9f;
|
|
params.flFadeStart = 0.6f;
|
|
params.flFadeDuration = 0.3f;
|
|
params.rgbColor = Color( 200, 200, 200, 255 );
|
|
if ( info.GetDamageCustom() & DAMAGE_FLAG_WEAKSPOT )
|
|
{
|
|
params.rgbColor = Color( 255, 170, 150, 255 );
|
|
params.flFadeStart = 0.65f;
|
|
params.flFadeDuration = 0.4f;
|
|
params.flMoveDuration = 0.95f;
|
|
}
|
|
params.alignment = vgui::Label::a_center;
|
|
params.bWorldSpace = true;
|
|
params.vecPos = vecPos;
|
|
|
|
new CFloatingNumber( (int) info.GetDamage(), params, GetClientMode()->GetViewport() );
|
|
}
|
|
else if ( asw_floating_number_type.GetInt() == 2 )
|
|
{
|
|
C_ASW_Marine* pMarine = dynamic_cast<C_ASW_Marine*>( info.GetAttacker() );
|
|
if ( !pMarine )
|
|
return;
|
|
|
|
C_ASW_Player *pAttackingPlayer = pMarine->GetCommander();
|
|
if ( !pAttackingPlayer )
|
|
return;
|
|
|
|
if ( pAttackingPlayer != C_BasePlayer::GetLocalPlayer() )
|
|
return;
|
|
|
|
UTIL_ASW_ParticleDamageNumber( info.GetAttacker(), info.GetDamagePosition(), int(info.GetDamage()), info.GetDamageCustom(), 1.0f, false );
|
|
}
|
|
}
|
|
|
|
void UTIL_ASW_ParticleDamageNumber( C_BaseEntity *pEnt, Vector vecPos, int iDamage, int iDmgCustom, float flScale, bool bRandomVelocity )
|
|
{
|
|
if ( asw_floating_number_type.GetInt() != 2 )
|
|
return;
|
|
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
QAngle vecAngles;
|
|
vecAngles[PITCH] = 0.0f;
|
|
vecAngles[YAW] = ASWInput()->ASW_GetCameraYaw();
|
|
vecAngles[ROLL] = ASWInput()->ASW_GetCameraPitch();
|
|
|
|
//Msg( "DMG # angles ( %f, %f, %f )\n", vecAngles[PITCH], vecAngles[YAW], vecAngles[ROLL] );
|
|
Vector vecForward, vecRight, vecUp;
|
|
AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
|
|
|
|
Color cNumber = Color( 255, 240, 240 );
|
|
|
|
int iCrit = 0;
|
|
float flNewScale = MAX( flScale, 1.0f );
|
|
float flLifetime = 1.0f;
|
|
int r, g, b;
|
|
if ( iDmgCustom & DAMAGE_FLAG_CRITICAL )
|
|
{
|
|
flNewScale *= 1.8f;
|
|
flLifetime = 3.0f;
|
|
iCrit = 1;
|
|
cNumber = Color( 255, 0, 0 );
|
|
}
|
|
else if ( iDmgCustom & DAMAGE_FLAG_WEAKSPOT )
|
|
{
|
|
flNewScale *= 1.3f;
|
|
flLifetime = 1.25f;
|
|
cNumber = Color( 255, 128, 128 );
|
|
}
|
|
else if ( iDmgCustom & DAMAGE_FLAG_T75 )
|
|
{
|
|
flNewScale = 1.3f;
|
|
cNumber = Color( 255, 0, 0 );
|
|
// TODO: Stop these numbers from moving randomly
|
|
}
|
|
|
|
r = cNumber.r();
|
|
g = cNumber.g();
|
|
b = cNumber.b();
|
|
|
|
CUtlReference<CNewParticleEffect> pEffect;
|
|
if ( bRandomVelocity )
|
|
{
|
|
pEffect = pEnt->ParticleProp()->Create( "damage_numbers", PATTACH_CUSTOMORIGIN );
|
|
}
|
|
else
|
|
{
|
|
pEffect = pEnt->ParticleProp()->Create( "floating_numbers", PATTACH_CUSTOMORIGIN );
|
|
}
|
|
pEffect->SetControlPoint( 0, vecPos );
|
|
pEffect->SetControlPoint( 1, Vector( 0, iDamage, iCrit ) );
|
|
pEffect->SetControlPoint( 2, Vector( r, g, b ) );
|
|
pEffect->SetControlPoint( 3, Vector( flNewScale, flLifetime, 0 ) );
|
|
pEffect->SetControlPointOrientation( 5, vecForward, vecRight, vecUp );
|
|
}
|
|
|
|
void __MsgFunc_ASWDamageNumber( bf_read &msg )
|
|
{
|
|
int iAmount = msg.ReadShort();
|
|
int iFlags = msg.ReadShort();
|
|
int iEntIndex = msg.ReadShort();
|
|
C_BaseEntity *pEnt = iEntIndex > 0 ? ClientEntityList().GetEnt( iEntIndex ) : NULL;
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
if ( asw_floating_number_type.GetInt() == 1 )
|
|
{
|
|
Vector vecPos;
|
|
vecPos.x = msg.ReadFloat();
|
|
vecPos.y = msg.ReadFloat();
|
|
vecPos.z = msg.ReadFloat();
|
|
|
|
if ( pEnt )
|
|
{
|
|
vecPos = pEnt->WorldSpaceCenter();
|
|
}
|
|
|
|
Vector screenPos;
|
|
debugoverlay->ScreenPosition( vecPos, screenPos );
|
|
|
|
floating_number_params_t params;
|
|
params.x = 0;//screenPos.x;
|
|
params.y = 0;// screenPos.y;
|
|
params.bShowPlus = false;
|
|
//params.hFont = m_fontLargeFloatingText;
|
|
params.flMoveDuration = 0.85f;
|
|
params.flFadeStart = 0.6f;
|
|
params.flFadeDuration = 0.3f;
|
|
params.rgbColor = Color( 200, 200, 200, 255 );
|
|
if ( iFlags & DAMAGE_FLAG_WEAKSPOT )
|
|
{
|
|
params.rgbColor = Color( 255, 170, 150, 255 );
|
|
params.flFadeStart = 0.65f;
|
|
params.flFadeDuration = 0.4f;
|
|
params.flMoveDuration = 0.95f;
|
|
}
|
|
|
|
params.alignment = vgui::Label::a_center;
|
|
params.bWorldSpace = true;
|
|
params.vecPos = vecPos;
|
|
|
|
new CFloatingNumber( iAmount, params, GetClientMode()->GetViewport() );
|
|
}
|
|
else if ( asw_floating_number_type.GetInt() == 2 )
|
|
{
|
|
UTIL_ASW_ParticleDamageNumber( pEnt, pEnt->WorldSpaceCenter(), iAmount, iFlags, 1.25f, false );
|
|
}
|
|
}
|
|
USER_MESSAGE_REGISTER( ASWDamageNumber );
|
|
#endif
|
|
|
|
|
|
/// @desc This function can be used as a convenience for when you want to
|
|
/// rapidly experiment with different screenshakes for a gameplay feature.
|
|
/// You have a single "scratchpad" screen shake which you can fill out with
|
|
/// the concommand asw_shake_setscratch .
|
|
/// Then you can read it in code with the ASW_DefaultScreenShake.
|
|
/// So, the way you use it is,
|
|
/// if you have a function Kaboom() that needs to do a screenpunch,
|
|
/// but you don't know what numbers you want for that punch yet,
|
|
/// you write the function to use the default screen shake:
|
|
///
|
|
/// void Kaboom() {
|
|
/// ASW_TransmitShakeEvent( player, ASW_DefaultScreenShake() );
|
|
/// }
|
|
///
|
|
/// and then, while the game is running, you can fiddle the numbers around
|
|
/// with asw_shake_setscratch and try the Kaboom() function over and over
|
|
/// again to see the results without having to recompile.
|
|
/// Once you have numbers you are happy with, you can go back and hardcode
|
|
/// them into Kaboom(), freeing up the "Default" shake to be used somewhere
|
|
/// else.
|
|
ScreenShake_t ASW_DefaultScreenShake( void )
|
|
{
|
|
return ScreenShake_t( SHAKE_START,
|
|
asw_shake_test_punch_amp.GetFloat(),
|
|
asw_shake_test_punch_freq.GetFloat(),
|
|
asw_shake_test_punch_dura.GetFloat(),
|
|
Vector( asw_shake_test_punch_dirx.GetFloat(), asw_shake_test_punch_diry.GetFloat(), asw_shake_test_punch_dirz.GetFloat() )
|
|
);
|
|
}
|
|
|
|
static void ASW_PrintDefaultScreenShake( void )
|
|
{
|
|
// x y z f a d
|
|
Msg( "< %.3f,%.3f,%.3f > %.3f %.3f %.3f\n",
|
|
asw_shake_test_punch_dirx.GetFloat(), asw_shake_test_punch_diry.GetFloat(), asw_shake_test_punch_dirz.GetFloat(),
|
|
asw_shake_test_punch_freq.GetFloat(),
|
|
asw_shake_test_punch_amp.GetFloat(),
|
|
asw_shake_test_punch_dura.GetFloat()
|
|
);
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
/// convenient console command for setting the default screen shake parameters
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Test a punch-type screen shake
|
|
//-----------------------------------------------------------------------------
|
|
static void CC_ASW_Shake_SetScratch( const CCommand &args )
|
|
{
|
|
if ( args.ArgC() < 7 )
|
|
{
|
|
Msg("Usage: %s x y z f a d\n"
|
|
"where x,y,z are direction of screen punch\n"
|
|
" f is frequency (1 means three bounces before settling)\n"
|
|
" a is amplitude\n"
|
|
" d is duration\n"
|
|
"you can specify a direction 0 0 0 to mean a classic 'vibrating' shake rather than a directional punch.\n"
|
|
"The current default screen shake is:\n\t",
|
|
args[0]
|
|
);
|
|
ASW_PrintDefaultScreenShake();
|
|
}
|
|
|
|
const float x = atof( args[1] );
|
|
const float y = atof( args[2] );
|
|
const float z = atof( args[3] );
|
|
const float f = atof( args[4] );
|
|
const float a = atof( args[5] );
|
|
const float d = atof( args[6] );
|
|
|
|
|
|
asw_shake_test_punch_dirx.SetValue( x );
|
|
asw_shake_test_punch_diry.SetValue( y );
|
|
asw_shake_test_punch_dirz.SetValue( z );
|
|
asw_shake_test_punch_freq.SetValue( f );
|
|
asw_shake_test_punch_amp.SetValue( a );
|
|
asw_shake_test_punch_dura.SetValue( d );
|
|
}
|
|
static ConCommand asw_shake_setscratch("asw_shake_setscratch", CC_ASW_Shake_SetScratch, "Set values for the \"default\" screenshake used for rapid iteration.\n", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
|
|
|
|
#endif //#ifndef CLIENT_DLL
|
|
|
|
|
|
/// get a parabola that goes from source to destination in specified time
|
|
Vector UTIL_LaunchVector( const Vector &src, const Vector &dest, float gravity, float flightTime )
|
|
{
|
|
Assert( gravity > 0 );
|
|
Assert( !AlmostEqual(src,dest) );
|
|
|
|
if ( flightTime == 0.0f )
|
|
{
|
|
flightTime = MAX( 0.8f, sqrt( ( (dest - src).Length2D() * 1.5f ) / gravity ) );
|
|
}
|
|
|
|
// delta high from start to end
|
|
float H = dest.z - src.z ;
|
|
// azimuth vector
|
|
Vector azimuth = dest-src;
|
|
azimuth.z = 0;
|
|
// get horizontal distance start to end
|
|
float D = azimuth.Length2D();
|
|
// normalize azimuth
|
|
azimuth /= D;
|
|
|
|
float Vy = ( H / flightTime + 0.5 * gravity * flightTime );
|
|
float Vx = ( D / flightTime );
|
|
Vector ret = azimuth * Vx;
|
|
ret.z = Vy;
|
|
return ret;
|
|
}
|
|
|
|
extern ConVar sv_maxvelocity;
|
|
|
|
void UTIL_Bound_Velocity( Vector &vec )
|
|
{
|
|
for ( int i=0 ; i<3 ; i++ )
|
|
{
|
|
if ( IS_NAN(vec[i]) )
|
|
{
|
|
vec[i] = 0;
|
|
}
|
|
|
|
if ( vec[i] > sv_maxvelocity.GetFloat() )
|
|
{
|
|
vec[i] = sv_maxvelocity.GetFloat();
|
|
}
|
|
else if ( vec[i] < -sv_maxvelocity.GetFloat() )
|
|
{
|
|
vec[i] = -sv_maxvelocity.GetFloat();
|
|
}
|
|
}
|
|
}
|
|
|
|
extern ConVar sv_gravity;
|
|
|
|
Vector UTIL_Check_Throw( const Vector &vecSrc, const Vector &vecThrowVelocity, float flGravity, const Vector &vecHullMins, const Vector &vecHullMaxs,
|
|
int iCollisionMask, int iCollisionGroup, CBaseEntity *pIgnoreEnt, bool bDrawArc )
|
|
{
|
|
Vector vecVelocity = vecThrowVelocity;
|
|
const int iMaxSteps = 200;
|
|
Vector vecPos = vecSrc;
|
|
float flInterval = 0.016667f;
|
|
float flActualGravity = sv_gravity.GetFloat() * flGravity;
|
|
for ( int i = 0; i < iMaxSteps; i++ )
|
|
{
|
|
// add gravity
|
|
Vector vecAbsVelocity = vecVelocity;
|
|
|
|
Vector vecMove;
|
|
vecMove.x = (vecVelocity.x ) * flInterval;
|
|
vecMove.y = (vecVelocity.y ) * flInterval;
|
|
|
|
// linear acceleration due to gravity
|
|
float newZVelocity = vecVelocity.z - flActualGravity * flInterval;
|
|
|
|
vecMove.z = ((vecVelocity.z + newZVelocity) / 2.0 ) * flInterval;
|
|
|
|
vecVelocity.z = newZVelocity;
|
|
|
|
UTIL_Bound_Velocity( vecVelocity );
|
|
|
|
// trace to new pos
|
|
trace_t tr;
|
|
Vector vecNewPos = vecPos + vecVelocity * flInterval;
|
|
UTIL_TraceHull( vecPos, vecNewPos, vecHullMins, vecHullMaxs, iCollisionMask, pIgnoreEnt, iCollisionGroup, &tr );
|
|
|
|
if ( bDrawArc )
|
|
{
|
|
debugoverlay->AddLineOverlay( vecPos, vecNewPos, 65, 65, 255, true, 3.0f );
|
|
}
|
|
|
|
if ( tr.fraction < 1.0f || tr.startsolid )
|
|
break;
|
|
|
|
vecPos = tr.endpos;
|
|
}
|
|
|
|
return vecPos;
|
|
} |