This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -0,0 +1,526 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: One of the two ends of a portal pair which can be picked up and placed by weapon_camera
//
//===========================================================================//
#include "cbase.h"
#include "portal_mp_gamerules.h"
#include "cvisibilitymonitor.h"
#include "cegclientwrapper.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define PROP_BUTTON_MODEL_NAME "models/props/switch001.mdl"
#define PROP_UNDER_BUTTON_MODEL_NAME "models/props_underground/underground_testchamber_button.mdl"
ConVar sv_portal2_button_hint_range( "sv_portal2_button_hint_range", "350.0", FCVAR_NONE );
//-----------------------------------------------------------------------------
// Context think
//-----------------------------------------------------------------------------
static const char *s_pTimerThinkContext = "TimerThinkContext";
class CPropButton : public CBaseAnimating
{
public:
DECLARE_CLASS( CPropButton, CBaseAnimating );
DECLARE_DATADESC();
CPropButton( void );
virtual void Precache( void );
virtual void Spawn( void );
virtual bool CreateVPhysics( void );
virtual void Activate( void );
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_IMPULSE_USE ); }
void AnimateThink( void );
void TimerThink( void );
void Lock();
void Unlock();
int DrawDebugTextOverlays();
virtual const char *GetButtonModelName();
private:
void Press( CBaseEntity *pActivator );
void InputPress( inputdata_t &input );
void InputLock( inputdata_t &inputdata );
void InputUnlock( inputdata_t &inputdata );
void InputCancelPress( inputdata_t &input );
void OnPressed( void );
void OnButtonReset( void );
EHANDLE m_hActivator;
COutputEvent m_OnPressed;
COutputEvent m_OnPressedOrange;
COutputEvent m_OnPressedBlue;
COutputEvent m_OnButtonReset;
bool m_bLocked;
float m_flDelayBeforeReset;
float m_flGoalTime; // goal time when a pressed button should unpress
bool m_bIsTimer;
bool m_bTimerCancelled;
bool m_bPreventFastReset;
protected:
virtual void LookUpAnimationSequences( void );
// animation sequences for the button
int m_UpSequence;
int m_DownSequence;
int m_IdleDownSequence;
int m_IdleUpSequence;
};
LINK_ENTITY_TO_CLASS( prop_button, CPropButton );
BEGIN_DATADESC( CPropButton )
DEFINE_THINKFUNC( AnimateThink ),
DEFINE_THINKFUNC( TimerThink ),
DEFINE_KEYFIELD( m_flDelayBeforeReset, FIELD_FLOAT, "Delay" ),
DEFINE_KEYFIELD( m_bIsTimer, FIELD_BOOLEAN, "IsTimer" ),
DEFINE_KEYFIELD( m_bPreventFastReset, FIELD_BOOLEAN, "PreventFastReset" ),
DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bTimerCancelled, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flGoalTime, FIELD_TIME ),
DEFINE_FIELD( m_UpSequence, FIELD_INTEGER ),
DEFINE_FIELD( m_DownSequence, FIELD_INTEGER ),
DEFINE_FIELD( m_IdleDownSequence, FIELD_INTEGER ),
DEFINE_FIELD( m_IdleUpSequence, FIELD_INTEGER ),
DEFINE_INPUTFUNC( FIELD_VOID, "Press", InputPress ),
DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
DEFINE_INPUTFUNC( FIELD_VOID, "CancelPress", InputCancelPress ),
DEFINE_OUTPUT( m_OnPressed, "OnPressed" ),
DEFINE_OUTPUT( m_OnPressedOrange, "OnPressedOrange" ),
DEFINE_OUTPUT( m_OnPressedBlue, "OnPressedBlue" ),
DEFINE_OUTPUT( m_OnButtonReset, "OnButtonReset" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CPropButton::CPropButton( void )
{
// set the default locked state on spawn
m_bLocked = false;
m_bTimerCancelled = false;
RemoveEffects( EF_SHADOWDEPTH_NOCACHE );
AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
}
const char *CPropButton::GetButtonModelName()
{
return PROP_BUTTON_MODEL_NAME;
}
void CPropButton::LookUpAnimationSequences( void )
{
// look up animation sequences
m_UpSequence = LookupSequence( "up" );
m_DownSequence = LookupSequence( "down" );
m_IdleUpSequence = LookupSequence( "idle" );
m_IdleDownSequence = LookupSequence( "idle_down" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropButton::Precache( void )
{
PrecacheModel( GetButtonModelName() );
// sounds for button
PrecacheScriptSound( "Portal.button_down" );
PrecacheScriptSound( "Portal.button_up" );
PrecacheScriptSound( "Portal.button_locked" );
PrecacheScriptSound( "Portal.room1_TickTock" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropButton::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_VPHYSICS );
SetModel( GetButtonModelName() );
//Buttons are unpaintable
AddFlag( FL_UNPAINTABLE );
LookUpAnimationSequences();
m_flGoalTime = 0;
CreateVPhysics();
VisibilityMonitor_AddEntity_NotVisibleThroughGlass( this, sv_portal2_button_hint_range.GetFloat() - 50.0f, NULL, NULL );
// Never let crucial game components fade out!
SetFadeDistance( -1.0f, 0.0f );
SetGlobalFadeScale( 0.0f );
// Start "up"
ResetSequence( m_IdleUpSequence );
}
bool CPropButton::CreateVPhysics( void )
{
VPhysicsInitStatic();
return true;
}
void CPropButton::Activate( void )
{
BaseClass::Activate();
SetThink( &CPropButton::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Animate and catch edge cases for us stopping / starting our animation
//-----------------------------------------------------------------------------
void CPropButton::AnimateThink( void )
{
// Update our animation
StudioFrameAdvance();
DispatchAnimEvents( this );
// this loop runs every time an animation finishes
// and figures out the next animation to play.
if ( IsSequenceFinished() )
{
int nSequence = GetSequence();
if ( nSequence == m_UpSequence )
{
ResetSequence( m_IdleUpSequence );
// fire the OnButtonReset output
OnButtonReset();
}
else if ( nSequence == m_DownSequence )
{
ResetSequence( m_IdleDownSequence );
// set the time for the button to reset
m_flGoalTime = gpGlobals->curtime + m_flDelayBeforeReset;
// fire the OnPressed output
OnPressed();
//if the button is a timer play the tick-tock sound while button is down
if ( m_bIsTimer )
{
SetContextThink( &CPropButton::TimerThink, gpGlobals->curtime + 1.0f, s_pTimerThinkContext );
if( !m_bPreventFastReset )
{
// since this is a timer button the button resets to the up position immediately after being pressed
ResetSequence( m_UpSequence );
}
}
}
else if ( nSequence == m_IdleDownSequence )
{
// reset the button if it is time
if ( gpGlobals->curtime > m_flGoalTime )
{
ResetSequence( m_UpSequence );
}
}
}
SetThink( &CPropButton::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
void CPropButton::TimerThink( void )
{
// determine if we should play the tick-tock sound
if ( m_flGoalTime > gpGlobals->curtime )
{
EmitSound( "Portal.room1_TickTock" );
// tick again in 1 second
SetContextThink( &CPropButton::TimerThink, gpGlobals->curtime + 1.0f, s_pTimerThinkContext );
}
else
{
// stop ticking
SetContextThink( NULL, TICK_NEVER_THINK, s_pTimerThinkContext );
// skip the button reset events if the timer was cancelled
if ( m_bTimerCancelled )
{
m_bTimerCancelled = false;
}
else
{
// play the button up sound
EmitSound( "Portal.button_up" );
// fire the OnReset output
m_OnButtonReset.FireOutput( this, this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Press the button
//-----------------------------------------------------------------------------
void CPropButton::Press( CBaseEntity *pActivator )
{
if ( m_bLocked )
{
// button is locked so play a locked sound
EmitSound( "Portal.button_locked" );
}
else
{
// animate the button being pressed
int nCurrentSequence = GetSequence();
if (nCurrentSequence == m_IdleUpSequence )
{
ResetSequence( m_DownSequence );
// play the button press sound
EmitSound( "Portal.button_down" );
}
m_hActivator = pActivator;
}
}
//-----------------------------------------------------------------------------
// Purpose: Press the button (via input)
//-----------------------------------------------------------------------------
void CPropButton::InputPress( inputdata_t &input )
{
Press( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: Expire the timer
//-----------------------------------------------------------------------------
void CPropButton::InputCancelPress( inputdata_t &input )
{
m_bTimerCancelled = true;
// set the goal time to the current time so the timer logic will expire
m_flGoalTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose: Fire output for button being pressed
//-----------------------------------------------------------------------------
void CPropButton::OnPressed( void )
{
// fire the OnPressed output
if ( m_hActivator.Get() != NULL )
{
CBaseEntity *pOther = dynamic_cast<CBaseEntity*>(m_hActivator.Get());
if ( GameRules() && GameRules()->IsMultiplayer() && pOther && pOther->IsPlayer() )
{
if ( pOther->GetTeamNumber() == TEAM_RED )
{
m_OnPressedOrange.FireOutput( pOther, this );
}
else if ( pOther->GetTeamNumber() == TEAM_BLUE )
{
m_OnPressedBlue.FireOutput( pOther, this );
}
}
m_OnPressed.FireOutput( m_hActivator.Get(), this );
}
else
m_OnPressed.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose: Fire output when button has reset after being pressed
//-----------------------------------------------------------------------------
void CPropButton::OnButtonReset( void )
{
// skip the button reset events if the timer was cancelled
if ( m_bTimerCancelled )
{
m_bTimerCancelled = false;
}
else if( !m_bIsTimer ) // if the button is a timer then don't do this. the timer will handle this step when it expires.
{
// play the button up sound
EmitSound( "Portal.button_up" );
// fire the OnReset output
m_OnButtonReset.FireOutput( this, this );
}
else
{
STEAMWORKS_SELFCHECK();
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pActivator -
// *pCaller -
// useType -
// value -
//-----------------------------------------------------------------------------
void CPropButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
CBasePlayer *pPlayer = ToBasePlayer( pActivator );
if ( pPlayer )
{
// press the button
Press( pActivator );
}
}
//-----------------------------------------------------------------------------
// Purpose: Locks the button. If locked, the button will play the locked sound
// when the player tries to use it.
//-----------------------------------------------------------------------------
void CPropButton::Lock()
{
m_bLocked = true;
}
//-----------------------------------------------------------------------------
// Purpose: Unlocks the button, making it able to be pressed again.
//-----------------------------------------------------------------------------
void CPropButton::Unlock()
{
m_bLocked = false;
}
//-----------------------------------------------------------------------------
// Purpose: Locks the button. If locked, the button will play the locked sound
// when the player tries to use it.
//-----------------------------------------------------------------------------
void CPropButton::InputLock( inputdata_t &inputdata )
{
Lock();
}
//-----------------------------------------------------------------------------
// Purpose: Unlocks the button, making it able to be pressed again.
//-----------------------------------------------------------------------------
void CPropButton::InputUnlock( inputdata_t &inputdata )
{
Unlock();
}
//-----------------------------------------------------------------------------
// Draw debug overlays
//-----------------------------------------------------------------------------
int CPropButton::DrawDebugTextOverlays()
{
int text_offset = BaseClass::DrawDebugTextOverlays();
char tempstr[255];
Q_snprintf( tempstr, sizeof(tempstr), "%s", m_bLocked ? "Locked" : "Unlocked" );
EntityText( text_offset, tempstr, 0 );
text_offset++;
Q_snprintf( tempstr, sizeof(tempstr), "%s", m_bIsTimer ? "Is a timer" : "Is not a timer" );
EntityText( text_offset, tempstr, 0 );
text_offset++;
Q_snprintf( tempstr, sizeof(tempstr), "Delay: %f", m_flDelayBeforeReset );
EntityText( text_offset, tempstr, 0 );
text_offset++;
if ( ( m_flGoalTime - gpGlobals->curtime) > 0 )
{
Q_snprintf( tempstr, sizeof(tempstr), "Timer expires in: %.2f", ( m_flGoalTime - gpGlobals->curtime) );
EntityText( text_offset, tempstr, 0 );
text_offset++;
}
return text_offset;
}
//-----------------------------------------------------------------------------
// Underground button
//-----------------------------------------------------------------------------
class CPropUnderButton : public CPropButton
{
DECLARE_CLASS( CPropUnderButton, CPropButton );
DECLARE_DATADESC()
public:
virtual const char *GetButtonModelName();
protected:
virtual void LookUpAnimationSequences( void );
};
LINK_ENTITY_TO_CLASS( prop_under_button, CPropUnderButton );
BEGIN_DATADESC( CPropUnderButton )
END_DATADESC()
const char *CPropUnderButton::GetButtonModelName()
{
return PROP_UNDER_BUTTON_MODEL_NAME;
}
void CPropUnderButton::LookUpAnimationSequences( void )
{
// look up animation sequences
m_UpSequence = LookupSequence( "release" );
m_DownSequence = LookupSequence( "press" );
m_IdleUpSequence = LookupSequence( "release_idle" );
m_IdleDownSequence = LookupSequence( "press_idle" );
}

View File

@@ -0,0 +1,797 @@
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: button that can be pressed by a weighted cube or player standing on it
//
//===========================================================================//
#include "cbase.h"
#include "props.h"
#include "triggers.h"
#include "prop_weightedcube.h"
#include "portal_mp_gamerules.h"
#include "prop_monster_box.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define PROP_FLOOR_BUTTON_DEFAULT_MODEL_NAME "models/props/portal_button.mdl"
#define PROP_FLOOR_CUBE_BUTTON_MODEL_NAME "models/props/box_socket.mdl"
#define PROP_FLOOR_BALL_BUTTON_MODEL_NAME "models/props/ball_button.mdl"
#define PROP_UNDER_FLOOR_BUTTON_MODEL_NAME "models/props_underground/underground_floor_button.mdl"
static const char * s_pszPressingBoxHasSetteledThinkContext = "PressingBoxHasSetteledThinkContext";
class CPropFloorButton;
enum button_skins
{
button_off_skin,
button_on_skin
};
//
// Trigger for floor button
//
class CPortalButtonTrigger : public CBaseTrigger
{
public:
DECLARE_CLASS( CPortalButtonTrigger, CBaseTrigger );
DECLARE_DATADESC();
static CPortalButtonTrigger *Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecMins, const Vector &vecMaxs, CPropFloorButton *pOwner );
virtual bool PassesTriggerFilters(CBaseEntity *pOther);
virtual void OnStartTouchAll( CBaseEntity *pOther );
virtual void EndTouch(CBaseEntity *pOther);
virtual void OnEndTouchAll( CBaseEntity *pOther );
virtual void StartTouch(CBaseEntity *pOther);
virtual void Spawn( void );
private:
CPropFloorButton *m_pOwnerButton;
};
LINK_ENTITY_TO_CLASS( trigger_portal_button, CPortalButtonTrigger );
BEGIN_DATADESC( CPortalButtonTrigger )
DEFINE_FIELD( m_pOwnerButton, FIELD_CLASSPTR ),
END_DATADESC()
//
// Floor Button
//
class CPropFloorButton : public CDynamicProp
{
public:
DECLARE_CLASS( CPropFloorButton, CDynamicProp );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
CPropFloorButton();
virtual void Precache( void );
virtual void Spawn( void );
virtual bool CreateVPhysics( void );
virtual void Activate( void );
virtual void AnimateThink( void );
virtual void UpdateOnRemove( void );
void PressingBoxHasSetteledThink( void );
virtual const char *GetButtonModelName();
virtual bool ShouldPlayerTouch();
virtual bool OnlyAcceptBall( void ) { return false; }
virtual bool AcceptsBall( void ) { return true; }
void SetSkin( int skinNum );
private:
void OnPressed( CBaseEntity* pActivator );
void OnUnPressed( CBaseEntity* pActivator );
COutputEvent m_OnPressed;
COutputEvent m_OnPressedOrange;
COutputEvent m_OnPressedBlue;
COutputEvent m_OnUnPressed;
protected:
CHandle<CPortalButtonTrigger> m_hButtonTrigger;
CNetworkVar( bool, m_bButtonState );
virtual void CreateTriggers( void );
void TriggerStartTouch( CBaseEntity *pOther );
void TriggerEndTouch( CBaseEntity *pOther );
virtual void Press( CBaseEntity *pActivator );
virtual void UnPress( CBaseEntity *pActivator );
void InputPressIn( inputdata_t &inputdata );
void InputPressOut( inputdata_t &inputdata );
virtual void LookUpAnimationSequences( void );
// animation sequences for the button
int m_UpSequence;
int m_DownSequence;
friend class CPortalButtonTrigger;
};
LINK_ENTITY_TO_CLASS( prop_floor_button, CPropFloorButton );
BEGIN_DATADESC( CPropFloorButton )
DEFINE_THINKFUNC( AnimateThink ),
DEFINE_THINKFUNC( PressingBoxHasSetteledThink ),
DEFINE_FIELD( m_UpSequence, FIELD_INTEGER ),
DEFINE_FIELD( m_DownSequence, FIELD_INTEGER ),
DEFINE_FIELD( m_hButtonTrigger, FIELD_EHANDLE ),
DEFINE_INPUTFUNC( FIELD_VOID, "PressIn", InputPressIn ),
DEFINE_INPUTFUNC( FIELD_VOID, "PressOut", InputPressOut ),
DEFINE_OUTPUT( m_OnPressed, "OnPressed" ),
DEFINE_OUTPUT( m_OnPressedOrange, "OnPressedOrange" ),
DEFINE_OUTPUT( m_OnPressedBlue, "OnPressedBlue" ),
DEFINE_OUTPUT( m_OnUnPressed, "OnUnPressed" ),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST(CPropFloorButton, DT_PropFloorButton)
SendPropBool( SENDINFO( m_bButtonState ) ),
END_SEND_TABLE()
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CPropFloorButton::CPropFloorButton():
m_bButtonState( false ) // button is not pressed by default
{
RemoveEffects( EF_SHADOWDEPTH_NOCACHE );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropFloorButton::Precache( void )
{
PrecacheModel( GetButtonModelName() );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropFloorButton::Spawn( void )
{
KeyValue( "model", GetButtonModelName() );
Precache();
BaseClass::Spawn();
SetSolid( SOLID_VPHYSICS );
LookUpAnimationSequences();
// Start in the up state
ResetSequence( m_UpSequence );
// Ensure the 'off' skin is set
SetSkin( button_off_skin );
//Buttons are unpaintable
AddFlag( FL_UNPAINTABLE );
CreateVPhysics();
CreateTriggers();
AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
// Never let crucial game components fade out!
SetFadeDistance( -1.0f, 0.0f );
SetGlobalFadeScale( 0.0f );
}
void CPropFloorButton::LookUpAnimationSequences( void )
{
// look up animation sequences
m_UpSequence = LookupSequence( "up" );
m_DownSequence = LookupSequence( "down" );
}
bool CPropFloorButton::CreateVPhysics( void )
{
BaseClass::CreateVPhysics();
return true;
}
void CPropFloorButton::Activate( void )
{
BaseClass::Activate();
SetThink( &CPropFloorButton::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Animate and catch edge cases for us stopping / starting our animation
//-----------------------------------------------------------------------------
void CPropFloorButton::AnimateThink( void )
{
// Update our animation
StudioFrameAdvance();
DispatchAnimEvents( this );
m_BoneFollowerManager.UpdateBoneFollowers(this);
SetNextThink( gpGlobals->curtime + 0.1f );
RANDOM_CEG_TEST_SECRET();
// debug overlay of trigger displays if ent_bbox is used on entity
if ( m_debugOverlays & OVERLAY_BBOX_BIT )
{
if ( m_hButtonTrigger )
{
m_hButtonTrigger->m_debugOverlays |= OVERLAY_BBOX_BIT;
NDebugOverlay::Cross3D( GetAbsOrigin(), 4, 0, 255, 0, true, 0.1f );
}
}
else
{
if ( m_hButtonTrigger )
{
m_hButtonTrigger->m_debugOverlays &= ~OVERLAY_BBOX_BIT;
}
}
}
void CPropFloorButton::PressingBoxHasSetteledThink( void )
{
if ( gpGlobals->maxClients == 1 && (V_strcmp( gpGlobals->mapname.ToCStr(), "sp_a2_bts1" ) != 0)
&& (V_strcmp( gpGlobals->mapname.ToCStr(), "mp_coop_catapult_1" ) != 0) )
{
UTIL_RecordAchievementEvent( "ACH.BOX_HOLE_IN_ONE" );
}
SetContextThink( NULL, gpGlobals->curtime, s_pszPressingBoxHasSetteledThinkContext );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropFloorButton::UpdateOnRemove( void )
{
if ( m_hButtonTrigger )
{
UTIL_Remove( m_hButtonTrigger );
m_hButtonTrigger = NULL;
}
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose: return floor button model name
//-----------------------------------------------------------------------------
const char *CPropFloorButton::GetButtonModelName()
{
if ( m_ModelName == NULL_STRING )
return PROP_FLOOR_BUTTON_DEFAULT_MODEL_NAME;
return STRING( m_ModelName );
}
//-----------------------------------------------------------------------------
// Purpose: Press the button
//-----------------------------------------------------------------------------
CEG_NOINLINE void CPropFloorButton::Press( CBaseEntity *pActivator )
{
// play the down sequence
ResetSequence( m_DownSequence );
// set the button state for the client
m_bButtonState = true;
// Change the skin
SetSkin( button_on_skin );
CEG_PROTECT_MEMBER_FUNCTION( CPropFloorButton_Press );
// call the function that fires the OnPressed output
OnPressed( pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: UnPress the button
//-----------------------------------------------------------------------------
void CPropFloorButton::UnPress( CBaseEntity *pActivator )
{
// play the up sequence
ResetSequence( m_UpSequence );
// set the button state for the client
m_bButtonState = false;
// Change the skin
SetSkin( button_off_skin );
// call the function that fires the OnUnPressed output
OnUnPressed( pActivator );
}
void CPropFloorButton::InputPressIn( inputdata_t &inputdata )
{
Press( inputdata.pActivator );
}
void CPropFloorButton::InputPressOut( inputdata_t &inputdata )
{
UnPress( inputdata.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: Fire output for button being pressed
//-----------------------------------------------------------------------------
void CPropFloorButton::OnPressed( CBaseEntity* pActivator )
{
if ( pActivator != NULL )
{
CBaseEntity *pOther = dynamic_cast<CBaseEntity*>(pActivator);
if ( GameRules() && GameRules()->IsMultiplayer() && pOther && pOther->IsPlayer() )
{
if ( pOther->GetTeamNumber() == TEAM_RED )
{
m_OnPressedOrange.FireOutput( pOther, this );
}
else if ( pOther->GetTeamNumber() == TEAM_BLUE )
{
m_OnPressedBlue.FireOutput( pOther, this );
}
}
if( FClassnameIs( pActivator, "prop_monster_box" ) )
{
CPropMonsterBox* pMonsterBox = static_cast<CPropMonsterBox*>( pActivator );
pMonsterBox->BecomeBox( false );
}
}
// If this button was pressed without touching the player, fire the special output used for the 'hole in one' achievement.
if ( UTIL_IsWeightedCube( pActivator ) )
{
CPropWeightedCube *pCube = (CPropWeightedCube*)pActivator;
Assert( pCube );
if ( pCube )
{
if ( pCube->WasTouchedByPlayer() == false )
{
// HACK: this delay is a guess at how long it takes to be sure the box has setteled...
SetContextThink( &CPropFloorButton::PressingBoxHasSetteledThink, gpGlobals->curtime + 2.0f, s_pszPressingBoxHasSetteledThinkContext );
}
}
}
m_OnPressed.FireOutput( pActivator, this );
}
//-----------------------------------------------------------------------------
// Purpose: Fire output when button has reset after being pressed
//-----------------------------------------------------------------------------
void CPropFloorButton::OnUnPressed( CBaseEntity* pActivator )
{
if( pActivator && FClassnameIs( pActivator, "prop_monster_box" ) )
{
CPropMonsterBox* pMonsterBox = static_cast<CPropMonsterBox*>( pActivator );
pMonsterBox->BecomeMonster( false );
}
SetContextThink( NULL, gpGlobals->curtime, s_pszPressingBoxHasSetteledThinkContext );
// fire the OnUnPressed output
m_OnUnPressed.FireOutput( pActivator, this );
}
//-----------------------------------------------------------------------------
// Purpose: Create triggers for button
//-----------------------------------------------------------------------------
void CPropFloorButton::CreateTriggers( void )
{
Vector vecOrigin = GetAbsOrigin();
// trigger size
Vector vecMins( -20,-20,0 );
Vector vecMaxs( 20,20,14 );
// Create the button trigger
m_hButtonTrigger = CPortalButtonTrigger::Create( vecOrigin, GetAbsAngles(), vecMins, vecMaxs, this );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropFloorButton::TriggerStartTouch( CBaseEntity *pOther )
{
Press( pOther );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropFloorButton::TriggerEndTouch( CBaseEntity *pOther )
{
UnPress( pOther );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropFloorButton::ShouldPlayerTouch()
{
// Yes, players can touch the prop floor button
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Set the model skin
//-----------------------------------------------------------------------------
void CPropFloorButton::SetSkin( int skinNum )
{
m_nSkin = skinNum;
}
//-----------------------------------------------------------------------------
// Purpose: creates the button trigger
//-----------------------------------------------------------------------------
CPortalButtonTrigger *CPortalButtonTrigger::Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecMins, const Vector &vecMaxs, CPropFloorButton *pOwner )
{
CPortalButtonTrigger *pTrigger = (CPortalButtonTrigger *) CreateEntityByName( "trigger_portal_button" );
if ( pTrigger == NULL )
return NULL;
UTIL_SetOrigin( pTrigger, vecOrigin );
pTrigger->SetAbsAngles( vecAngles );
UTIL_SetSize( pTrigger, vecMins, vecMaxs );
DispatchSpawn( pTrigger );
pTrigger->SetParent( (CBaseEntity *) pOwner );
pTrigger->m_pOwnerButton = pOwner;
return pTrigger;
}
void CPortalButtonTrigger::StartTouch(CBaseEntity *pOther)
{
if( !pOther->IsMarkedForDeletion() && FClassnameIs( pOther, "prop_weighted_cube") )
{
if ( PassesTriggerFilters( pOther ) )
{
//Set the cube to activate
CPropWeightedCube* pCube = assert_cast<CPropWeightedCube*>( pOther );
if( pCube )
{
pCube->SetActivated( true );
}
}
}
BaseClass::StartTouch( pOther );
}
void CPortalButtonTrigger::EndTouch(CBaseEntity *pOther)
{
if( !pOther->IsMarkedForDeletion() && FClassnameIs( pOther, "prop_weighted_cube") )
{
if ( PassesTriggerFilters( pOther ) )
{
//Set the cube to deactivate
CPropWeightedCube* pCube = assert_cast<CPropWeightedCube*>( pOther );
if( pCube )
{
pCube->SetActivated( false );
}
}
}
BaseClass::EndTouch( pOther );
}
//----------------------------------------------------------------------------------
// Purpose: checks filters on trigger in addition to specific filters (player, cube)
//----------------------------------------------------------------------------------
bool CPortalButtonTrigger::PassesTriggerFilters(CBaseEntity *pOther)
{
bool bPassedFilter = BaseClass::PassesTriggerFilters( pOther );
// did I fail the baseclass filter check?
if ( !bPassedFilter )
return false;
// are players allowed to touch me?
if ( m_pOwnerButton->ShouldPlayerTouch() )
{
// did a player touch me?
if ( pOther->IsPlayer() )
return true;
}
// did a cube touch me?
if ( FClassnameIs( pOther, "prop_weighted_cube") || FClassnameIs( pOther, "prop_monster_box") )
{
CPropWeightedCube *pCube = static_cast<CPropWeightedCube*>( pOther );
bool bIsBall = pCube && pCube->GetCubeType() == CUBE_SPHERE;
if ( ( bIsBall && m_pOwnerButton->AcceptsBall() ) || //If the button accepts balls and this is a ball ( floor, under and ball buttons )
( !bIsBall && !m_pOwnerButton->OnlyAcceptBall() ) ) //If the button doesn't only accept balls and this is not a ball ( cube buttons )
{
return true;
}
}
// failed filter check
return false;
}
//----------------------------------------------------------------------------------
// Purpose: called only when the trigger is touched for the first time
//----------------------------------------------------------------------------------
void CPortalButtonTrigger::OnStartTouchAll( CBaseEntity *pOther )
{
// call the button's start touch function
if ( m_pOwnerButton )
{
m_pOwnerButton->TriggerStartTouch( pOther );
}
BaseClass::OnStartTouchAll( pOther );
}
//----------------------------------------------------------------------------------
// Purpose: called when the last object stops touching the trigger
//----------------------------------------------------------------------------------
void CPortalButtonTrigger::OnEndTouchAll( CBaseEntity *pOther )
{
// call the button's end touch function
if ( m_pOwnerButton )
{
m_pOwnerButton->TriggerEndTouch( pOther );
}
BaseClass::OnEndTouchAll( pOther );
}
//----------------------------------------------------------------------------------
// Purpose: spawn the trigger
//----------------------------------------------------------------------------------
void CPortalButtonTrigger::Spawn( void )
{
// Setup our basic attributes
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_OBB );
SetSolidFlags( FSOLID_NOT_SOLID|FSOLID_TRIGGER );
AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS|SF_TRIGGER_ALLOW_PHYSICS );
BaseClass::Spawn();
}
//
// Floor Button
//
ConVar sv_slippery_cube_button( "sv_slippery_cube_button", "1", FCVAR_CHEAT );
class CPropFloorCubeButton : public CPropFloorButton
{
public:
DECLARE_CLASS( CPropFloorCubeButton, CPropFloorButton );
DECLARE_DATADESC();
CPropFloorCubeButton();
virtual const char *GetButtonModelName();
virtual bool ShouldPlayerTouch();
virtual bool OnlyAcceptBall( void ) { return false; }
virtual bool AcceptsBall( void ) { return m_bAcceptsBall; }
friend class CPortalButtonTrigger;
protected:
virtual void Press( CBaseEntity *pActivator );
virtual void UnPress( CBaseEntity *pActivator );
private:
int m_nStoredMaterialIndex;
bool m_bAcceptsBall;
};
LINK_ENTITY_TO_CLASS( prop_floor_cube_button, CPropFloorCubeButton );
BEGIN_DATADESC( CPropFloorCubeButton )
DEFINE_KEYFIELD( m_bAcceptsBall, FIELD_BOOLEAN, "AcceptsBall" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPropFloorCubeButton::CPropFloorCubeButton()
: m_bAcceptsBall( true )
{
}
//-----------------------------------------------------------------------------
// Purpose: get cube button model name
//-----------------------------------------------------------------------------
const char *CPropFloorCubeButton::GetButtonModelName()
{
return PROP_FLOOR_CUBE_BUTTON_MODEL_NAME;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropFloorCubeButton::ShouldPlayerTouch()
{
// Players may not touch the cube button
return false;
}
void CPropFloorCubeButton::Press( CBaseEntity *pActivator )
{
if ( sv_slippery_cube_button.GetBool() )
{
// set the object material type to be ice so it will slip into the cube button
IPhysicsObject* pPhysObject = pActivator->VPhysicsGetObject();
if( pPhysObject )
{
m_nStoredMaterialIndex = pPhysObject->GetMaterialIndex();
pPhysObject->SetMaterialIndex( physprops->GetSurfaceIndex( "ice" ) );
}
}
BaseClass::Press( pActivator );
}
void CPropFloorCubeButton::UnPress( CBaseEntity *pActivator )
{
if ( sv_slippery_cube_button.GetBool() )
{
IPhysicsObject* pPhysObject = pActivator->VPhysicsGetObject();
if( pPhysObject )
{
pPhysObject->SetMaterialIndex( m_nStoredMaterialIndex );
}
}
BaseClass::UnPress( pActivator );
}
class CPropFloorBallButton : public CPropFloorButton
{
public:
DECLARE_CLASS( CPropFloorBallButton, CPropFloorButton );
DECLARE_DATADESC();
virtual const char *GetButtonModelName();
virtual bool ShouldPlayerTouch();
virtual bool OnlyAcceptBall( void ) { return true; }
virtual bool AcceptsBall( void ) { return true; }
virtual void CreateTriggers( void );
friend class CPortalButtonTrigger;
private:
int m_nStoredMaterialIndex;
};
LINK_ENTITY_TO_CLASS( prop_floor_ball_button, CPropFloorBallButton );
BEGIN_DATADESC( CPropFloorBallButton )
END_DATADESC()
void CPropFloorBallButton::CreateTriggers( void )
{
Vector vecOrigin = GetAbsOrigin();
// trigger size
Vector vecMins( -5,-5,0 );
Vector vecMaxs( 5,5,14 );
// Create the button trigger
m_hButtonTrigger = CPortalButtonTrigger::Create( vecOrigin, GetAbsAngles(), vecMins, vecMaxs, this );
}
//-----------------------------------------------------------------------------
// Purpose: get cube button model name
//-----------------------------------------------------------------------------
const char *CPropFloorBallButton::GetButtonModelName()
{
return PROP_FLOOR_BALL_BUTTON_MODEL_NAME;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropFloorBallButton::ShouldPlayerTouch()
{
// Players may not touch the cube button
return false;
}
class CPropUnderFloorButton : public CPropFloorButton
{
public:
DECLARE_CLASS( CPropUnderFloorButton, CPropFloorButton );
DECLARE_DATADESC();
virtual const char *GetButtonModelName();
friend class CPortalButtonTrigger;
protected:
virtual void LookUpAnimationSequences( void );
virtual void CreateTriggers( void );
};
LINK_ENTITY_TO_CLASS( prop_under_floor_button, CPropUnderFloorButton );
BEGIN_DATADESC( CPropUnderFloorButton )
END_DATADESC()
const char *CPropUnderFloorButton::GetButtonModelName()
{
return PROP_UNDER_FLOOR_BUTTON_MODEL_NAME;
}
void CPropUnderFloorButton::LookUpAnimationSequences( void )
{
// look up animation sequences
m_UpSequence = LookupSequence( "release" );
m_DownSequence = LookupSequence( "press" );
}
void CPropUnderFloorButton::CreateTriggers( void )
{
Vector vecOrigin = GetAbsOrigin();
// trigger size
Vector vecMins( -30, -30, 0 );
Vector vecMaxs( 30, 30, 17 );
// Create the button trigger
m_hButtonTrigger = CPortalButtonTrigger::Create( vecOrigin, GetAbsAngles(), vecMins, vecMaxs, this );
}

View File

@@ -0,0 +1,982 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Door pairs that can be seamlessly linked via portals to connect disparate areas
//
//===========================================================================//
#include "cbase.h"
#include "portal_base2d.h"
#include "physics_bone_follower.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//=============================================================================
// Non-animated linked portal door
//=============================================================================
class CLinkedPortalDoor : public CBaseAnimating
{
public:
DECLARE_CLASS( CLinkedPortalDoor, CBaseAnimating );
DECLARE_DATADESC();
DECLARE_ENT_SCRIPTDESC();
CLinkedPortalDoor( void );
virtual int DrawDebugTextOverlays( void );
virtual void UpdateOnRemove( void );
virtual void Spawn( void );
virtual void Activate( void );
virtual void NotifyPortalEvent( PortalEvent_t nEventType, CPortal_Base2D *pNotifier );
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
const char *GetPartnername() { return STRING( m_szPartnerName ); }
HSCRIPT ScriptGetPartnerInstanceHandle(){ return ToHScript( m_hPartner ); }
void DisableLinkageThink( void );
virtual void Open( CBaseEntity *pActivator );
virtual void Close( CBaseEntity *pActivator );
protected:
// These functions are called directly to avoid recursion
virtual void OpenInternal( CBaseEntity *pActivator );
virtual void CloseInternal( CBaseEntity *pActivator );
// Creation/removal of internal members
virtual void Destroy( void );
void ClearLinkPartner( void );
virtual void OnOpen( void ) {}
virtual void OnClose( void ) {}
virtual bool IsOpen();
virtual bool IsClosed();
// Prevents/allows portal functionality
void DisableLinkage( void );
void EnableLinkage( void );
void SetPartner( CLinkedPortalDoor *pPartner );
void SetPartnerByName( string_t iszentityname );
void InputSetPartner( inputdata_t &input );
void InputOpen( inputdata_t &input );
void InputClose( inputdata_t &input );
virtual const Vector &OffsetPosition( void ) const { return vec3_origin; }
string_t m_szPartnerName; // name of our linked CLinkedPortalDoor partner
bool m_bIsLinkedToPartner; // if true, door links to partner and does not move physically
float m_flPortalCloseDelay;
int m_nWidth;
int m_nHeight;
bool m_bStartActive;
COutputEvent m_OnOpen;
COutputEvent m_OnClose;
COutputEvent m_OnEntityTeleportFromMe;
COutputEvent m_OnPlayerTeleportFromMe;
COutputEvent m_OnEntityTeleportToMe;
COutputEvent m_OnPlayerTeleportToMe;
CHandle<CPortal_Base2D> m_hPortal;
CHandle<CLinkedPortalDoor> m_hPartner;
};
BEGIN_DATADESC( CLinkedPortalDoor )
DEFINE_INPUTFUNC( FIELD_STRING, "SetPartner", InputSetPartner ),
DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ),
DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ),
DEFINE_KEYFIELD( m_nWidth, FIELD_INTEGER, "width" ),
DEFINE_KEYFIELD( m_nHeight, FIELD_INTEGER, "height" ),
DEFINE_KEYFIELD( m_szPartnerName, FIELD_STRING, "partnername" ),
DEFINE_KEYFIELD( m_bStartActive, FIELD_BOOLEAN, "startactive" ),
DEFINE_FIELD( m_bIsLinkedToPartner, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hPortal, FIELD_EHANDLE ),
DEFINE_FIELD( m_hPartner, FIELD_EHANDLE ),
DEFINE_THINKFUNC( DisableLinkageThink ),
DEFINE_OUTPUT( m_OnOpen, "OnOpen" ),
DEFINE_OUTPUT( m_OnClose, "OnClose" ),
DEFINE_OUTPUT( m_OnEntityTeleportFromMe, "OnEntityTeleportFromMe" ),
DEFINE_OUTPUT( m_OnPlayerTeleportFromMe, "OnPlayerTeleportFromMe" ),
DEFINE_OUTPUT( m_OnEntityTeleportToMe, "OnEntityTeleportToMe" ),
DEFINE_OUTPUT( m_OnPlayerTeleportToMe, "OnPlayerTeleportToMe" ),
END_DATADESC()
BEGIN_ENT_SCRIPTDESC( CLinkedPortalDoor, CBaseAnimating, "Door linked by portals to a partner portal door")
DEFINE_SCRIPTFUNC( GetPartnername, "Returns the partnername of the door." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetPartnerInstanceHandle, "GetPartnerInstance", "Get the instance handle of the door's linked partner" )
END_SCRIPTDESC();
LINK_ENTITY_TO_CLASS( linked_portal_door, CLinkedPortalDoor );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CLinkedPortalDoor::CLinkedPortalDoor( void ) :
m_szPartnerName( NULL_STRING ),
m_bIsLinkedToPartner( false ),
m_hPortal( NULL ),
m_hPartner( NULL ),
m_flPortalCloseDelay( 0.0f ),
m_nWidth( 128.0f ),
m_nHeight( 128.0f ),
m_bStartActive( false )
{
}
//-----------------------------------------------------------------------------
// Purpose: Draw any debug text overlays
// Output : Current text offset from the top
//-----------------------------------------------------------------------------
int CLinkedPortalDoor::DrawDebugTextOverlays( void )
{
int text_offset = BaseClass::DrawDebugTextOverlays();
if (m_debugOverlays & OVERLAY_TEXT_BIT)
{
char tempstr[512];
// print flame size
Q_snprintf(tempstr,sizeof(tempstr),"linked partner: %s", m_szPartnerName.ToCStr() );
EntityText(text_offset,tempstr,0);
text_offset++;
}
return text_offset;
}
//-----------------------------------------------------------------------------
// Purpose: Cleanup when we're killed off
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::UpdateOnRemove( void )
{
Destroy();
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_NONE );
AddEffects( EF_NOSHADOW );
m_flPortalCloseDelay = 0.0f;
// Reseat the portal on the wall
Vector vForward, vUp;
GetVectors( &vForward, NULL, &vUp );
// Create our portal
m_hPortal = (CPortal_Base2D *) CreateEntityByName( "portal_base2D" );
Assert( m_hPortal );
if ( m_hPortal )
{
//
m_hPortal->m_bIsPortal2 = false;
m_hPortal->SetOwnerEntity( this );
m_hPortal->SetModel( "models/portals/portal1.mdl" );
// Setup our bounds
Vector mins, maxs;
CollisionProp()->WorldSpaceAABB( &mins, &maxs );
UTIL_SetSize( m_hPortal, mins, maxs );
m_hPortal->Resize( m_nWidth, m_nHeight ); // Default size (get from map)
m_hPortal->Teleport( &GetAbsOrigin(), &GetAbsAngles(), NULL );
// Set up the correct state for activation
m_hPortal->SetActive( m_bStartActive );
// Go!
DispatchSpawn( m_hPortal );
// Listen for our events
m_hPortal->AddPortalEventListener( this );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::Activate( void )
{
BaseClass::Activate();
// Link to our initial partner (if any)
SetPartnerByName( m_szPartnerName );
}
//-----------------------------------------------------------------------------
// Purpose: Push our transmit state down to our attached portal
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
{
// Are we already marked for transmission?
if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
return;
BaseClass::SetTransmit( pInfo, bAlways );
// Force our attached entities to go too...
if ( m_hPortal )
{
m_hPortal->SetTransmit( pInfo, bAlways );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::Destroy( void )
{
DisableLinkage();
if ( m_hPortal )
{
m_hPortal->m_hLinkedPortal = NULL;
}
m_hPartner = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Disables portal functionality.
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::DisableLinkage( void )
{
if ( m_bIsLinkedToPartner == false )
return;
m_bIsLinkedToPartner = false;
if ( m_hPortal )
{
m_hPortal->SetActive( false );
m_hPortal->m_PortalSimulator.DetachFromLinked();
}
if ( m_hPartner )
{
m_hPartner->DisableLinkage();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::ClearLinkPartner( void )
{
DisableLinkage();
if ( m_hPortal )
{
m_hPortal->m_hLinkedPortal = NULL;
}
m_hPartner = NULL;
m_szPartnerName = NULL_STRING;
}
//-----------------------------------------------------------------------------
// Purpose: Enables portal functionality
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::EnableLinkage( void )
{
if ( m_hPartner )
{
Assert( ((CLinkedPortalDoor *)m_hPartner.Get())->GetEntityName() == m_szPartnerName );
}
else if ( m_szPartnerName != NULL_STRING )
{
SetPartnerByName( m_szPartnerName );
}
else
{
// Is this valid?
Assert( 0 );
}
// Already linked
if ( m_bIsLinkedToPartner )
return;
if ( m_hPartner )
{
// We're whatever our partner is not
m_hPortal->m_bIsPortal2 = !m_hPartner->m_hPortal->m_bIsPortal2;
Vector vForward, vRight, vUp;
GetVectors( &vForward, &vRight, &vUp );
Vector vOffset = OffsetPosition();
m_hPortal->NewLocation( GetAbsOrigin() + (vForward * 0.5f) + ( vForward * vOffset.x ) + ( vRight * vOffset.y ) + ( vUp * vOffset.z ), GetAbsAngles() );
m_bIsLinkedToPartner = true;
// Set up our linkage here
m_hPortal->m_hLinkedPortal = m_hPartner->m_hPortal;
m_hPartner->m_hPortal->m_hLinkedPortal = m_hPortal;
m_hPortal->UpdatePortalLinkage();
// Force them on as well
((CLinkedPortalDoor *)m_hPartner.Get())->EnableLinkage();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::SetPartner( CLinkedPortalDoor *pPartner )
{
// Don't bother if we've done this already
if ( pPartner == ((CLinkedPortalDoor *)m_hPartner.Get()) )
return;
if ( pPartner == NULL )
{
// This is invalid!
Assert( pPartner != NULL );
DisableLinkage();
return;
}
else if ( m_hPartner && ((CLinkedPortalDoor *)m_hPartner.Get()) != pPartner )
{
// Unlink from our current partner!
CLinkedPortalDoor *pPartner = ((CLinkedPortalDoor *)m_hPartner.Get());
pPartner->ClearLinkPartner();// Force our partner to forget about us as well
ClearLinkPartner();
}
// Take the new partner
m_szPartnerName = pPartner->GetEntityName();
m_hPartner = pPartner;
m_hPortal->m_hLinkedPortal = pPartner->m_hPortal;
// This must be reciprocal!
pPartner->SetPartner( this );
// If we're already open, then update our portal immediately
if ( m_hPortal->IsActive() )
{
EnableLinkage();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::SetPartnerByName( string_t iszentityname )
{
// Take the new partner name
m_szPartnerName = iszentityname;
// Find the entity and link to it
CBaseEntity* pEnt = gEntList.FindEntityByName( NULL, STRING(m_szPartnerName), this, NULL, NULL, NULL );
if ( pEnt )
{
SetPartner( dynamic_cast<CLinkedPortalDoor*>(pEnt) );
}
else
{
Warning( "prop_portal_linked_door '%s' failed to link to partner named: '%s'\n", GetDebugName(), STRING(m_szPartnerName) );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::InputSetPartner( inputdata_t &input )
{
SetPartnerByName( input.value.StringID() );
}
//-----------------------------------------------------------------------------
// Purpose: Open the door input
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::InputOpen( inputdata_t &input )
{
Open( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: "Open" the door
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::Open( CBaseEntity *pActivator )
{
if ( m_hPartner == NULL )
{
SetPartnerByName( m_szPartnerName );
}
// Open ourself
OpenInternal( pActivator );
// Force our partner to respond in kind
if ( m_hPartner != NULL )
{
((CLinkedPortalDoor *)m_hPartner.Get())->OpenInternal( pActivator );
}
EnableLinkage();
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the door is open
//-----------------------------------------------------------------------------
bool CLinkedPortalDoor::IsOpen()
{
return m_hPortal->IsActive();
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the door is closed
//-----------------------------------------------------------------------------
bool CLinkedPortalDoor::IsClosed()
{
return !(m_hPortal->IsActive());
}
//-----------------------------------------------------------------------------
// Purpose: Internal version which is non-public
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::OpenInternal( CBaseEntity *pActivator )
{
SetContextThink( NULL, TICK_NEVER_THINK, "DisableLinkageThink" );
// Don't fire the output if the door is already opened
if ( IsOpen() )
return;
// Fire the OnOpen output
m_OnOpen.FireOutput( this, this );
OnOpen();
}
//-----------------------------------------------------------------------------
// Purpose: "Close" the door
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::Close( CBaseEntity *pActivator )
{
CloseInternal( pActivator );
// Get our partner to open
if ( m_hPartner != NULL )
{
((CLinkedPortalDoor *)m_hPartner.Get())->CloseInternal( pActivator );
// Close right now unless being overridden
if ( m_flPortalCloseDelay == 0.0f )
{
DisableLinkage();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Internal non-public version
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::CloseInternal( CBaseEntity *pActivator )
{
// Don't fire the output if the door is already closed
if ( IsClosed() )
return;
// Fire the OnClose output
m_OnClose.FireOutput( this, this );
OnClose();
}
//-----------------------------------------------------------------------------
// Purpose: Close the door input
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::InputClose( inputdata_t &input )
{
Close( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: Close the portal down
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::DisableLinkageThink( void )
{
DisableLinkage();
}
//-----------------------------------------------------------------------------
// Purpose: Handle entity teleports from / to my portal
//-----------------------------------------------------------------------------
void CLinkedPortalDoor::NotifyPortalEvent( PortalEvent_t nEventType, CPortal_Base2D *pNotifier )
{
BaseClass::NotifyPortalEvent( nEventType, pNotifier );
switch ( nEventType )
{
case PORTALEVENT_ENTITY_TELEPORTED_TO:
m_OnEntityTeleportToMe.FireOutput( this, this );
break;
case PORTALEVENT_ENTITY_TELEPORTED_FROM:
m_OnEntityTeleportFromMe.FireOutput( this, this );
break;
case PORTALEVENT_PLAYER_TELEPORTED_TO:
m_OnPlayerTeleportToMe.FireOutput( this, this );
break;
case PORTALEVENT_PLAYER_TELEPORTED_FROM:
m_OnPlayerTeleportFromMe.FireOutput( this, this );
break;
}
}
#define PORTAL_LINKED_DOOR_MODEL_NAME "models/props/portal_door.mdl"
#define PORTAL_LINKED_DOOR_RESTING_SURFACE_TRACE_DIST 1.5f
//=============================================================================
// Animated linked portal door
//=============================================================================
class CPropLinkedPortalDoor : public CLinkedPortalDoor
{
public:
DECLARE_CLASS( CPropLinkedPortalDoor, CLinkedPortalDoor );
DECLARE_SERVERCLASS();
DECLARE_DATADESC();
DECLARE_ENT_SCRIPTDESC();
CPropLinkedPortalDoor( void );
virtual void Precache( void );
virtual void Spawn( void );
virtual bool CreateVPhysics( void );
virtual void Activate( void );
virtual bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
virtual void AnimateThink( void );
virtual bool IsOpen( void ) { return ( GetSequence() == m_nSequenceOpenIdle || GetSequence() == m_nSequenceOpen ); }
virtual bool IsClosed( void ) { return ( GetSequence() == m_nSequenceCloseIdle || GetSequence() == m_nSequenceClose ); }
protected:
void CreateBoneFollowers( void );
// Creation/removal of internal members
virtual void Destroy( void );
virtual void OpenInternal( CBaseEntity *pActivator );
virtual void CloseInternal( CBaseEntity *pActivator );
void OnFullyOpened( void );
void OnFullyClosed( void );
virtual void OnOpen( void );
virtual void OnClose( void );
virtual void SetPartner( CLinkedPortalDoor *pPartner );
virtual const Vector &OffsetPosition( void ) const { static Vector vOffsetVector( 0, 0, 45 ); return vOffsetVector; }
COutputEvent m_OnFullyOpen;
COutputEvent m_OnFullyClosed;
int m_nSequenceOpen;
int m_nSequenceOpenIdle;
int m_nSequenceClose;
int m_nSequenceCloseIdle;
CBoneFollowerManager m_BoneFollowerManager;
};
IMPLEMENT_SERVERCLASS_ST( CPropLinkedPortalDoor, DT_PropLinkedPortalDoor )
END_SEND_TABLE()
BEGIN_DATADESC( CPropLinkedPortalDoor )
DEFINE_INPUTFUNC( FIELD_STRING, "SetPartner", InputSetPartner ),
DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ),
DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ),
DEFINE_KEYFIELD( m_szPartnerName, FIELD_STRING, "partnername" ),
DEFINE_FIELD( m_bIsLinkedToPartner, FIELD_BOOLEAN ),
DEFINE_FIELD( m_nSequenceOpen, FIELD_INTEGER ),
DEFINE_FIELD( m_nSequenceOpenIdle, FIELD_INTEGER ),
DEFINE_FIELD( m_nSequenceClose, FIELD_INTEGER ),
DEFINE_FIELD( m_nSequenceCloseIdle, FIELD_INTEGER ),
DEFINE_FIELD( m_hPortal, FIELD_EHANDLE ),
DEFINE_FIELD( m_hPartner, FIELD_EHANDLE ),
DEFINE_THINKFUNC( AnimateThink ),
DEFINE_THINKFUNC( DisableLinkageThink ),
DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ),
DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ),
DEFINE_OUTPUT( m_OnOpen, "OnOpen" ),
DEFINE_OUTPUT( m_OnClose, "OnClose" ),
DEFINE_OUTPUT( m_OnEntityTeleportFromMe, "OnEntityTeleportFromMe" ),
DEFINE_OUTPUT( m_OnPlayerTeleportFromMe, "OnPlayerTeleportFromMe" ),
DEFINE_OUTPUT( m_OnEntityTeleportToMe, "OnEntityTeleportToMe" ),
DEFINE_OUTPUT( m_OnPlayerTeleportToMe, "OnPlayerTeleportToMe" ),
DEFINE_EMBEDDED( m_BoneFollowerManager ),
END_DATADESC()
BEGIN_ENT_SCRIPTDESC( CPropLinkedPortalDoor, CBaseAnimating, "Door linked by portals to a partner portal door")
DEFINE_SCRIPTFUNC( GetPartnername, "Returns the partnername of the door." )
DEFINE_SCRIPTFUNC_NAMED( ScriptGetPartnerInstanceHandle, "GetPartnerInstance", "Get the instance handle of the door's linked partner" )
END_SCRIPTDESC();
LINK_ENTITY_TO_CLASS( prop_linked_portal_door, CPropLinkedPortalDoor );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPropLinkedPortalDoor::CPropLinkedPortalDoor( void ) :
m_nSequenceOpen( -1 ),
m_nSequenceOpenIdle( -1 ),
m_nSequenceClose( -1 ),
m_nSequenceCloseIdle( -1 )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::Precache( void )
{
PrecacheModel( PORTAL_LINKED_DOOR_MODEL_NAME );
PrecacheScriptSound( "prop_portal_door.open" );
PrecacheScriptSound( "prop_portal_door.close" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_VPHYSICS );
AddEffects( EF_NOSHADOW );
m_flPortalCloseDelay = 0.5f;
SetModel( PORTAL_LINKED_DOOR_MODEL_NAME );
CreateVPhysics();
// Cache off our sequences for quick lookup later
m_nSequenceOpen = LookupSequence( "open" );
m_nSequenceOpenIdle = LookupSequence( "idleopen" );
m_nSequenceClose = LookupSequence( "close" );
m_nSequenceCloseIdle = LookupSequence( "idleclose" );
// Reseat the portal on the wall
Vector vForward, vUp;
GetVectors( &vForward, NULL, &vUp );
Vector vHeightOffset = vUp * 45.0f;
Vector vCenterTraceOrigin = GetAbsOrigin() + ( vForward * 12.0f ) + vHeightOffset; //HACKHACK: 45 up because we're using a model with a bottom oriented origin, but portals use centered origin
trace_t tr;
UTIL_TraceLine( vCenterTraceOrigin, vCenterTraceOrigin - ( vForward * 36.0f ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
Vector vFinalPos = tr.endpos - vHeightOffset; // Take the fix-up back off
Teleport( &vFinalPos, &GetAbsAngles(), NULL );
// Start closed
ResetSequence( m_nSequenceCloseIdle );
// Create our portal
if ( m_hPortal )
{
m_hPortal->SetModel( "models/portals/portal1.mdl" );
// Setup our bounds
Vector mins, maxs;
CollisionProp()->WorldSpaceAABB( &mins, &maxs );
UTIL_SetSize( m_hPortal, mins, maxs );
m_hPortal->Resize( 78, 82 ); // FIXME: Need to get these dimensions from somewhere else!
Vector vPos = tr.endpos + (vForward * 0.5f);
m_hPortal->Teleport( &vPos, &GetAbsAngles(), NULL );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropLinkedPortalDoor::CreateVPhysics( void )
{
CreateBoneFollowers();
if ( m_BoneFollowerManager.GetNumBoneFollowers() )
{
if ( GetSolidFlags() & FSOLID_NOT_SOLID )
{
// Already non-solid? Must need bone followers for some other reason
// like needing to attach constraints to this object
for ( int i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
{
CBaseEntity *pFollower = m_BoneFollowerManager.GetBoneFollower(i)->hFollower;
if ( pFollower )
{
pFollower->AddSolidFlags(FSOLID_NOT_SOLID);
}
}
}
// If our collision is through bone followers, we want to be non-solid
AddSolidFlags( FSOLID_NOT_SOLID );
// add these for the client, FSOLID_NOT_SOLID should keep it out of the testCollision code
// except in the case of TraceEntity() which the client does for impact effects
AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
return true;
}
else
{
VPhysicsInitStatic();
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::CreateBoneFollowers( void )
{
// already created bone followers? Don't do so again.
if ( m_BoneFollowerManager.GetNumBoneFollowers() )
return;
KeyValues *modelKeyValues = new KeyValues("");
if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
{
// Do we have a bone follower section?
KeyValues *pkvBoneFollowers = modelKeyValues->FindKey("bone_followers");
if ( pkvBoneFollowers )
{
// Loop through the list and create the bone followers
KeyValues *pBone = pkvBoneFollowers->GetFirstSubKey();
while ( pBone )
{
// Add it to the list
const char *pBoneName = pBone->GetString();
m_BoneFollowerManager.AddBoneFollower( this, pBoneName );
pBone = pBone->GetNextKey();
}
}
modelKeyValues->deleteThis();
}
// if we got here, we don't have a bone follower section, but if we have a ragdoll
// go ahead and create default bone followers for it
if ( m_BoneFollowerManager.GetNumBoneFollowers() == 0 )
{
vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() );
if ( pCollide && pCollide->solidCount > 1 )
{
CreateBoneFollowersFromRagdoll( this, &m_BoneFollowerManager, pCollide );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropLinkedPortalDoor::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
{
if ( IsSolidFlagSet(FSOLID_NOT_SOLID) )
{
// if this entity is marked non-solid and custom test it must have bone followers
if ( IsSolidFlagSet( FSOLID_CUSTOMBOXTEST ) && IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ))
{
for ( int i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
{
CBaseEntity *pEntity = m_BoneFollowerManager.GetBoneFollower(i)->hFollower;
if ( pEntity && pEntity->TestCollision(ray, mask, trace) )
return true;
}
}
}
// PORTAL2: This is a change from shipped code, but should be benign
return BaseClass::TestCollision( ray, mask, trace );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::Activate( void )
{
BaseClass::Activate();
// Start our animation cycle
SetThink( &CPropLinkedPortalDoor::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Animate and catch edge cases for us stopping / starting our animation
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::AnimateThink( void )
{
// Update our animation
StudioFrameAdvance();
DispatchAnimEvents( this );
m_BoneFollowerManager.UpdateBoneFollowers( this );
if ( IsSequenceFinished() )
{
int nSequence = GetSequence();
if ( nSequence == m_nSequenceOpen )
{
int nIdleSequence = m_nSequenceOpenIdle;
ResetSequence( nIdleSequence );
OnFullyOpened();
}
else if ( nSequence == m_nSequenceClose )
{
int nIdleSequence = m_nSequenceCloseIdle;
ResetSequence( nIdleSequence );
OnFullyClosed();
}
}
SetThink( &CPropLinkedPortalDoor::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Open the door
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::OpenInternal( CBaseEntity *pActivator )
{
BaseClass::OpenInternal( pActivator );
// Only set the door open sequence if the door is closed
if ( IsClosed() )
{
ResetSequence( m_nSequenceOpen );
}
}
//-----------------------------------------------------------------------------
// Purpose: Close the door
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::CloseInternal( CBaseEntity *pActivator )
{
BaseClass::CloseInternal( pActivator );
// Only set the door closed sequence if the door is open
if ( IsOpen() )
{
ResetSequence( m_nSequenceClose );
}
}
//-----------------------------------------------------------------------------
// Purpose: OnOpened output
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::OnOpen( void )
{
// Play door open sound
EmitSound( "prop_portal_door.open" );
}
//-----------------------------------------------------------------------------
// Purpose: OnClosed output
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::OnClose( void )
{
// Play door close sound
EmitSound( "prop_portal_door.close" );
}
//-----------------------------------------------------------------------------
// Purpose: OnFullyOpened output
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::OnFullyOpened( void )
{
m_OnFullyOpen.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose: Close the door
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::OnFullyClosed( void )
{
m_OnFullyClosed.FireOutput( this, this );
SetContextThink( &CLinkedPortalDoor::DisableLinkageThink, gpGlobals->curtime + m_flPortalCloseDelay, "DisableLinkageThink" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::Destroy( void )
{
BaseClass::Destroy();
m_BoneFollowerManager.DestroyBoneFollowers();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropLinkedPortalDoor::SetPartner( CLinkedPortalDoor *pPartner )
{
BaseClass::SetPartner( pPartner );
// If we're already open, then update our portal immediately
if ( GetSequence() == m_nSequenceOpenIdle )
{
if ( IsOpen() )
{
pPartner->Open( this );
}
else
{
pPartner->Close( this );
}
}
}

View File

@@ -0,0 +1,457 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Test chamber doors
//
//===========================================================================//
#include "cbase.h"
#include "prop_testchamber_door.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define TESTCHAMBER_DOOR_MODEL_NAME "models/props/portal_door_combined.mdl"
#define TESTCHAMBER_DOOR_AREA_PORTAL_NEVER_FADE_DISTANCE 10000.0f
BEGIN_DATADESC( CPropTestChamberDoor )
DEFINE_FIELD( m_nSequenceOpen, FIELD_INTEGER ),
DEFINE_FIELD( m_nSequenceOpenIdle, FIELD_INTEGER ),
DEFINE_FIELD( m_nSequenceClose, FIELD_INTEGER ),
DEFINE_FIELD( m_nSequenceCloseIdle, FIELD_INTEGER ),
DEFINE_FIELD( m_bIsOpen, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bIsAnimating, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bIsLocked, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hAreaPortalWindow, FIELD_EHANDLE ),
DEFINE_KEYFIELD( m_strAreaPortalWindowName, FIELD_STRING, "AreaPortalWindow" ),
DEFINE_KEYFIELD( m_bUseAreaPortalFade, FIELD_BOOLEAN, "UseAreaPortalFade" ),
DEFINE_KEYFIELD( m_flAreaPortalFadeStartDistance, FIELD_FLOAT, "AreaPortalFadeStart" ),
DEFINE_KEYFIELD( m_flAreaPortalFadeEndDistance, FIELD_FLOAT, "AreaPortalFadeEnd" ),
DEFINE_THINKFUNC( AnimateThink ),
DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ),
DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ),
DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
DEFINE_INPUTFUNC( FIELD_VOID, "LockOpen", InputLockOpen ),
DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
DEFINE_OUTPUT( m_OnOpen, "OnOpen" ),
DEFINE_OUTPUT( m_OnClose, "OnClose" ),
DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ),
DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ),
DEFINE_EMBEDDED( m_BoneFollowerManager ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( prop_testchamber_door, CPropTestChamberDoor );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPropTestChamberDoor::CPropTestChamberDoor( void )
: m_bIsOpen( false ),
m_bIsAnimating( false ),
m_bIsLocked( false )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::Precache( void )
{
PrecacheModel( TESTCHAMBER_DOOR_MODEL_NAME );
PrecacheScriptSound( "prop_portal_door.open" );
PrecacheScriptSound( "prop_portal_door.close" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetMoveType( MOVETYPE_NONE );
SetSolid( SOLID_VPHYSICS );
AddEffects( EF_NOSHADOW );
SetModel( TESTCHAMBER_DOOR_MODEL_NAME );
CreateVPhysics();
// Cache off our sequences for quick lookup later
m_nSequenceOpen = LookupSequence( "open" );
m_nSequenceOpenIdle = LookupSequence( "idleopen" );
m_nSequenceClose = LookupSequence( "close" );
m_nSequenceCloseIdle = LookupSequence( "idleclose" );
// Start closed
ResetSequence( m_nSequenceOpen );
SetPlaybackRate( 0.0f );
//If an area portal window name has been specified
if( m_strAreaPortalWindowName != NULL_STRING )
{
CBaseEntity *pAreaPortalWindow = gEntList.FindEntityByName( NULL, m_strAreaPortalWindowName );
if( pAreaPortalWindow )
{
m_hAreaPortalWindow = (CFuncAreaPortalWindow*) pAreaPortalWindow;
AreaPortalClose();
}
else
{
DevWarning( "Could not find area portal window named %s for test chamber door %s\n", m_strAreaPortalWindowName.ToCStr(), m_iClassname.ToCStr() );
}
}
AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
// Never let crucial game components fade out!
SetFadeDistance( -1.0f, 0.0f );
SetGlobalFadeScale( 0.0f );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::Activate( void )
{
BaseClass::Activate();
// Start our animation cycle
SetThink( &CPropTestChamberDoor::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: Open the door input
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::UpdateOnRemove( void )
{
m_BoneFollowerManager.DestroyBoneFollowers();
BaseClass::UpdateOnRemove();
}
//-----------------------------------------------------------------------------
// Purpose: Open the door input
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::InputOpen( inputdata_t &input )
{
Open( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: "Open" the door
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::Open( CBaseEntity *pActivator )
{
// Don't fire the output if the door is already opened or locked
if ( IsOpen() || m_bIsLocked )
return;
//Play the open animation forwards
SetPlaybackRate( 1.0f );
m_bIsOpen = true;
m_bIsAnimating = true;
OnOpen();
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the door is open
//-----------------------------------------------------------------------------
bool CPropTestChamberDoor::IsOpen()
{
return ( m_bIsOpen );
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the door is closed
//-----------------------------------------------------------------------------
bool CPropTestChamberDoor::IsClosed()
{
return ( !m_bIsOpen );
}
//-----------------------------------------------------------------------------
// Purpose: "Close" the door
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::Close( CBaseEntity *pActivator )
{
// Don't fire the output if the door is already closed or locked
if ( IsClosed() || m_bIsLocked )
return;
//Play the open animation backwards
SetPlaybackRate( -1.0f );
m_bIsOpen = false;
m_bIsAnimating = true;
OnClose();
}
//-----------------------------------------------------------------------------
// Purpose: Close the door input
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::InputClose( inputdata_t &input )
{
Close( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose: Locks the door so open and close input don't have any affect until unlocked
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::InputLock( inputdata_t &input )
{
m_bIsLocked = true;
}
//-----------------------------------------------------------------------------
// Purpose: If closed, opens the door and then locks it so open and close inputs have no effect
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::InputLockOpen( inputdata_t &input )
{
Open( input.pActivator );
m_bIsLocked = true;
}
//-----------------------------------------------------------------------------
// Purpose: Unlocks the door so open and close input work again
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::InputUnlock( inputdata_t &input )
{
m_bIsLocked = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropTestChamberDoor::CreateVPhysics( void )
{
CreateBoneFollowers();
if ( m_BoneFollowerManager.GetNumBoneFollowers() )
{
if ( GetSolidFlags() & FSOLID_NOT_SOLID )
{
// Already non-solid? Must need bone followers for some other reason
// like needing to attach constraints to this object
for ( int i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
{
CBaseEntity *pFollower = m_BoneFollowerManager.GetBoneFollower(i)->hFollower;
if ( pFollower )
{
pFollower->AddSolidFlags(FSOLID_NOT_SOLID);
}
}
}
// If our collision is through bone followers, we want to be non-solid
AddSolidFlags( FSOLID_NOT_SOLID );
// add these for the client, FSOLID_NOT_SOLID should keep it out of the testCollision code
// except in the case of TraceEntity() which the client does for impact effects
AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
return true;
}
else
{
VPhysicsInitStatic();
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::CreateBoneFollowers( void )
{
// already created bone followers? Don't do so again.
if ( m_BoneFollowerManager.GetNumBoneFollowers() )
return;
KeyValues *modelKeyValues = new KeyValues("");
if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
{
// Do we have a bone follower section?
KeyValues *pkvBoneFollowers = modelKeyValues->FindKey("bone_followers");
if ( pkvBoneFollowers )
{
// Loop through the list and create the bone followers
KeyValues *pBone = pkvBoneFollowers->GetFirstSubKey();
while ( pBone )
{
// Add it to the list
const char *pBoneName = pBone->GetString();
m_BoneFollowerManager.AddBoneFollower( this, pBoneName );
pBone = pBone->GetNextKey();
}
}
modelKeyValues->deleteThis();
}
// if we got here, we don't have a bone follower section, but if we have a ragdoll
// go ahead and create default bone followers for it
if ( m_BoneFollowerManager.GetNumBoneFollowers() == 0 )
{
vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() );
if ( pCollide && pCollide->solidCount > 1 )
{
CreateBoneFollowersFromRagdoll( this, &m_BoneFollowerManager, pCollide );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CPropTestChamberDoor::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
{
if ( IsSolidFlagSet(FSOLID_NOT_SOLID) )
{
// if this entity is marked non-solid and custom test it must have bone followers
if ( IsSolidFlagSet( FSOLID_CUSTOMBOXTEST ) && IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ))
{
for ( int i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ )
{
CBaseEntity *pEntity = m_BoneFollowerManager.GetBoneFollower(i)->hFollower;
if ( pEntity && pEntity->TestCollision(ray, mask, trace) )
return true;
}
}
}
// PORTAL2: This is a change from shipped code, but should be benign
return BaseClass::TestCollision( ray, mask, trace );
}
//-----------------------------------------------------------------------------
// Purpose: Animate and catch edge cases for us stopping / starting our animation
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::AnimateThink( void )
{
// Update our animation
StudioFrameAdvance();
DispatchAnimEvents( this );
m_BoneFollowerManager.UpdateBoneFollowers( this );
if( m_bIsAnimating )
{
if ( IsSequenceFinished() )
{
if( m_bIsOpen )
{
OnFullyOpened();
}
else
{
OnFullyClosed();
}
m_bIsAnimating = false;
}
}
SetThink( &CPropTestChamberDoor::AnimateThink );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//-----------------------------------------------------------------------------
// Purpose: OnOpened output
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::OnOpen( void )
{
// Fire the OnOpen output
m_OnOpen.FireOutput( this, this );
// Play door open sound
EmitSound( "prop_portal_door.open" );
AreaPortalOpen();
}
//-----------------------------------------------------------------------------
// Purpose: OnClosed output
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::OnClose( void )
{
// Fire the OnClose output
m_OnClose.FireOutput( this, this );
// Play door close sound
EmitSound( "prop_portal_door.close" );
}
//-----------------------------------------------------------------------------
// Purpose: OnFullyOpened output
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::OnFullyOpened( void )
{
m_OnFullyOpen.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose: Close the door
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::OnFullyClosed( void )
{
m_OnFullyClosed.FireOutput( this, this );
AreaPortalClose();
}
//-----------------------------------------------------------------------------
// Purpose: Open the area portal window associated with this door
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::AreaPortalOpen( void )
{
if( m_hAreaPortalWindow)
{
float flFadeStart = TESTCHAMBER_DOOR_AREA_PORTAL_NEVER_FADE_DISTANCE;
float flFadeEnd = TESTCHAMBER_DOOR_AREA_PORTAL_NEVER_FADE_DISTANCE;
if( m_bUseAreaPortalFade )
{
flFadeStart = m_flAreaPortalFadeStartDistance;
flFadeEnd = m_flAreaPortalFadeEndDistance;
}
m_hAreaPortalWindow->m_flFadeStartDist = flFadeStart;
m_hAreaPortalWindow->m_flFadeDist = flFadeEnd;
}
}
//-----------------------------------------------------------------------------
// Purpose: Close the area portal window associated with this door
//-----------------------------------------------------------------------------
void CPropTestChamberDoor::AreaPortalClose( void )
{
if( m_hAreaPortalWindow)
{
m_hAreaPortalWindow->m_flFadeStartDist = 0;
m_hAreaPortalWindow->m_flFadeDist = 0;
}
}

View File

@@ -0,0 +1,93 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#ifndef PROP_TESTCHAMBER_DOOR_H
#define PROP_TESTCHAMBER_DOOR_H
#ifdef _WIN32
#pragma once
#endif
#include "func_areaportalwindow.h"
#include "physics_bone_follower.h"
//=============================================================================
// Non-animated linked portal door
//=============================================================================
class CPropTestChamberDoor : public CBaseAnimating
{
public:
DECLARE_CLASS( CPropTestChamberDoor, CBaseAnimating );
DECLARE_DATADESC();
CPropTestChamberDoor( void );
virtual void Precache( void );
virtual void Spawn( void );
virtual void Activate( void );
virtual bool CreateVPhysics( void );
virtual void UpdateOnRemove( void );
virtual void Open( CBaseEntity *pActivator );
virtual void Close( CBaseEntity *pActivator );
virtual bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
virtual void AnimateThink( void );
virtual bool IsOpen();
virtual bool IsClosed();
bool IsAnimating() const { return m_bIsAnimating; }
protected:
void Destroy( void );
void CreateBoneFollowers( void );
void OnFullyOpened( void );
void OnFullyClosed( void );
virtual void OnOpen( void );
virtual void OnClose( void );
void InputOpen( inputdata_t &input );
void InputClose( inputdata_t &input );
void InputLock( inputdata_t &input );
void InputLockOpen( inputdata_t &input );
void InputUnlock( inputdata_t &input );
COutputEvent m_OnOpen;
COutputEvent m_OnClose;
COutputEvent m_OnFullyOpen;
COutputEvent m_OnFullyClosed;
int m_nSequenceOpen;
int m_nSequenceOpenIdle;
int m_nSequenceClose;
int m_nSequenceCloseIdle;
bool m_bIsOpen;
bool m_bIsAnimating;
bool m_bIsLocked;
CBoneFollowerManager m_BoneFollowerManager;
//Area portal window
void AreaPortalOpen( void );
void AreaPortalClose( void );
string_t m_strAreaPortalWindowName;
CHandle<CFuncAreaPortalWindow> m_hAreaPortalWindow;
bool m_bUseAreaPortalFade;
float m_flAreaPortalFadeStartDistance;
float m_flAreaPortalFadeEndDistance;
};
#endif // PROP_TESTCHAMBER_DOOR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,219 @@
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
//
// Purpose: First-class cube entity so we can query by type and generally make inferences
// that are harder to do without an entity of that type.
//
//=====================================================================================//
#include "cbase.h"
#include "props.h"
#include "ai_utils.h"
#include "physics_saverestore.h"
#include "phys_controller.h"
#include "portal_base2d.h"
#include "datacache/imdlcache.h"
#include "func_portal_detector.h"
#include "player_pickup_paint_power_user.h"
DECLARE_AUTO_LIST( IPropWeightedCubeAutoList );
enum WeightedCubeType_e
{
CUBE_STANDARD,
CUBE_COMPANION,
CUBE_REFLECTIVE,
CUBE_SPHERE,
CUBE_ANTIQUE,
CUBE_SCHRODINGER,
};
//
// Tip controller
//
class CCubeRotationController : public CPointEntity, public IMotionEvent
{
DECLARE_CLASS( CCubeRotationController, CPointEntity );
DECLARE_DATADESC();
public:
~CCubeRotationController( void );
void Spawn( void );
void Activate( void );
void Enable( bool state = true );
void Suspend( float time );
float SuspendedTill( void );
void SetAlignmentVector( const Vector &vecAlign );
Vector GetAlignmentVector( void ) { return m_worldGoalAxis; }
void SuspendAfter( float flTime );
bool Enabled( void );
static CCubeRotationController *CreateRotationController( CBaseEntity *pOwner );
// IMotionEvent
virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
private:
bool m_bEnabled;
float m_flSuspendTime;
Vector m_worldGoalAxis;
Vector m_localTestAxis;
IPhysicsMotionController *m_pController;
float m_angularLimit;
CBaseEntity *m_pParent;
};
class CPropWeightedCube : public PlayerPickupPaintPowerUser< CPhysicsProp >, public IPropWeightedCubeAutoList
{
public:
DECLARE_CLASS( CPropWeightedCube, PlayerPickupPaintPowerUser< CPhysicsProp > );
DECLARE_SERVERCLASS();
IMPLEMENT_AUTO_LIST_GET();
CPropWeightedCube();
virtual void Spawn( void );
virtual void Activate( void );
virtual void Precache( void );
virtual int ObjectCaps( void );
virtual void OnFizzled( void )
{
// Handle the special Summer Sale achievement case
// If a cube ever fizzles on the "red racer" map, the achievement is lost
if ( V_stricmp( STRING(gpGlobals->mapname), "mp_coop_paint_red_racer" ) == 0 )
{
CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
if ( pEntity )
{
pEntity->RunScript( "CoopCubeFizzle()", "OnFizzled" );
}
}
m_OnFizzled.FireOutput( this, this );
}
virtual bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer );
virtual QAngle PreferredCarryAngles( void );
virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
virtual void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason );
virtual void UpdateOnRemove( void );
virtual void NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
virtual void StartTouch( CBaseEntity *pOther );
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void SetSkin( int skinNum );
void SetActivated( bool bActivate );
// instead of getting which model it uses, we can just ask this
WeightedCubeType_e GetCubeType( void ) { return m_nCubeType; }
#ifndef CLIENT_DLL
CPropWeightedCube* GetSchrodingerTwin( void );
#endif
void SetLaser( CBaseEntity *pLaser );
CBaseEntity* GetLaser()
{
return m_hLaser.Get();
}
bool HasLaser( void )
{
return m_hLaser.Get() != NULL;
}
virtual int UpdateTransmitState();
virtual void Paint( PaintPowerType paintType, const Vector &worldContactPt );
void UpdateSchrodingerSound( void );
void SchrodingerThink( void );
void DisabledThink( void );
bool IsMovementDisabled() const { return m_bMovementDisabled; }
bool ShouldEnterDisabledState( void );
void EnterDisabledState( void );
void ExitDisabledState( void );
void OnEnteredTractorBeam( void );
void OnExitedTractorBeam( void );
void TractorBeamThink( void );
void ExitTractorBeamThink( void );
bool WasTouchedByPlayer() { return m_bTouchedByPlayer; }
#ifndef CLIENT_DLL
#define CREATE_CUBE_AT_POSITION false
static void CreatePortalWeightedCube( WeightedCubeType_e objectType, bool bAtCursorPosition = true, const Vector &position = vec3_origin );
#endif
private:
void ConvertOldSkins( void );
void SetCubeType( void );
void SetPaintedMaterial( PaintPowerType paintType );
void SetCubeSkin( void );
void InputDissolve( inputdata_t &in );
void InputSilentDissolve( inputdata_t &in );
void InputPreDissolveJoke( inputdata_t &in );
QAngle CalculatePreferredAngles( CBasePlayer *pPlayer );
void UpdatePreferredAngles( CBasePlayer *pPlayer );
COutputEvent m_OnFizzled;
COutputEvent m_OnBluePickUp;
COutputEvent m_OnOrangePickUp;
COutputEvent m_OnPainted;
QAngle m_vecCarryAngles;
CHandle<CCubeRotationController> m_pController;
EHANDLE m_hLaser;
int m_nBouncyMaterialIndex;
void InputDisablePortalFunnel( inputdata_t &in );
void InputEnablePortalFunnel( inputdata_t &in );
void InputExitDisabledState( inputdata_t &in );
void InputSetPaint( inputdata_t &in );
void InputDisablePickup( inputdata_t &in );
void InputEnablePickup( inputdata_t &in );
DECLARE_DATADESC();
CSoundPatch *m_pSchrodingerSound;
WeightedCubeType_e m_nCubeType;
bool m_bRusted;
bool m_bActivated;
bool m_bNewSkins;
PaintPowerType m_nCurrentPaintedType;
float m_flDisabledNudgeStartTime;
bool m_bMovementDisabled;
bool m_bTouchedByPlayer;
bool m_bPickupDisabled;
#ifndef CLIENT_DLL
// Schrodinger's Balls
CHandle< CPropWeightedCube > m_hSchrodingerTwin;
static CHandle< CPropWeightedCube > m_hSchrodingerDangling;
#endif
};
bool UTIL_IsReflectiveCube( CBaseEntity *pEntity );
bool UTIL_IsWeightedCube( CBaseEntity *pEntity );
#ifndef CLIENT_DLL
bool UTIL_IsSchrodinger( CBaseEntity *pEntity );
CPropWeightedCube* UTIL_GetSchrodingerTwin( CBaseEntity *pEntity );
#endif