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.
269 lines
7.8 KiB
269 lines
7.8 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: A volume which bumps portal placement. Keeps a global list loaded in from the map
|
||
|
// and provides an interface with which prop_portal can get this list and avoid successfully
|
||
|
// creating portals partially inside the volume.
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//======================================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "triggers.h"
|
||
|
#include "portal_player.h"
|
||
|
#include "weapon_portalgun.h"
|
||
|
#include "prop_portal_shared.h"
|
||
|
#include "portal_shareddefs.h"
|
||
|
#include "physobj.h"
|
||
|
#include "portal/weapon_physcannon.h"
|
||
|
#include "model_types.h"
|
||
|
#include "rumble_shared.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
static char *g_pszPortalNonCleansable[] =
|
||
|
{
|
||
|
"func_door",
|
||
|
"func_door_rotating",
|
||
|
"prop_door_rotating",
|
||
|
"func_tracktrain",
|
||
|
"env_ghostanimating",
|
||
|
"physicsshadowclone",
|
||
|
"prop_energy_ball",
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Removes anything that touches it. If the trigger has a targetname,
|
||
|
// firing it will toggle state.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CTriggerPortalCleanser : public CBaseTrigger
|
||
|
{
|
||
|
public:
|
||
|
DECLARE_CLASS( CTriggerPortalCleanser, CBaseTrigger );
|
||
|
|
||
|
void Spawn( void );
|
||
|
void Touch( CBaseEntity *pOther );
|
||
|
|
||
|
DECLARE_DATADESC();
|
||
|
|
||
|
// Outputs
|
||
|
COutputEvent m_OnDissolve;
|
||
|
COutputEvent m_OnFizzle;
|
||
|
COutputEvent m_OnDissolveBox;
|
||
|
};
|
||
|
|
||
|
BEGIN_DATADESC( CTriggerPortalCleanser )
|
||
|
|
||
|
// Outputs
|
||
|
DEFINE_OUTPUT( m_OnDissolve, "OnDissolve" ),
|
||
|
DEFINE_OUTPUT( m_OnFizzle, "OnFizzle" ),
|
||
|
DEFINE_OUTPUT( m_OnDissolveBox, "OnDissolveBox" ),
|
||
|
|
||
|
END_DATADESC()
|
||
|
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( trigger_portal_cleanser, CTriggerPortalCleanser );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTriggerPortalCleanser::Spawn( void )
|
||
|
{
|
||
|
BaseClass::Spawn();
|
||
|
InitTrigger();
|
||
|
}
|
||
|
|
||
|
// Creates a base entity with model/physics matching the parameter ent.
|
||
|
// Used to avoid higher level functions on a disolving entity, which should be inert
|
||
|
// and not react the way it used to (touches, etc).
|
||
|
// Uses simple physics entities declared in physobj.cpp
|
||
|
CBaseEntity* ConvertToSimpleProp ( CBaseEntity* pEnt )
|
||
|
{
|
||
|
CBaseEntity *pRetVal = NULL;
|
||
|
int modelindex = pEnt->GetModelIndex();
|
||
|
const model_t *model = modelinfo->GetModel( modelindex );
|
||
|
if ( model && modelinfo->GetModelType(model) == mod_brush )
|
||
|
{
|
||
|
pRetVal = CreateEntityByName( "simple_physics_brush" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pRetVal = CreateEntityByName( "simple_physics_prop" );
|
||
|
}
|
||
|
|
||
|
pRetVal->KeyValue( "model", STRING(pEnt->GetModelName()) );
|
||
|
pRetVal->SetAbsOrigin( pEnt->GetAbsOrigin() );
|
||
|
pRetVal->SetAbsAngles( pEnt->GetAbsAngles() );
|
||
|
pRetVal->Spawn();
|
||
|
pRetVal->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false );
|
||
|
|
||
|
return pRetVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CTriggerPortalCleanser::Touch( CBaseEntity *pOther )
|
||
|
{
|
||
|
if ( !PassesTriggerFilters( pOther ) )
|
||
|
return;
|
||
|
|
||
|
if ( pOther->IsPlayer() )
|
||
|
{
|
||
|
CPortal_Player *pPlayer = ToPortalPlayer( pOther );
|
||
|
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
CWeaponPortalgun *pPortalgun = dynamic_cast<CWeaponPortalgun*>( pPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
|
||
|
|
||
|
if ( pPortalgun )
|
||
|
{
|
||
|
bool bFizzledPortal = false;
|
||
|
|
||
|
if ( pPortalgun->CanFirePortal1() )
|
||
|
{
|
||
|
CProp_Portal *pPortal = CProp_Portal::FindPortal( pPortalgun->m_iPortalLinkageGroupID, false );
|
||
|
|
||
|
if ( pPortal && pPortal->m_bActivated )
|
||
|
{
|
||
|
pPortal->DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
|
||
|
pPortal->Fizzle();
|
||
|
// HACK HACK! Used to make the gun visually change when going through a cleanser!
|
||
|
pPortalgun->m_fEffectsMaxSize1 = 50.0f;
|
||
|
|
||
|
bFizzledPortal = true;
|
||
|
}
|
||
|
|
||
|
// Cancel portals that are still mid flight
|
||
|
if ( pPortal && pPortal->GetNextThink( s_pDelayedPlacementContext ) > gpGlobals->curtime )
|
||
|
{
|
||
|
pPortal->SetContextThink( NULL, gpGlobals->curtime, s_pDelayedPlacementContext );
|
||
|
pPortalgun->m_fEffectsMaxSize2 = 50.0f;
|
||
|
bFizzledPortal = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pPortalgun->CanFirePortal2() )
|
||
|
{
|
||
|
CProp_Portal *pPortal = CProp_Portal::FindPortal( pPortalgun->m_iPortalLinkageGroupID, true );
|
||
|
|
||
|
if ( pPortal && pPortal->m_bActivated )
|
||
|
{
|
||
|
pPortal->DoFizzleEffect( PORTAL_FIZZLE_KILLED, false );
|
||
|
pPortal->Fizzle();
|
||
|
// HACK HACK! Used to make the gun visually change when going through a cleanser!
|
||
|
pPortalgun->m_fEffectsMaxSize2 = 50.0f;
|
||
|
|
||
|
bFizzledPortal = true;
|
||
|
}
|
||
|
|
||
|
// Cancel portals that are still mid flight
|
||
|
if ( pPortal && pPortal->GetNextThink( s_pDelayedPlacementContext ) > gpGlobals->curtime )
|
||
|
{
|
||
|
pPortal->SetContextThink( NULL, gpGlobals->curtime, s_pDelayedPlacementContext );
|
||
|
pPortalgun->m_fEffectsMaxSize2 = 50.0f;
|
||
|
bFizzledPortal = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( bFizzledPortal )
|
||
|
{
|
||
|
pPortalgun->SendWeaponAnim( ACT_VM_FIZZLE );
|
||
|
pPortalgun->SetLastFiredPortal( 0 );
|
||
|
m_OnFizzle.FireOutput( pOther, this );
|
||
|
pPlayer->RumbleEffect( RUMBLE_RPG_MISSILE, 0, RUMBLE_FLAG_RESTART );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CBaseAnimating *pBaseAnimating = dynamic_cast<CBaseAnimating*>( pOther );
|
||
|
|
||
|
if ( pBaseAnimating && !pBaseAnimating->IsDissolving() )
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
while ( g_pszPortalNonCleansable[ i ] )
|
||
|
{
|
||
|
if ( FClassnameIs( pBaseAnimating, g_pszPortalNonCleansable[ i ] ) )
|
||
|
{
|
||
|
// Don't dissolve non cleansable objects
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
// The portal weight box, used for puzzles in the portal mod is differentiated by its name
|
||
|
// always being 'box'. We use special logic when the cleanser dissolves a box so this is a special output for it.
|
||
|
if ( pBaseAnimating->NameMatches( "box" ) )
|
||
|
{
|
||
|
m_OnDissolveBox.FireOutput( pOther, this );
|
||
|
}
|
||
|
|
||
|
if ( FClassnameIs( pBaseAnimating, "updateitem2" ) )
|
||
|
{
|
||
|
pBaseAnimating->EmitSound( "UpdateItem.Fizzle" );
|
||
|
}
|
||
|
|
||
|
Vector vOldVel;
|
||
|
AngularImpulse vOldAng;
|
||
|
pBaseAnimating->GetVelocity( &vOldVel, &vOldAng );
|
||
|
|
||
|
IPhysicsObject* pOldPhys = pBaseAnimating->VPhysicsGetObject();
|
||
|
|
||
|
if ( pOldPhys && ( pOldPhys->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
|
||
|
{
|
||
|
CPortal_Player *pPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pBaseAnimating );
|
||
|
if( pPlayer )
|
||
|
{
|
||
|
// Modify the velocity for held objects so it gets away from the player
|
||
|
pPlayer->ForceDropOfCarriedPhysObjects( pBaseAnimating );
|
||
|
|
||
|
pPlayer->GetAbsVelocity();
|
||
|
vOldVel = pPlayer->GetAbsVelocity() + Vector( pPlayer->EyeDirection2D().x * 4.0f, pPlayer->EyeDirection2D().y * 4.0f, -32.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Swap object with an disolving physics model to avoid touch logic
|
||
|
CBaseEntity *pDisolvingObj = ConvertToSimpleProp( pBaseAnimating );
|
||
|
if ( pDisolvingObj )
|
||
|
{
|
||
|
// Remove old prop, transfer name and children to the new simple prop
|
||
|
pDisolvingObj->SetName( pBaseAnimating->GetEntityName() );
|
||
|
UTIL_TransferPoseParameters( pBaseAnimating, pDisolvingObj );
|
||
|
TransferChildren( pBaseAnimating, pDisolvingObj );
|
||
|
pDisolvingObj->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
|
||
|
pBaseAnimating->AddSolidFlags( FSOLID_NOT_SOLID );
|
||
|
pBaseAnimating->AddEffects( EF_NODRAW );
|
||
|
|
||
|
IPhysicsObject* pPhys = pDisolvingObj->VPhysicsGetObject();
|
||
|
if ( pPhys )
|
||
|
{
|
||
|
pPhys->EnableGravity( false );
|
||
|
|
||
|
Vector vVel = vOldVel;
|
||
|
AngularImpulse vAng = vOldAng;
|
||
|
|
||
|
// Disolving hurts, damp and blur the motion a little
|
||
|
vVel *= 0.5f;
|
||
|
vAng.z += 20.0f;
|
||
|
|
||
|
pPhys->SetVelocity( &vVel, &vAng );
|
||
|
}
|
||
|
|
||
|
pBaseAnimating->AddFlag( FL_DISSOLVING );
|
||
|
UTIL_Remove( pBaseAnimating );
|
||
|
}
|
||
|
|
||
|
CBaseAnimating *pDisolvingAnimating = dynamic_cast<CBaseAnimating*>( pDisolvingObj );
|
||
|
if ( pDisolvingAnimating )
|
||
|
{
|
||
|
pDisolvingAnimating->Dissolve( "", gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
|
||
|
}
|
||
|
|
||
|
m_OnDissolve.FireOutput( pOther, this );
|
||
|
}
|
||
|
}
|