feat(trigger_catapult): Pasted trigger catapult from TF2.

This commit is contained in:
tupoy-ya 2023-09-25 19:14:16 +05:00
parent 99e620a4df
commit 00cdd31768
No known key found for this signature in database
GPG Key ID: 3541E2B1B448A2DB
4 changed files with 1089 additions and 0 deletions

View File

@ -0,0 +1,412 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "trigger_catapult.h"
#include "vcollide_parse.h"
#include "props.h"
#include "movevars_shared.h"
#include "portal_player.h"
#include "saverestore_utlvector.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
const char *CTriggerCatapult::s_szPlayerPassesTriggerFiltersThinkContext = "CTriggerCatapult::PlayerPassesTriggerFiltersThink";
extern ConVar catapult_physics_drag_boost;
BEGIN_DATADESC( CTriggerCatapult )
DEFINE_THINKFUNC( LaunchThink ),
DEFINE_THINKFUNC( PlayerPassesTriggerFiltersThink ),
DEFINE_KEYFIELD( m_flPlayerVelocity, FIELD_FLOAT, "playerSpeed" ),
DEFINE_KEYFIELD( m_flPhysicsVelocity, FIELD_FLOAT, "physicsSpeed" ),
DEFINE_KEYFIELD( m_vecLaunchAngles, FIELD_VECTOR, "launchDirection" ),
DEFINE_KEYFIELD( m_strLaunchTarget, FIELD_STRING, "launchTarget" ),
DEFINE_KEYFIELD( m_bUseThresholdCheck, FIELD_BOOLEAN, "useThresholdCheck" ),
DEFINE_KEYFIELD( m_bUseExactVelocity, FIELD_BOOLEAN, "useExactVelocity" ),
DEFINE_KEYFIELD( m_flLowerThreshold, FIELD_FLOAT, "lowerThreshold" ),
DEFINE_KEYFIELD( m_flUpperThreshold, FIELD_FLOAT, "upperThreshold" ),
DEFINE_KEYFIELD( m_ExactVelocityChoice, FIELD_INTEGER, "exactVelocityChoiceType" ),
DEFINE_KEYFIELD( m_bOnlyVelocityCheck, FIELD_BOOLEAN, "onlyVelocityCheck" ),
DEFINE_KEYFIELD( m_bApplyAngularImpulse, FIELD_BOOLEAN, "applyAngularImpulse" ),
DEFINE_KEYFIELD( m_flEntryAngleTolerance, FIELD_FLOAT, "EntryAngleTolerance" ),
DEFINE_KEYFIELD( m_flAirControlSupressionTime, FIELD_FLOAT, "AirCtrlSupressionTime" ),
DEFINE_KEYFIELD( m_bDirectionSuppressAirControl, FIELD_BOOLEAN, "DirectionSuppressAirControl" ),
DEFINE_FIELD( m_hLaunchTarget, FIELD_EHANDLE ),
DEFINE_ARRAY( m_flRefireDelay, FIELD_TIME, MAX_PLAYERS + 1 ),
DEFINE_UTLVECTOR( m_hAbortedLaunchees, FIELD_EHANDLE ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPlayerSpeed", InputSetPlayerSpeed ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPhysicsSpeed", InputSetPhysicsSpeed ),
DEFINE_INPUTFUNC( FIELD_STRING,"SetLaunchTarget", InputSetLaunchTarget ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetExactVelocityChoiceType", InputSetExactVelocityChoiceType ),
DEFINE_OUTPUT( m_OnCatapulted, "OnCatapulted" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( trigger_catapult, CTriggerCatapult );
//IMPLEMENT_SERVERCLASS_ST( CTriggerCatapult, DT_TriggerCatapult )
// SendPropArray3( SENDINFO_ARRAY3(m_flRefireDelay), SendPropFloat(SENDINFO_ARRAY(m_flRefireDelay)) ),
// SendPropFloat( SENDINFO( m_flPlayerVelocity ) ),
// SendPropFloat( SENDINFO( m_flPhysicsVelocity ) ),
// SendPropQAngles( SENDINFO( m_vecLaunchAngles ) ),
// //SendPropStringT( SENDINFO( m_strLaunchTarget ) ),
// SendPropInt( SENDINFO( m_ExactVelocityChoice ) ),
// SendPropBool( SENDINFO( m_bUseExactVelocity ) ),
// SendPropBool( SENDINFO( m_bUseThresholdCheck ) ),
// SendPropBool( SENDINFO( m_bOnlyVelocityCheck ) ),
// SendPropFloat( SENDINFO( m_flLowerThreshold ) ),
// SendPropFloat( SENDINFO( m_flUpperThreshold ) ),
// SendPropFloat( SENDINFO( m_flAirControlSupressionTime ) ),
// SendPropBool( SENDINFO( m_bApplyAngularImpulse ) ),
// SendPropFloat( SENDINFO( m_flEntryAngleTolerance ) ),
// SendPropEHandle( SENDINFO( m_hLaunchTarget ) ),
// SendPropBool( SENDINFO( m_bPlayersPassTriggerFilters ) ),
// SendPropBool( SENDINFO( m_bDirectionSuppressAirControl ) ),
//END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTriggerCatapult::CTriggerCatapult( void )
{
//Defaulting to true;
m_bApplyAngularImpulse = true;
m_flAirControlSupressionTime = -1.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::DrawDebugGeometryOverlays( void )
{
BaseClass::DrawDebugGeometryOverlays();
CBaseEntity *pLaunchTarget = m_hLaunchTarget;
if ( pLaunchTarget )
{
// Help us visualize the target
Vector vecSourcePos = GetAbsOrigin();
Vector vecTargetPos = pLaunchTarget->GetAbsOrigin();
float flSpeed = m_flPlayerVelocity;
float flGravity = sv_gravity.GetFloat();
Vector vecVelocity = (vecTargetPos - vecSourcePos);
// This is a hack to get around air resistance with weighted cubes -- this is not intended for all objects!
// float flDragCoefficient = (pVictim->IsPlayer()) ? 1.0f : ( 1.6f );
float flDragCoefficient = 0.0f;
// throw at a constant time
float time = vecVelocity.Length( ) / flSpeed;
vecVelocity = vecVelocity * (1.0 / time) * flDragCoefficient;
// adjust upward toss to compensate for gravity loss
vecVelocity.z += flGravity * time * 0.5;
Vector vecApex = vecSourcePos + (vecTargetPos - vecSourcePos) * 0.5;
vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);
// Visualize it!
if( !m_bUseExactVelocity )
{
NDebugOverlay::Box( vecSourcePos, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
NDebugOverlay::Box( vecTargetPos, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
NDebugOverlay::Line( vecSourcePos, vecApex, 0, 255, 0, false, 0.05f );
NDebugOverlay::Line( vecApex, vecTargetPos, 0, 255, 0, false, 0.05f );
}
else
{
Vector lastPos = vecSourcePos;
vecVelocity = (vecTargetPos - vecSourcePos);
vecVelocity = CalculateLaunchVectorPreserve( vecVelocity, this, pLaunchTarget, true );
for( int i = 0; i < 20; i++ )
{
float flTime = 0.2f*(i+1);
vecApex = vecSourcePos + vecVelocity*flTime;
vecApex.z -= 0.5 * flGravity * (flTime) * (flTime);
NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
NDebugOverlay::Line( vecApex, lastPos, 0, 255, 0, false, 0.05f );
lastPos = vecApex;
}
}
// Physics!
flSpeed = m_flPhysicsVelocity;
vecVelocity = (vecTargetPos - vecSourcePos);
// This is a hack to get around air resistance with weighted cubes -- this is not intended for all objects!
flDragCoefficient = catapult_physics_drag_boost.GetFloat();
// throw at a constant time
time = vecVelocity.Length( ) / flSpeed;
vecVelocity = vecVelocity * (1.0 / time) * flDragCoefficient;
// adjust upward toss to compensate for gravity loss
vecVelocity.z += flGravity * time * 0.5;
vecApex = vecSourcePos + (vecTargetPos - vecSourcePos) * 0.5;
vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);
// Visualize it!
if( !m_bUseExactVelocity )
{
NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, 8.0f, 0.05f );
NDebugOverlay::Line( vecSourcePos, vecApex, 255, 255, 0, false, 0.05f );
NDebugOverlay::Line( vecApex, vecTargetPos, 255, 255, 0, false, 0.05f );
}
else
{
Vector lastPos = vecSourcePos;
vecVelocity = (vecTargetPos - vecSourcePos);
vecVelocity = CalculateLaunchVectorPreserve( vecVelocity, this, pLaunchTarget );
for( int i = 0; i < 20; i++ )
{
float flTime = 0.2f*(i+1);
vecApex = vecSourcePos + vecVelocity*flTime;
vecApex.z -= 0.5 * flGravity * (flTime) * (flTime);
NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, 8.0f, 0.05f );
NDebugOverlay::Line( vecApex, lastPos, 255, 255, 0, false, 0.05f );
lastPos = vecApex;
}
}
}
else
{
// Meh
}
}
//---------------------------------------------------------
//---------------------------------------------------------
int CTriggerCatapult::DrawDebugTextOverlays(void)
{
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
char tempstr[512];
Q_snprintf(tempstr,sizeof(tempstr), "Launch target: %s", m_strLaunchTarget.ToCStr() );
EntityText(text_offset,tempstr,0);
text_offset++;
if ( m_bUseThresholdCheck )
{
Q_snprintf(tempstr,sizeof(tempstr), "Lower threshold velocity: %.2f", m_flLowerThreshold );
EntityText(text_offset,tempstr,0);
text_offset++;
Q_snprintf(tempstr,sizeof(tempstr), "Upper threshold velocity: %.2f", m_flUpperThreshold );
EntityText(text_offset,tempstr,0);
text_offset++;
}
Q_snprintf(tempstr,sizeof(tempstr), "Player velocity: %.2f", m_flPlayerVelocity );
EntityText(text_offset,tempstr,0);
text_offset++;
Q_snprintf(tempstr,sizeof(tempstr), "Physics velocity: %.2f", m_flPhysicsVelocity );
EntityText(text_offset,tempstr,0);
text_offset++;
// Get the target
CBaseEntity *pLaunchTarget = m_hLaunchTarget;
// See if we're attempting to hit a target
if ( pLaunchTarget )
{
Vector vecSourcePos = GetAbsOrigin();
float flGravity = sv_gravity.GetFloat();
{
Vector vecTargetPos = pLaunchTarget->GetAbsOrigin();
vecTargetPos.z -= 32.0f;
Vector vecVelocity = (vecTargetPos - vecSourcePos);
// throw at a constant time
float time = vecVelocity.Length( ) / m_flPlayerVelocity;
vecVelocity = vecVelocity * (1.0 / time);
// adjust upward toss to compensate for gravity loss
vecVelocity.z += flGravity * time * 0.5;
Q_snprintf(tempstr,sizeof(tempstr), "Adjusted Player velocity: %.2f", vecVelocity.Length() );
EntityText(text_offset,tempstr,0);
text_offset++;
}
{
Vector vecTargetPos = pLaunchTarget->GetAbsOrigin();
Vector vecVelocity = (vecTargetPos - vecSourcePos );
// throw at a constant time
float time = vecVelocity.Length( ) / m_flPhysicsVelocity;
vecVelocity = vecVelocity * (1.0 / time);
// adjust upward toss to compensate for gravity loss
vecVelocity.z += flGravity * time * 0.5;
Q_snprintf(tempstr,sizeof(tempstr), "Adjusted Physics velocity: %.2f", vecVelocity.Length() );
EntityText(text_offset,tempstr,0);
text_offset++;
}
}
}
return text_offset;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::Spawn( void )
{
BaseClass::Spawn();
// Don't let the camera shoot through us!
InitTrigger();
for ( int i = 0; i < MAX_PLAYERS + 1; ++i )
{
m_flRefireDelay[i] = 0.0f;
}
m_flLowerThreshold = clamp( m_flLowerThreshold, 0.0f, 1.0f );
m_flUpperThreshold = clamp( m_flUpperThreshold, 0.0f, 1.0f );
SetTransmitState( FL_EDICT_PVSCHECK );
m_hLaunchTarget = gEntList.FindEntityByName( NULL, m_strLaunchTarget );
SetContextThink( &CTriggerCatapult::PlayerPassesTriggerFiltersThink, gpGlobals->curtime + 1.0f, s_szPlayerPassesTriggerFiltersThinkContext );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::InputSetPlayerSpeed( inputdata_t &in )
{
m_flPlayerVelocity = in.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::InputSetPhysicsSpeed( inputdata_t &in )
{
m_flPhysicsVelocity = in.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::InputSetLaunchTarget( inputdata_t &in )
{
m_strLaunchTarget = in.value.StringID();
m_hLaunchTarget = gEntList.FindEntityByName( NULL, m_strLaunchTarget );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::InputSetExactVelocityChoiceType( inputdata_t &in )
{
m_ExactVelocityChoice = in.value.Int();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::LaunchThink( void )
{
for ( int i = 0; i < m_hAbortedLaunchees.Count(); i++ )
{
CBaseEntity *pOther = m_hAbortedLaunchees[i].Get();
bool bShouldRemove = true;
if ( pOther )
{
if ( pOther->IsPlayer() )
{
// Time to get launched and stay in the list in case we're stuck under something again.
bShouldRemove = false;
StartTouch( pOther );
}
else if ( pOther->VPhysicsGetObject() )
{
if( ( pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
{
// the sphere should stay in the list.
bShouldRemove = false;
}
else
{
// Time to get launched!
StartTouch( pOther );
}
}
}
if ( bShouldRemove )
{
m_hAbortedLaunchees.Remove( i );
i--;
}
}
// see if we are still holding something in the catapult
if( m_hAbortedLaunchees.Count() )
{
SetThink( &CTriggerCatapult::LaunchThink );
SetNextThink( gpGlobals->curtime + 0.05f );
}
else
{
SetThink( NULL );
}
}
//Think once a second, looking for a living player. Once one is found, evaluate if they pass our trigger filters and network that down to the client
void CTriggerCatapult::PlayerPassesTriggerFiltersThink( void )
{
for( int i = 1; i != gpGlobals->maxClients; ++i )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer && pPlayer->IsAlive() )
{
m_bPlayersPassTriggerFilters = PassesTriggerFilters( pPlayer );
SetContextThink( NULL, TICK_NEVER_THINK, s_szPlayerPassesTriggerFiltersThinkContext ); //never test again
return;
}
}
SetContextThink( &CTriggerCatapult::PlayerPassesTriggerFiltersThink, gpGlobals->curtime + 1.0f, s_szPlayerPassesTriggerFiltersThinkContext );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::EndTouch( CBaseEntity *pOther )
{
m_hAbortedLaunchees.FindAndFastRemove( pOther );
}

View File

@ -0,0 +1,101 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
// copied from portal2 code; original code came with client-predicted counterpart,
// but implementing predictable triggers in tf2 wasn't trivial so this is just the
// server component. it works but causes prediction errors.
#ifndef TRIGGER_CATAPULT_H
#define TRIGGER_CATAPULT_H
#ifdef _WIN32
#pragma once
#endif
#include "triggers.h"
class CTriggerCatapult : public CBaseTrigger
{
DECLARE_CLASS( CTriggerCatapult, CBaseTrigger );
DECLARE_DATADESC();
//DECLARE_SERVERCLASS();
public:
CTriggerCatapult( void );
virtual void Spawn( void );
virtual void StartTouch( CBaseEntity *pOther );
virtual void EndTouch( CBaseEntity *pOther );
virtual int DrawDebugTextOverlays(void);
virtual void DrawDebugGeometryOverlays( void );
void LaunchThink( void );
void PlayerPassesTriggerFiltersThink( void );
static const char *s_szPlayerPassesTriggerFiltersThinkContext;
private:
void InputSetPlayerSpeed( inputdata_t &in );
void InputSetPhysicsSpeed( inputdata_t &in );
void InputSetLaunchTarget( inputdata_t &in );
void InputSetExactVelocityChoiceType( inputdata_t &in );
void LaunchByTarget( CBaseEntity *pVictim, CBaseEntity *pTarget );
Vector CalculateLaunchVector( CBaseEntity *pVictim, CBaseEntity *pTarget );
Vector CalculateLaunchVectorPreserve( Vector vecInitialVelocity, CBaseEntity *pVictim, CBaseEntity *pTarget, bool bForcePlayer = false );
void LaunchByDirection( CBaseEntity *pVictim );
void OnLaunchedVictim( CBaseEntity *pVictim );
float m_flRefireDelay[MAX_PLAYERS + 1];
float m_flPlayerVelocity;
float m_flPhysicsVelocity;
QAngle m_vecLaunchAngles;
string_t m_strLaunchTarget;
int m_ExactVelocityChoice;
bool m_bUseExactVelocity;
bool m_bUseThresholdCheck;
float m_flLowerThreshold;
float m_flUpperThreshold;
float m_flEntryAngleTolerance;
EHANDLE m_hLaunchTarget;
bool m_bOnlyVelocityCheck;
bool m_bApplyAngularImpulse;
bool m_bPlayersPassTriggerFilters;
float m_flAirControlSupressionTime;
bool m_bDirectionSuppressAirControl;
//CNetworkArray( float, m_flRefireDelay, MAX_PLAYERS + 1 ); // 0 for physics object the rest for each player userid
//CNetworkVar( float, m_flPlayerVelocity );
//CNetworkVar( float, m_flPhysicsVelocity );
//CNetworkQAngle( m_vecLaunchAngles );
////CNetworkVar( string_t, m_strLaunchTarget );
//string_t m_strLaunchTarget;
//CNetworkVar( int, m_ExactVelocityChoice );
//CNetworkVar( bool, m_bUseExactVelocity );
//CNetworkVar( bool, m_bUseThresholdCheck );
//CNetworkVar( float, m_flLowerThreshold );
//CNetworkVar( float, m_flUpperThreshold );
//CNetworkVar( float, m_flEntryAngleTolerance );
//CNetworkHandle( CBaseEntity, m_hLaunchTarget );
//CNetworkVar( bool, m_bOnlyVelocityCheck );
//CNetworkVar( bool, m_bApplyAngularImpulse );
//CNetworkVar( bool, m_bPlayersPassTriggerFilters );
//CNetworkVar( float, m_flAirControlSupressionTime ); // After catapult, stop air control for this long (or default of quarter second if this value is negative)
//CNetworkVar( bool, m_bDirectionSuppressAirControl ); // Do we want to use air control suppression for directional catapults.
COutputEvent m_OnCatapulted;
CUtlVector< EHANDLE > m_hAbortedLaunchees;
};
#endif // TRIGGER_CATAPULT_H

View File

@ -367,6 +367,9 @@ $Project "Server (Portal)"
$File "movie_display.cpp"
$File "portal2\trigger_catapult.cpp"
$File "$SRCDIR\game\shared\portal2\trigger_catapult_shared.cpp"
//$Folder "Paint"
//{
// // shared

View File

@ -0,0 +1,573 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
// copied from portal2 code; original code came with client-predicted counterpart,
// but implementing predictable triggers in tf2 wasn't trivial so this is just the
// server component. it works but causes prediction errors.
#include "cbase.h"
#include "movevars_shared.h"
#include "portal/portal_player.h"
#if defined( GAME_DLL )
#include "trigger_catapult.h"
#include "portal_player.h"
#include "vcollide_parse.h"
#include "props.h"
#else
#include "c_trigger_catapult.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar catapult_physics_drag_boost( "catapult_physics_drag_boost", "2.1", FCVAR_REPLICATED );
//-----------------------------------------------------------------------------
// Purpose: calculates the launch vector between the entity that touched the
// catapult trigger and the catapult target
//-----------------------------------------------------------------------------
Vector CTriggerCatapult::CalculateLaunchVector( CBaseEntity *pVictim, CBaseEntity *pTarget )
{
#if defined( CLIENT_DLL )
if( !GetPredictable() || !pVictim->GetPredictable() )
return vec3_origin;
#endif
// Find where we're going
Vector vecSourcePos = pVictim->GetAbsOrigin();
Vector vecTargetPos = pTarget->GetAbsOrigin();
// If victim is player, adjust target position so player's center will hit the target
if ( pVictim->IsPlayer() )
{
vecTargetPos.z -= 32.0f;
}
float flSpeed = (pVictim->IsPlayer()) ? (float)m_flPlayerVelocity : (float)m_flPhysicsVelocity; // u/sec
float flGravity = GetCurrentGravity();
Vector vecVelocity = (vecTargetPos - vecSourcePos);
// throw at a constant time
float time = vecVelocity.Length( ) / flSpeed;
vecVelocity = vecVelocity * (1.f / time); // CatapultLaunchVelocityMultiplier
// adjust upward toss to compensate for gravity loss
vecVelocity.z += flGravity * time * 0.5;
return vecVelocity;
}
//-----------------------------------------------------------------------------
// Purpose: calculates the launch vector between the entity that touched the
// catapult trigger and the catapult target
//-----------------------------------------------------------------------------
Vector CTriggerCatapult::CalculateLaunchVectorPreserve( Vector vecInitialVelocity, CBaseEntity *pVictim, CBaseEntity *pTarget, bool bForcePlayer )
{
#if defined( CLIENT_DLL )
if( !GetPredictable() || !pVictim->GetPredictable() )
return vec3_origin;
#endif
// Find where we're going
Vector vecSourcePos = pVictim->GetAbsOrigin();
Vector vecTargetPos = pTarget->GetAbsOrigin();
// If victim is player, adjust target position so player's center will hit the target
if ( pVictim->IsPlayer() || bForcePlayer )
{
vecTargetPos.z -= 32.0f;
}
Vector vecDiff = (vecTargetPos - vecSourcePos);
float flHeight = vecDiff.z;
float flDist = vecDiff.Length2D();
float flVelocity = (pVictim->IsPlayer() || bForcePlayer ) ? (float)m_flPlayerVelocity : (float)m_flPhysicsVelocity;
float flGravity = -1.0f*GetCurrentGravity();
if( flDist == 0.f )
{
DevWarning( "Bad location input for catapult!\n" );
return CalculateLaunchVector(pVictim, pTarget);
}
float flRadical = flVelocity*flVelocity*flVelocity*flVelocity - flGravity*(flGravity*flDist*flDist - 2.f*flHeight*flVelocity*flVelocity);
if( flRadical <= 0.f )
{
DevWarning( "Catapult can't hit target! Add more speed!\n" );
return CalculateLaunchVector(pVictim, pTarget);
}
flRadical = ( sqrt( flRadical ) );
float flTestAngle1 = flVelocity*flVelocity;
float flTestAngle2 = flTestAngle1;
flTestAngle1 = -atan( (flTestAngle1 + flRadical) / (flGravity*flDist) );
flTestAngle2 = -atan( (flTestAngle2 - flRadical) / (flGravity*flDist) );
Vector vecTestVelocity1 = vecDiff;
vecTestVelocity1.z = 0;
vecTestVelocity1.NormalizeInPlace();
Vector vecTestVelocity2 = vecTestVelocity1;
vecTestVelocity1 *= flVelocity*cos( flTestAngle1 );
vecTestVelocity1.z = flVelocity*sin( flTestAngle1 );
vecTestVelocity2 *= flVelocity*cos( flTestAngle2 );
vecTestVelocity2.z = flVelocity*sin( flTestAngle2 );
vecInitialVelocity.NormalizeInPlace();
if( m_ExactVelocityChoice == 1 )
{
return vecTestVelocity1;
}
else if( m_ExactVelocityChoice == 2 )
{
return vecTestVelocity2;
}
if( vecInitialVelocity.Dot( vecTestVelocity1 ) > vecInitialVelocity.Dot( vecTestVelocity2 ) )
{
return vecTestVelocity1;
}
return vecTestVelocity2;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::LaunchByTarget( CBaseEntity *pVictim, CBaseEntity *pTarget )
{
#if defined( CLIENT_DLL )
if( !GetPredictable() || !pVictim->GetPredictable() )
return;
#endif
Vector vecVictim;
if ( pVictim->VPhysicsGetObject() )
{
pVictim->VPhysicsGetObject()->GetVelocity( &vecVictim, NULL );
}
else
{
vecVictim = pVictim->GetAbsVelocity();
}
// get the launch vector
Vector vecVelocity = m_bUseExactVelocity ?
CalculateLaunchVectorPreserve( vecVictim, pVictim, pTarget ):
CalculateLaunchVector( pVictim, pTarget );
// Handle a player
if ( pVictim->IsPlayer() )
{
// Send us flying
if ( pVictim->GetFlags() & FL_ONGROUND )
{
pVictim->SetGroundEntity( NULL );
pVictim->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
}
CPortal_Player *pPlayer = ToPortalPlayer( pVictim );
if ( pPlayer )
{
float flSupressionTimeInSeconds = 0.25f;
if ( m_flAirControlSupressionTime > 0 )
{
// If set in the map, use this override time
flSupressionTimeInSeconds = m_flAirControlSupressionTime;
}
//pPlayer->SetAirControlSupressionTime( flSupressionTimeInSeconds * 1000.0f ); // fix units, this method expects milliseconds
pVictim->Teleport( NULL, NULL, &vecVelocity );
OnLaunchedVictim( pVictim );
#if defined( GAME_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
//g_PortalGameStats.Event_Catapult_LaunchByTarget( pPlayer, vecVelocity );
#endif
}
}
else
{
if ( pVictim->GetMoveType() == MOVETYPE_VPHYSICS )
{
// Launch!
IPhysicsObject *pPhysObject = pVictim->VPhysicsGetObject();
if ( pPhysObject )
{
AngularImpulse angImpulse = m_bApplyAngularImpulse ? RandomAngularImpulse( -150.0f, 150.0f ) : vec3_origin;
pPhysObject->SetVelocityInstantaneous( &vecVelocity, &angImpulse );
// UNDONE: don't mess with physics properties
#if defined( GAME_DLL )
CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pVictim);
if ( pProp != NULL )
{
//HACK!
pProp->OnPhysGunDrop( UTIL_GetLocalPlayer(), LAUNCHED_BY_CANNON );
}
#endif
}
}
OnLaunchedVictim( pVictim );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::LaunchByDirection( CBaseEntity *pVictim )
{
#if defined( CLIENT_DLL )
if( !GetPredictable() || !pVictim->GetPredictable() )
return;
#endif
Vector vecForward;
AngleVectors( m_vecLaunchAngles, &vecForward, NULL, NULL );
// Handle a player
if ( pVictim->IsPlayer() )
{
// Simply push us forward
Vector vecPush = vecForward * m_flPlayerVelocity;
// Hack on top of magic
if( CloseEnough( vecPush[0], 0.f ) && CloseEnough( vecPush[1],0.f ) )
{
vecPush[2] = m_flPlayerVelocity * 1.5f; // FIXME: Magic!
}
// Send us flying
if ( pVictim->GetFlags() & FL_ONGROUND )
{
pVictim->SetGroundEntity( NULL );
pVictim->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
}
pVictim->SetAbsVelocity( vecPush );
OnLaunchedVictim( pVictim );
// Do air control suppression
if( m_bDirectionSuppressAirControl )
{
float flSupressionTimeInSeconds = 0.25f;
if ( m_flAirControlSupressionTime > 0 )
{
// If set in the map, use this override time
flSupressionTimeInSeconds = m_flAirControlSupressionTime;
}
//CPortal_Player* pTFPlayer = static_cast<CPortal_Player*>(pVictim);
//pTFPlayer->SetAirControlSupressionTime( flSupressionTimeInSeconds * 1000.0f ); // fix units, this method expects milliseconds
}
#if defined( GAME_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
//g_PortalGameStats.Event_Catapult_LaunchByDirection( ToPortalPlayer(pVictim), vecPush );
#endif
}
#if defined( GAME_DLL )
else
{
if ( pVictim->GetMoveType() == MOVETYPE_VPHYSICS )
{
// Launch!
IPhysicsObject *pPhysObject = pVictim->VPhysicsGetObject();
if ( pPhysObject )
{
Vector vecVelocity = vecForward * m_flPhysicsVelocity;
vecVelocity[2] = m_flPhysicsVelocity;
AngularImpulse angImpulse = RandomAngularImpulse( -50.0f, 50.0f );
pPhysObject->SetVelocityInstantaneous( &vecVelocity, &angImpulse );
// Force this!
float flNull = 0.0f;
pPhysObject->SetDragCoefficient( &flNull, &flNull );
pPhysObject->SetDamping( &flNull, &flNull );
CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pVictim);
if ( pProp != NULL )
{
//HACK!
pProp->OnPhysGunDrop( UTIL_GetLocalPlayer(), LAUNCHED_BY_CANNON );
}
}
}
OnLaunchedVictim( pVictim );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::OnLaunchedVictim( CBaseEntity *pVictim )
{
#if defined( CLIENT_DLL )
if( !GetPredictable() || !pVictim->GetPredictable() )
return;
#endif
#if defined( GAME_DLL )
m_OnCatapulted.FireOutput( pVictim, this );
#endif
if ( pVictim->IsPlayer() )
{
CPortal_Player *pPlayer = static_cast< CPortal_Player* >( pVictim );
int nRefireIndex = pPlayer->entindex();
#if defined( GAME_DLL )
m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
#else
m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
#endif
}
else
{
#if defined( GAME_DLL )
m_flRefireDelay[ 0 ] = gpGlobals->curtime + 0.5f; // HACK!
#else
m_flRefireDelay[ 0 ] = gpGlobals->curtime + 0.5f; // HACK!
#endif
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerCatapult::StartTouch( CBaseEntity *pOther )
{
if ( pOther == NULL )
return;
#if defined( CLIENT_DLL )
if( !GetPredictable() || !pOther->GetPredictable() )
return;
#endif
//Warning( "CTriggerCatapult::StartTouch( %i %s %f )\n", entindex(), gpGlobals->IsClient() ? "client" : "server", gpGlobals->curtime );
#if defined( GAME_DLL )
if ( PassesTriggerFilters( pOther ) == false )
#else
if( !(pOther->IsPlayer() && m_bPlayersPassTriggerFilters) )
#endif
{
return;
}
// Don't refire too quickly
int nRefireIndex = pOther->IsPlayer() ? static_cast< CBasePlayer* >( pOther )->entindex() : 0;
if ( nRefireIndex >= MAX_PLAYERS + 1 )
{
Warning( "CTriggerCatapult::StartTouch Trying to store a refire index for an entity( %d ) outside the expected range ( < %d ).\n", nRefireIndex, MAX_PLAYERS + 1 );
nRefireIndex = 0;
}
if ( m_flRefireDelay[ nRefireIndex ] > gpGlobals->curtime )
{
// but also don't forget to try again
if ( m_hAbortedLaunchees.Find( pOther ) == -1 )
{
m_hAbortedLaunchees.AddToTail( pOther );
}
SetThink( &CTriggerCatapult::LaunchThink );
SetNextThink( gpGlobals->curtime + 0.05f );
return;
}
#if defined( GAME_DLL )
// Don't touch things the player is holding
if ( pOther->VPhysicsGetObject() && (pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
{
if ( m_hAbortedLaunchees.Find( pOther ) == -1 )
{
m_hAbortedLaunchees.AddToTail( pOther );
}
SetThink( &CTriggerCatapult::LaunchThink );
SetNextThink( gpGlobals->curtime + 0.05f );
return;
}
else if ( pOther->IsPlayer() )
{
// Always keep players in this list in case the were trapped under another player in the previous launch
if ( m_hAbortedLaunchees.Find( pOther ) == -1 )
{
m_hAbortedLaunchees.AddToTail( pOther );
}
SetThink( &CTriggerCatapult::LaunchThink );
SetNextThink( gpGlobals->curtime + 0.05f );
}
#endif
// Get the target
CBaseEntity *pLaunchTarget = m_hLaunchTarget;
// See if we're attempting to hit a target
if ( pLaunchTarget )
{
// See if we are using the threshold check
if ( m_bUseThresholdCheck )
{
// Get the velocity of the physics objects / players touching the catapult
Vector vecVictim;
if ( pOther->IsPlayer() )
{
vecVictim = pOther->GetAbsVelocity();
}
else if( pOther->VPhysicsGetObject() )
{
pOther->VPhysicsGetObject()->GetVelocity( &vecVictim, NULL );
}
else
{
// DevMsg("Catapult fail!! Object is not a player and has no physics object! BUG THIS\n");
vecVictim = vec3_origin;
}
float flVictimSpeed = vecVictim.Length();
// get the speed needed to hit the target
Vector vecVelocity;
if( m_bUseExactVelocity )
{
vecVelocity = CalculateLaunchVectorPreserve( vecVictim, pOther, pLaunchTarget );
}
else
{
vecVelocity = CalculateLaunchVector( pOther, pLaunchTarget );
}
float flLaunchSpeed = vecVelocity.Length();
// is the victim facing the target?
Vector vecDirection = ( pLaunchTarget->GetAbsOrigin() - pOther->GetAbsOrigin() );
Vector necNormalizedVictim = vecVictim;
Vector vecNormalizedDirection = vecDirection;
necNormalizedVictim.NormalizeInPlace();
vecNormalizedDirection.NormalizeInPlace();
float flDot = DotProduct( necNormalizedVictim, vecNormalizedDirection );
if ( flDot >= m_flEntryAngleTolerance )
{
// Is the victim speed within tolerance to launch them?
if ( ( ( flLaunchSpeed - (flLaunchSpeed * m_flLowerThreshold ) ) < flVictimSpeed ) && ( ( flLaunchSpeed + (flLaunchSpeed * m_flUpperThreshold ) ) > flVictimSpeed ) )
{
if( m_bOnlyVelocityCheck )
{
OnLaunchedVictim( pOther );
}
else
{
// Launch!
LaunchByTarget( pOther, pLaunchTarget );
// DevMsg( 1, "Catapult \"%s\" is adjusting velocity of \"%s\" so it will hit the target. (Object Velocity: %.1f -- Object needed to be between %.1f and %.1f \n", STRING(GetEntityName()), pOther->GetClassname(), flVictimSpeed, flLaunchSpeed - (flLaunchSpeed * m_flLowerThreshold ), flLaunchSpeed + (flLaunchSpeed * m_flUpperThreshold ) );
}
}
else
{
// DevMsg( 1, "Catapult \"%s\" ignoring object \"%s\" because its velocity is outside of the threshold. (Object Velocity: %.1f -- Object needed to be between %.1f and %.1f \n", STRING(GetEntityName()), pOther->GetClassname(), flVictimSpeed, flLaunchSpeed - (flLaunchSpeed * m_flLowerThreshold ), flLaunchSpeed + (flLaunchSpeed * m_flUpperThreshold ) );
// since we attempted a fling set the refire delay
#if defined( GAME_DLL )
m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
#else
m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
#endif
}
}
else
{
// we're facing the wrong way. set the refire delay.
#if defined( GAME_DLL )
m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
#else
m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
#endif
}
}
else
{
LaunchByTarget( pOther, pLaunchTarget );
}
}
else
{
#if defined( CLIENT_DLL )
if( m_hLaunchTarget.IsValid() )
{
Warning( "Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
"Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
"Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
"Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
"Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n" );
}
#endif
bool bShouldLaunch = true;
if( m_bUseThresholdCheck )
{
// Get the velocity of the physics objects / players touching the catapult
Vector vecVictim;
if ( pOther->IsPlayer() )
{
vecVictim = pOther->GetAbsVelocity();
}
else if( pOther->VPhysicsGetObject() )
{
pOther->VPhysicsGetObject()->GetVelocity( &vecVictim, NULL );
}
else
{
// DevMsg("Catapult fail!! Object is not a player and has no physics object! BUG THIS\n");
vecVictim = vec3_origin;
}
Vector vecForward;
AngleVectors( m_vecLaunchAngles, &vecForward, NULL, NULL );
float flDot = DotProduct( vecForward, vecVictim );
float flLower = m_flPlayerVelocity - (m_flPlayerVelocity * m_flLowerThreshold);
float flUpper = m_flPlayerVelocity + (m_flPlayerVelocity * m_flUpperThreshold);
if( flDot < flLower || flDot > flUpper )
{
bShouldLaunch = false;
}
}
if( bShouldLaunch )
{
#if defined( CLIENT_DLL )
CEG_PROTECT_VIRTUAL_FUNCTION ( CTriggerCatapult_StartTouch );
#endif
if( m_bOnlyVelocityCheck )
{
OnLaunchedVictim( pOther );
}
else
{
LaunchByDirection( pOther );
}
}
}
}