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.
1581 lines
42 KiB
1581 lines
42 KiB
//========= 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 "portal/weapon_physcannon.h"
|
|
#include "datacache/imdlcache.h"
|
|
#include "prop_weightedcube.h"
|
|
#include "portal_player.h"
|
|
#include "portal_player_shared.h"
|
|
#include "world.h"
|
|
#include "vcollide_parse.h"
|
|
#include "portal_gamestats.h"
|
|
#include "saverestore_utlvector.h"
|
|
#include "trigger_portal_cleanser.h"
|
|
#include "portal_mp_gamerules.h"
|
|
#include "cvisibilitymonitor.h"
|
|
|
|
ConVar reflector_cube_disabled_think_rate( "reflector_cube_disabled_think_rate", "0.1f", FCVAR_DEVELOPMENTONLY, "The rate at which the cube should think when it is disabled." );
|
|
ConVar reflector_cube_disabled_nudge_time( "reflector_cube_disabled_nudge_time", "0.5f", FCVAR_DEVELOPMENTONLY, "The amount of time the cube needs to be touched before it gets enabled again." );
|
|
ConVar reflector_cube_disabled_use_touch_check( "reflector_cube_disabled_use_touch_check", "0", FCVAR_DEVELOPMENTONLY, "Use touch checks to determine when to enable the cube." );
|
|
|
|
ConVar sv_portal2_pickup_hint_range( "sv_portal2_pickup_hint_range", "350.0", FCVAR_NONE );
|
|
|
|
// FIXME: Bring this back for DLC2
|
|
//extern ConVar sv_schrodinger_laser_world_aligned;
|
|
|
|
//Standard cube skins
|
|
enum StandardCubeSkinType_t
|
|
{
|
|
CUBE_STANDARD_CLEAN_SKIN = 0,
|
|
CUBE_STANDARD_CLEAN_ACTIVATED_SKIN = 2,
|
|
CUBE_STANDARD_RUSTED_SKIN = 3,
|
|
CUBE_STANDARD_RUSTED_ACTIVATED_SKIN = 5,
|
|
CUBE_STANDARD_BOUNCE_SKIN = 6,
|
|
CUBE_STANDARD_BOUNCE_ACTIVATED_SKIN = 10,
|
|
CUBE_STANDARD_SPEED_SKIN = 7,
|
|
CUBE_STANDARD_SPEED_ACTIVATED_SKIN = 11
|
|
};
|
|
|
|
//Companion cube skins
|
|
enum CompanionCubeSkinType_t
|
|
{
|
|
CUBE_COMPANION_CLEAN_SKIN = 1,
|
|
CUBE_COMPANION_CLEAN_ACTIVATED_SKIN = 4,
|
|
CUBE_COMPANION_BOUNCE_SKIN = 8,
|
|
CUBE_COMPANION_BOUNCE_ACTIVATED_SKIN = 8,
|
|
CUBE_COMPANION_SPEED_SKIN = 9,
|
|
CUBE_COMPANION_SPEED_ACTIVATED_SKIN = 9
|
|
};
|
|
|
|
//Reflective cubs skins
|
|
enum ReflectiveCubeSkinType_t
|
|
{
|
|
CUBE_REFLECTIVE_CLEAN_SKIN = 0,
|
|
CUBE_REFLECTIVE_RUSTED_SKIN = 1,
|
|
CUBE_REFLECTIVE_BOUNCE_SKIN = 2,
|
|
CUBE_REFLECTIVE_SPEED_SKIN = 3
|
|
};
|
|
|
|
//Sphere skins
|
|
enum WeightedSpherSkinType_t
|
|
{
|
|
CUBE_SPHERE_CLEAN_SKIN = 0,
|
|
CUBE_SPHERE_CLEAN_ACTIVATED_SKIN = 1,
|
|
CUBE_SPHERE_BOUNCE_SKIN = 2,
|
|
CUBE_SPHERE_BOUNCE_ACTIVATED_SKIN = 2,
|
|
CUBE_SPHERE_SPEED_SKIN = 3,
|
|
CUBE_SPHERE_SPEED_ACTIVATED_SKIN = 3
|
|
};
|
|
|
|
//Antique cube skins
|
|
enum AntiqueCubeSkinType_t
|
|
{
|
|
CUBE_ANTIQUE_CLEAN_SKIN = 0,
|
|
CUBE_ANTIQUE_BOUNCE_SKIN = 1,
|
|
CUBE_ANTIQUE_SPEED_SKIN = 2
|
|
};
|
|
|
|
//Schrodinger cube skins
|
|
enum SchrodingerCubeSkinType_t
|
|
{
|
|
CUBE_SCHRODINGER_CLEAN_SKIN = 4,
|
|
CUBE_SCHRODINGER_BOUNCE_SKIN = 5,
|
|
CUBE_SCHRODINGER_SPEED_SKIN = 6
|
|
};
|
|
|
|
const char SCHRODINGER_THINK_CONTEXT[] = "Schrodinger Think Context";
|
|
|
|
LINK_ENTITY_TO_CLASS( cube_rotationcontroller, CCubeRotationController );
|
|
|
|
//---------------------------------------------------------
|
|
// Save/Restore
|
|
//---------------------------------------------------------
|
|
BEGIN_DATADESC( CCubeRotationController )
|
|
|
|
DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_flSuspendTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_worldGoalAxis, FIELD_VECTOR ),
|
|
DEFINE_FIELD( m_localTestAxis, FIELD_VECTOR ),
|
|
DEFINE_PHYSPTR( m_pController ),
|
|
DEFINE_FIELD( m_angularLimit, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_pParent, FIELD_CLASSPTR ),
|
|
|
|
END_DATADESC()
|
|
|
|
IMPLEMENT_AUTO_LIST( IPropWeightedCubeAutoList );
|
|
|
|
CCubeRotationController::~CCubeRotationController()
|
|
{
|
|
if ( m_pController )
|
|
{
|
|
physenv->DestroyMotionController( m_pController );
|
|
m_pController = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CCubeRotationController::Spawn( void )
|
|
{
|
|
m_bEnabled = false;
|
|
|
|
// align the object's local Z axis
|
|
m_localTestAxis.Init( 1, 0, 0 );
|
|
|
|
// with the world's Z axis
|
|
m_worldGoalAxis.Init( 0, 0, 1 );
|
|
|
|
// recover from up to 25 degrees / sec angular velocity
|
|
m_angularLimit = 25;
|
|
m_flSuspendTime = 0;
|
|
|
|
SetMoveType( MOVETYPE_NONE );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Set the vector we'll try to match
|
|
//-----------------------------------------------------------------------------
|
|
void CCubeRotationController::SetAlignmentVector( const Vector &vecAlign )
|
|
{
|
|
m_worldGoalAxis = vecAlign;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CCubeRotationController::Activate( void )
|
|
{
|
|
BaseClass::Activate();
|
|
|
|
if ( m_pParent == NULL )
|
|
{
|
|
UTIL_Remove(this);
|
|
return;
|
|
}
|
|
|
|
IPhysicsObject *pPhys = m_pParent->VPhysicsGetObject();
|
|
|
|
if ( pPhys == NULL )
|
|
{
|
|
UTIL_Remove(this);
|
|
return;
|
|
}
|
|
|
|
//Setup the motion controller
|
|
if ( !m_pController )
|
|
{
|
|
m_pController = physenv->CreateMotionController( (IMotionEvent *)this );
|
|
m_pController->AttachObject( pPhys, true );
|
|
}
|
|
else
|
|
{
|
|
m_pController->SetEventHandler( this );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Simulation will be suspended after this amount of time
|
|
//-----------------------------------------------------------------------------
|
|
void CCubeRotationController::SuspendAfter( float flSuspendTime )
|
|
{
|
|
m_flSuspendTime = flSuspendTime;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Actual simulation for tip controller
|
|
//-----------------------------------------------------------------------------
|
|
IMotionEvent::simresult_e CCubeRotationController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
|
|
{
|
|
if ( Enabled() == false )
|
|
return SIM_NOTHING;
|
|
|
|
// Don't simulate if we're being carried by the player
|
|
// if ( m_pParent->IsBeingCarriedByPlayer() )
|
|
// return SIM_NOTHING;
|
|
|
|
float flAngularLimit = m_angularLimit;
|
|
|
|
// If we were just dropped by a friendly player, stabilise better
|
|
/*
|
|
if ( m_pParent->WasJustDroppedByPlayer() )
|
|
{
|
|
// Increase the controller strength a little
|
|
flAngularLimit += 20;
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
// If the turret has some vertical velocity, don't simulate
|
|
/*
|
|
Vector vecVelocity;
|
|
AngularImpulse angImpulse;
|
|
pObject->GetVelocity( &vecVelocity, &angImpulse );
|
|
if ( (vecVelocity.LengthSqr() > CNPC_FloorTurret::fMaxTipControllerVelocity) || (angImpulse.LengthSqr() > CNPC_FloorTurret::fMaxTipControllerAngularVelocity) )
|
|
return SIM_NOTHING;
|
|
*/
|
|
}
|
|
|
|
linear.Init();
|
|
|
|
AngularImpulse angVel;
|
|
pObject->GetVelocity( NULL, &angVel );
|
|
|
|
matrix3x4_t matrix;
|
|
// get the object's local to world transform
|
|
pObject->GetPositionMatrix( &matrix );
|
|
|
|
// Get the alignment axis in object space
|
|
Vector currentLocalTargetAxis;
|
|
VectorIRotate( m_worldGoalAxis, matrix, currentLocalTargetAxis );
|
|
|
|
float invDeltaTime = (1/deltaTime);
|
|
angular = ComputeRotSpeedToAlignAxes( m_localTestAxis, currentLocalTargetAxis, angVel, 1.0, invDeltaTime * invDeltaTime, flAngularLimit * invDeltaTime );
|
|
|
|
return SIM_LOCAL_ACCELERATION;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CCubeRotationController::Enable( bool state )
|
|
{
|
|
m_bEnabled = state;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CCubeRotationController::Enabled( void )
|
|
{
|
|
if ( m_flSuspendTime > gpGlobals->curtime )
|
|
return true;
|
|
|
|
return m_bEnabled;
|
|
}
|
|
|
|
CEG_NOINLINE CCubeRotationController * CCubeRotationController::CreateRotationController( CBaseEntity *pOwner )
|
|
{
|
|
if ( pOwner == NULL )
|
|
return NULL;
|
|
|
|
CCubeRotationController *pController = (CCubeRotationController *) Create( "cube_rotationcontroller", pOwner->GetAbsOrigin(), pOwner->GetAbsAngles() );
|
|
|
|
if ( pController != NULL )
|
|
{
|
|
pController->m_pParent = pOwner;
|
|
}
|
|
|
|
return pController;
|
|
}
|
|
|
|
CEG_PROTECT_STATIC_MEMBER_FUNCTION( CCubeRotationController_CreateRotationController, CCubeRotationController::CreateRotationController );
|
|
|
|
LINK_ENTITY_TO_CLASS( prop_weighted_cube, CPropWeightedCube );
|
|
|
|
BEGIN_DATADESC( CPropWeightedCube )
|
|
|
|
DEFINE_FIELD( m_vecCarryAngles, FIELD_VECTOR ),
|
|
DEFINE_FIELD( m_pController, FIELD_EHANDLE ),
|
|
DEFINE_FIELD( m_bMovementDisabled, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bActivated, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bTouchedByPlayer, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_nCurrentPaintedType, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_bPickupDisabled, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_SOUNDPATCH( m_pSchrodingerSound ),
|
|
|
|
DEFINE_THINKFUNC( SchrodingerThink ),
|
|
DEFINE_THINKFUNC( DisabledThink ),
|
|
DEFINE_THINKFUNC( TractorBeamThink ),
|
|
DEFINE_THINKFUNC( ExitTractorBeamThink ),
|
|
|
|
DEFINE_KEYFIELD( m_bRusted, FIELD_BOOLEAN, "SkinType" ),
|
|
DEFINE_KEYFIELD( m_nCubeType, FIELD_INTEGER, "CubeType" ),
|
|
DEFINE_KEYFIELD( m_bNewSkins, FIELD_BOOLEAN, "NewSkins" ),
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "Dissolve", InputDissolve ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "SilentDissolve", InputSilentDissolve ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "PreDissolveJoke", InputPreDissolveJoke ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "DisablePortalFunnel", InputDisablePortalFunnel ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "EnablePortalFunnel", InputEnablePortalFunnel ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "ExitDisabledState", InputExitDisabledState ),
|
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPaint", InputSetPaint ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "DisablePickup", InputDisablePickup ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "EnablePickup", InputEnablePickup ),
|
|
|
|
DEFINE_OUTPUT( m_OnFizzled, "OnFizzled" ),
|
|
DEFINE_OUTPUT( m_OnOrangePickUp, "OnOrangePickUp" ),
|
|
DEFINE_OUTPUT( m_OnBluePickUp, "OnBluePickUp" ),
|
|
DEFINE_OUTPUT( m_OnPainted, "OnPainted" ),
|
|
|
|
END_DATADESC()
|
|
|
|
//no new networked fields, just need entity specific virtual functions defined on the client
|
|
IMPLEMENT_SERVERCLASS_ST( CPropWeightedCube, DT_PropWeightedCube )
|
|
END_SEND_TABLE()
|
|
|
|
const char *CUBE_MODEL = "models/props/metal_box.mdl";
|
|
const char *CUBE_REFLECT_MODEL = "models/props/reflection_cube.mdl";
|
|
const char *CUBE_SPHERE_MODEL = "models/props_gameplay/mp_ball.mdl";
|
|
const char *CUBE_FX_FIZZLER_MODEL = "models/props/metal_box_fx_fizzler.mdl";
|
|
const char *CUBE_ANTIQUE_MODEL = "models/props_underground/underground_weighted_cube.mdl";
|
|
const char *CUBE_SCHRODINGER_MODEL = "models/props/reflection_cube.mdl";
|
|
|
|
|
|
CHandle< CPropWeightedCube > CPropWeightedCube::m_hSchrodingerDangling;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CPropWeightedCube::CPropWeightedCube()
|
|
: m_bMovementDisabled( false ),
|
|
m_bRusted( false ),
|
|
m_bActivated( false ),
|
|
m_nCubeType( CUBE_STANDARD ),
|
|
m_bTouchedByPlayer( false )
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CEG_NOINLINE void CPropWeightedCube::Spawn( void )
|
|
{
|
|
// Start out with nothing
|
|
m_vecCarryAngles.Init(0,0,0);
|
|
|
|
ConvertOldSkins();
|
|
|
|
Precache();
|
|
|
|
m_nCurrentPaintedType = NO_POWER;
|
|
|
|
m_bPickupDisabled = false;
|
|
|
|
SetCubeType();
|
|
|
|
CEG_PROTECT_VIRTUAL_FUNCTION( CPropWeightedCube_Spawn );
|
|
|
|
m_nBouncyMaterialIndex = physprops->GetSurfaceIndex( "WeightedCube_Bounce" );
|
|
SetInteraction( PROPINTER_PHYSGUN_ALLOW_OVERHEAD );
|
|
|
|
BaseClass::Spawn();
|
|
|
|
SetCollisionGroup( COLLISION_GROUP_WEIGHTED_CUBE );
|
|
|
|
if ( m_nCubeType == CUBE_SCHRODINGER )
|
|
{
|
|
SetContextThink( &CPropWeightedCube::SchrodingerThink, gpGlobals->curtime + reflector_cube_disabled_think_rate.GetFloat(), SCHRODINGER_THINK_CONTEXT );
|
|
}
|
|
|
|
#if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
|
|
g_PortalGameStats.Event_CubeSpawn();
|
|
#endif
|
|
|
|
VisibilityMonitor_AddEntity_NotVisibleThroughGlass( this, sv_portal2_pickup_hint_range.GetFloat() - 50.0f, NULL, NULL );
|
|
|
|
SetFadeDistance( -1.0f, 0.0f );
|
|
SetGlobalFadeScale( 0.0f );
|
|
}
|
|
|
|
|
|
void CPropWeightedCube::Activate( void )
|
|
{
|
|
SetPaintedMaterial( (PaintPowerType)( m_PrePaintedPower ) );
|
|
|
|
#if 0
|
|
if ( !m_pSchrodingerSound )
|
|
{
|
|
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
|
|
|
CPASAttenuationFilter filter( this );
|
|
|
|
m_pSchrodingerSound = controller.SoundCreate( filter, entindex(), "music.laser_node_02.play" );
|
|
controller.Play( m_pSchrodingerSound, 0, RandomFloat( 99, 101 ) );
|
|
}
|
|
#endif
|
|
|
|
BaseClass::Activate();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::UpdateOnRemove( void )
|
|
{
|
|
BaseClass::UpdateOnRemove();
|
|
|
|
if ( m_pController )
|
|
{
|
|
UTIL_Remove( m_pController );
|
|
}
|
|
|
|
CPropWeightedCube *pTwin = m_hSchrodingerTwin.Get();
|
|
if ( pTwin && !pTwin->IsMarkedForDeletion() )
|
|
{
|
|
CTriggerPortalCleanser::FizzleBaseAnimating( NULL, pTwin );
|
|
}
|
|
|
|
#if 0
|
|
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
|
|
|
controller.SoundDestroy( m_pSchrodingerSound );
|
|
m_pSchrodingerSound = NULL;
|
|
|
|
BaseClass::StopLoopingSounds();
|
|
#endif
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::Precache( void )
|
|
{
|
|
ConvertOldSkins();
|
|
|
|
switch ( m_nCubeType )
|
|
{
|
|
default:
|
|
case CUBE_STANDARD:
|
|
case CUBE_COMPANION:
|
|
PrecacheModel( CUBE_MODEL );
|
|
break;
|
|
|
|
case CUBE_REFLECTIVE:
|
|
PrecacheModel( CUBE_REFLECT_MODEL );
|
|
break;
|
|
|
|
case CUBE_SPHERE:
|
|
PrecacheModel( CUBE_SPHERE_MODEL );
|
|
break;
|
|
|
|
case CUBE_ANTIQUE:
|
|
PrecacheModel( CUBE_ANTIQUE_MODEL );
|
|
|
|
case CUBE_SCHRODINGER:
|
|
PrecacheModel( CUBE_SCHRODINGER_MODEL );
|
|
PrecacheScriptSound( "music.laser_node_02.play" );
|
|
PrecacheScriptSound( "prop_laser_catcher.poweron" );
|
|
PrecacheScriptSound( "prop_laser_catcher.poweroff" );
|
|
break;
|
|
}
|
|
|
|
PrecacheModel( CUBE_FX_FIZZLER_MODEL );
|
|
|
|
PrecacheScriptSound( "WeightedCube.JumpPowerActivateShort" );
|
|
PrecacheScriptSound( "WeightedCube.JumpPowerActivateLong" );
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
int CPropWeightedCube::ObjectCaps( void )
|
|
{
|
|
int flags = (BaseClass::ObjectCaps()|FCAP_IMPULSE_USE);
|
|
|
|
if ( GetPaintedPower() == BOUNCE_POWER )
|
|
{
|
|
flags |= FCAP_USE_IN_RADIUS;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
|
|
int CPropWeightedCube::UpdateTransmitState()
|
|
{
|
|
if ( HasLaser() )
|
|
{
|
|
return SetTransmitState( FL_EDICT_ALWAYS );
|
|
}
|
|
|
|
return BaseClass::UpdateTransmitState();
|
|
}
|
|
|
|
void CPropWeightedCube::ConvertOldSkins( void )
|
|
{
|
|
//HACK HACK: Make the cubes choose skins using the new method even though the maps have not been updated to use them.
|
|
if( !m_bNewSkins )
|
|
{
|
|
if( m_nSkin > 1 )
|
|
{
|
|
m_nSkin--;
|
|
}
|
|
|
|
m_nCubeType = static_cast<WeightedCubeType_e>( m_nSkin.Get() );
|
|
m_bNewSkins = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::SetCubeType( void )
|
|
{
|
|
// FIXME: Remove for DLC2
|
|
if ( m_nCubeType == CUBE_SCHRODINGER )
|
|
{
|
|
m_nCubeType = CUBE_REFLECTIVE;
|
|
}
|
|
|
|
switch( m_nCubeType )
|
|
{
|
|
//Standard cube
|
|
case CUBE_STANDARD:
|
|
case CUBE_COMPANION:
|
|
{
|
|
SetModelName( MAKE_STRING( CUBE_MODEL ) );
|
|
break;
|
|
}
|
|
|
|
//Reflective cube
|
|
case CUBE_REFLECTIVE:
|
|
{
|
|
SetModelName( MAKE_STRING( CUBE_REFLECT_MODEL ) );
|
|
m_pController = CCubeRotationController::CreateRotationController( this );
|
|
AddSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON );
|
|
break;
|
|
}
|
|
|
|
//Sphere
|
|
case CUBE_SPHERE:
|
|
{
|
|
SetModelName( MAKE_STRING( CUBE_SPHERE_MODEL ) );
|
|
break;
|
|
}
|
|
|
|
//Antique cube
|
|
case CUBE_ANTIQUE:
|
|
{
|
|
SetModelName( MAKE_STRING( CUBE_ANTIQUE_MODEL ) );
|
|
break;
|
|
}
|
|
|
|
//Schrodinger cube
|
|
case CUBE_SCHRODINGER:
|
|
{
|
|
SetModelName( MAKE_STRING( CUBE_SCHRODINGER_MODEL ) );
|
|
m_pController = CCubeRotationController::CreateRotationController( this );
|
|
AddSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON );
|
|
|
|
if ( m_hSchrodingerDangling.Get() == NULL )
|
|
{
|
|
m_hSchrodingerDangling = this;
|
|
}
|
|
else
|
|
{
|
|
m_hSchrodingerDangling->m_hSchrodingerTwin = this;
|
|
m_hSchrodingerTwin = m_hSchrodingerDangling;
|
|
m_hSchrodingerDangling = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetCubeSkin();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::SetActivated( bool bActivate )
|
|
{
|
|
m_bActivated = bActivate;
|
|
|
|
SetCubeSkin();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::SetCubeSkin( void )
|
|
{
|
|
switch( m_nCubeType )
|
|
{
|
|
//Standard cube
|
|
case CUBE_STANDARD:
|
|
{
|
|
//Rusted cubes don't show paint
|
|
if( m_bRusted )
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_STANDARD_RUSTED_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_STANDARD_RUSTED_SKIN );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch( GetPaintedPower() )
|
|
{
|
|
//Bounce painted
|
|
case BOUNCE_POWER:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
RANDOM_CEG_TEST_SECRET_PERIOD( 98, 106 );
|
|
SetSkin( CUBE_STANDARD_BOUNCE_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_STANDARD_BOUNCE_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Speed painted
|
|
case SPEED_POWER:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_STANDARD_SPEED_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_STANDARD_SPEED_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Not painted
|
|
default:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_STANDARD_CLEAN_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_STANDARD_CLEAN_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
//Companion cube
|
|
case CUBE_COMPANION:
|
|
{
|
|
switch( GetPaintedPower() )
|
|
{
|
|
//Bounce painted
|
|
case BOUNCE_POWER:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_COMPANION_BOUNCE_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_COMPANION_BOUNCE_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Speed painted
|
|
case SPEED_POWER:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_COMPANION_SPEED_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_COMPANION_SPEED_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Not painted
|
|
default:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_COMPANION_CLEAN_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_COMPANION_CLEAN_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
//Reflective cube
|
|
case CUBE_REFLECTIVE:
|
|
{
|
|
switch( GetPaintedPower() )
|
|
{
|
|
//Bounce painted
|
|
case BOUNCE_POWER:
|
|
{
|
|
if( m_bRusted )
|
|
{
|
|
// FIXME
|
|
SetSkin( CUBE_REFLECTIVE_BOUNCE_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_REFLECTIVE_BOUNCE_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Speed painted
|
|
case SPEED_POWER:
|
|
{
|
|
if( m_bRusted )
|
|
{
|
|
// FIXME
|
|
SetSkin( CUBE_REFLECTIVE_SPEED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_REFLECTIVE_SPEED_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Not painted
|
|
default:
|
|
{
|
|
if( m_bRusted )
|
|
{
|
|
SetSkin( CUBE_REFLECTIVE_RUSTED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_REFLECTIVE_CLEAN_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
//Sphere
|
|
case CUBE_SPHERE:
|
|
{
|
|
switch( GetPaintedPower() )
|
|
{
|
|
//Bounce painted
|
|
case BOUNCE_POWER:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_SPHERE_BOUNCE_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_SPHERE_BOUNCE_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Speed painted
|
|
case SPEED_POWER:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_SPHERE_SPEED_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_SPHERE_SPEED_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
//Not painted
|
|
default:
|
|
{
|
|
if( m_bActivated )
|
|
{
|
|
SetSkin( CUBE_SPHERE_CLEAN_ACTIVATED_SKIN );
|
|
}
|
|
else
|
|
{
|
|
SetSkin( CUBE_SPHERE_CLEAN_SKIN );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
//Antique cube
|
|
case CUBE_ANTIQUE:
|
|
{
|
|
switch( GetPaintedPower() )
|
|
{
|
|
//Bounce painted
|
|
case BOUNCE_POWER:
|
|
{
|
|
SetSkin( CUBE_ANTIQUE_BOUNCE_SKIN );
|
|
}
|
|
break;
|
|
//Speed painted
|
|
case SPEED_POWER:
|
|
{
|
|
SetSkin( CUBE_ANTIQUE_SPEED_SKIN );
|
|
}
|
|
break;
|
|
//Not painted
|
|
default:
|
|
{
|
|
SetSkin( CUBE_ANTIQUE_CLEAN_SKIN );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//Antique cube
|
|
case CUBE_SCHRODINGER:
|
|
{
|
|
switch( GetPaintedPower() )
|
|
{
|
|
//Bounce painted
|
|
case BOUNCE_POWER:
|
|
{
|
|
SetSkin( CUBE_SCHRODINGER_BOUNCE_SKIN );
|
|
}
|
|
break;
|
|
//Speed painted
|
|
case SPEED_POWER:
|
|
{
|
|
SetSkin( CUBE_SCHRODINGER_SPEED_SKIN );
|
|
}
|
|
break;
|
|
//Not painted
|
|
default:
|
|
{
|
|
SetSkin( CUBE_SCHRODINGER_CLEAN_SKIN );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::SetSkin( int skinNum )
|
|
{
|
|
m_nSkin = skinNum;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::InputDissolve( inputdata_t &in )
|
|
{
|
|
CTriggerPortalCleanser::FizzleBaseAnimating( NULL, this );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::InputSilentDissolve( inputdata_t &in )
|
|
{
|
|
OnFizzled();
|
|
UTIL_Remove( this );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::InputPreDissolveJoke( inputdata_t &in )
|
|
{
|
|
CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, "@glados" );
|
|
if ( pEntity )
|
|
{
|
|
pEntity->RunScript( "CoopCubeFizzle()", "PreDissolveJoke" );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::InputDisablePortalFunnel( inputdata_t &in )
|
|
{
|
|
m_bAllowPortalFunnel = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::InputEnablePortalFunnel( inputdata_t &in )
|
|
{
|
|
m_bAllowPortalFunnel = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
QAngle CPropWeightedCube::CalculatePreferredAngles( CBasePlayer *pPlayer )
|
|
{
|
|
return QAngle(0,0,0);
|
|
}
|
|
|
|
void CPropWeightedCube::UpdatePreferredAngles( CBasePlayer *pPlayer )
|
|
{
|
|
m_vecCarryAngles = CalculatePreferredAngles( pPlayer );
|
|
|
|
if( HasPreferredCarryAnglesForPlayer( pPlayer ) )
|
|
{
|
|
m_qPreferredPlayerCarryAngles = m_vecCarryAngles;
|
|
}
|
|
else
|
|
{
|
|
if( m_qPreferredPlayerCarryAngles.Get().x < FLT_MAX )
|
|
{
|
|
m_qPreferredPlayerCarryAngles.GetForModify().Init( FLT_MAX, FLT_MAX, FLT_MAX );
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
QAngle CPropWeightedCube::PreferredCarryAngles( void )
|
|
{
|
|
static QAngle s_prefAngles;
|
|
s_prefAngles = m_vecCarryAngles;
|
|
|
|
CBasePlayer *pPlayer = GetPlayerHoldingEntity( this );
|
|
if ( pPlayer )
|
|
{
|
|
Vector vecRight;
|
|
pPlayer->GetVectors( NULL, &vecRight, NULL );
|
|
|
|
Quaternion qRotation;
|
|
AxisAngleQuaternion( vecRight, pPlayer->EyeAngles().x, qRotation );
|
|
|
|
matrix3x4_t tmp;
|
|
ComputePlayerMatrix( pPlayer, tmp );
|
|
|
|
QAngle qTemp = TransformAnglesToWorldSpace( s_prefAngles, tmp );
|
|
Quaternion qExisting;
|
|
AngleQuaternion( qTemp, qExisting );
|
|
Quaternion qFinal;
|
|
QuaternionMult( qRotation, qExisting, qFinal );
|
|
|
|
QuaternionAngles( qFinal, qTemp );
|
|
s_prefAngles = TransformAnglesToLocalSpace( qTemp, tmp );
|
|
}
|
|
|
|
return s_prefAngles;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
|
|
{
|
|
BaseClass::OnPhysGunPickup( pPhysGunUser, reason );
|
|
|
|
m_bMovementDisabled = false;
|
|
m_bTouchedByPlayer = true;
|
|
|
|
// Calculate our preferred angles on the first pickup
|
|
if ( reason == PICKED_UP_BY_CANNON || reason == PICKED_UP_BY_PLAYER )
|
|
{
|
|
UpdatePreferredAngles( pPhysGunUser );
|
|
|
|
if ( m_pController )
|
|
{
|
|
m_pController->Enable( false );
|
|
}
|
|
|
|
CPortal_Player *pPlayer = ToPortalPlayer( pPhysGunUser );
|
|
if ( pPlayer )
|
|
{
|
|
// Force a cool-down on the +USE key after a successful grab
|
|
pPlayer->SetUseKeyCooldownTime( 0.5f );
|
|
}
|
|
}
|
|
|
|
if ( pPhysGunUser )
|
|
{
|
|
if ( pPhysGunUser->GetTeamNumber() == TEAM_RED )
|
|
{
|
|
m_OnOrangePickUp.FireOutput( pPhysGunUser, this );
|
|
}
|
|
else if ( pPhysGunUser->GetTeamNumber() == TEAM_BLUE )
|
|
{
|
|
m_OnBluePickUp.FireOutput( pPhysGunUser, this );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Turn on our rotation controller when we're dropped nicely
|
|
//-----------------------------------------------------------------------------
|
|
ConVar sv_box_physgundrop_angle_threshold("sv_box_physgundrop_angle_threshold", "70.f");
|
|
void CPropWeightedCube::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason )
|
|
{
|
|
BaseClass::OnPhysGunDrop( pPhysGunUser, reason );
|
|
|
|
// Only care about this if we're dropped, as opposed to launched or thrown
|
|
if ( reason != DROPPED_BY_PLAYER && reason != DROPPED_BY_CANNON )
|
|
return;
|
|
|
|
// Enable the controller for a short time
|
|
if ( m_pController )
|
|
{
|
|
m_pController->Activate();
|
|
Vector vecForward;
|
|
AngleVectors( GetAbsAngles(), &vecForward );
|
|
m_pController->SetAlignmentVector( vecForward );
|
|
m_pController->SuspendAfter( gpGlobals->curtime + 0.5f );
|
|
}
|
|
|
|
// When player drop the box and player's up is not world up, try to throw the box in the local down direction
|
|
bool bThrowBoxLocalDown = false;
|
|
CPortal_Player *pPortalPlayer = ToPortalPlayer( pPhysGunUser );
|
|
if ( pPortalPlayer && !AlmostEqual( DotProduct( pPortalPlayer->GetPortalPlayerLocalData().m_Up, Vector( 0, 0, 1 ) ), 1.f ) )
|
|
{
|
|
// check if player looks too far off the ground, then just drop the box with gravity
|
|
Vector vForward;
|
|
pPortalPlayer->GetVectors( &vForward, NULL, NULL );
|
|
float flLookAngle = RAD2DEG( acosf( DotProduct( vForward, pPortalPlayer->GetPortalPlayerLocalData().m_StickNormal ) ) );
|
|
bThrowBoxLocalDown = flLookAngle >= sv_box_physgundrop_angle_threshold.GetFloat();
|
|
|
|
if( bThrowBoxLocalDown )
|
|
{
|
|
float flDropSpeed = 400.f;
|
|
Vector vecDownVelocity = -( flDropSpeed * pPortalPlayer->GetPortalPlayerLocalData().m_Up );
|
|
IPhysicsObject *pPhysics = VPhysicsGetObject();
|
|
if ( pPhysics )
|
|
{
|
|
pPhysics->SetVelocityInstantaneous( &vecDownVelocity, NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Only bother with preferred carry angles if we're a reflective cube
|
|
//-----------------------------------------------------------------------------
|
|
bool CPropWeightedCube::HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer )
|
|
{
|
|
return ( m_nCubeType == CUBE_REFLECTIVE ) || ( /*FIXME: Bring back for DLC2 !sv_schrodinger_laser_world_aligned.GetBool() && */ m_nCubeType == CUBE_SCHRODINGER && m_hLaser.Get() );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t ¶ms )
|
|
{
|
|
// On teleport, we record a pointer to the portal we are arriving at
|
|
if ( eventType == NOTIFY_EVENT_TELEPORT )
|
|
{
|
|
CPortal_Base2D *pEnteredPortal = dynamic_cast<CPortal_Base2D*>( pNotify );
|
|
if ( pEnteredPortal && m_pController )
|
|
{
|
|
Vector vecWorldAign = pEnteredPortal->m_matrixThisToLinked.ApplyRotation( m_pController->GetAlignmentVector() );
|
|
vecWorldAign.NormalizeInPlace();
|
|
m_pController->SetAlignmentVector( vecWorldAign );
|
|
}
|
|
}
|
|
|
|
BaseClass::NotifySystemEvent( pNotify, eventType, params );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::Paint( PaintPowerType paintType, const Vector &worldContactPt )
|
|
{
|
|
BaseClass::Paint( paintType, worldContactPt );
|
|
|
|
SetPaintedMaterial( paintType );
|
|
SetCubeSkin();
|
|
|
|
CPropWeightedCube *pTwin = m_hSchrodingerTwin.Get();
|
|
if ( pTwin && pTwin->GetPaintedPower() != paintType )
|
|
{
|
|
pTwin->Paint( paintType, worldContactPt );
|
|
}
|
|
}
|
|
|
|
|
|
void CPropWeightedCube::SetPaintedMaterial( PaintPowerType paintType )
|
|
{
|
|
if ( m_nCurrentPaintedType != paintType && paintType != NO_POWER )
|
|
{
|
|
m_OnPainted.FireOutput( this, this );
|
|
}
|
|
|
|
m_nCurrentPaintedType = paintType;
|
|
|
|
switch( paintType )
|
|
{
|
|
case BOUNCE_POWER:
|
|
{
|
|
//Set the box to be bouncy
|
|
IPhysicsObject* pPhysObject = VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
pPhysObject->SetMaterialIndex( m_nBouncyMaterialIndex );
|
|
}
|
|
|
|
ExitDisabledState();
|
|
|
|
break;
|
|
|
|
}
|
|
case SPEED_POWER:
|
|
{
|
|
IPhysicsObject* pPhysObject = VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
pPhysObject->SetMaterialIndex( BaseClass::GetSpeedMaterialIndex() );
|
|
}
|
|
break;
|
|
}
|
|
case PORTAL_POWER:
|
|
case REFLECT_POWER:
|
|
case NO_POWER:
|
|
default:
|
|
{
|
|
// Store our material index
|
|
IPhysicsObject* pPhysObject = VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
pPhysObject->SetMaterialIndex( m_nOriginalMaterialIndex );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CPropWeightedCube* CPropWeightedCube::GetSchrodingerTwin( void )
|
|
{
|
|
return m_hSchrodingerTwin;
|
|
}
|
|
|
|
void CPropWeightedCube::UpdateSchrodingerSound( void )
|
|
{
|
|
if ( !m_hSchrodingerTwin.Get() )
|
|
return;
|
|
|
|
float fDist = m_hSchrodingerTwin->GetDistanceToEntity( this );
|
|
|
|
if ( m_pSchrodingerSound )
|
|
{
|
|
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
|
controller.SoundChangeVolume( m_pSchrodingerSound, RemapValClamped( fDist, 350.0f, 0.0f, 0.0f, 1.0f ), 0.1 );
|
|
}
|
|
}
|
|
|
|
void CPropWeightedCube::SetLaser( CBaseEntity *pLaser )
|
|
{
|
|
m_hLaser = pLaser;
|
|
|
|
if ( pLaser )
|
|
{
|
|
CBasePlayer *pPlayer = GetPlayerHoldingEntity( this );
|
|
if ( pPlayer )
|
|
{
|
|
UpdatePreferredAngles( pPlayer );
|
|
}
|
|
|
|
if ( GetCubeType() == CUBE_SCHRODINGER )
|
|
{
|
|
// FIXME: Need a better sound for this
|
|
//EmitSound( "prop_laser_catcher.poweron" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( GetCubeType() == CUBE_SCHRODINGER )
|
|
{
|
|
// FIXME: Need a better sound for this
|
|
//EmitSound( "prop_laser_catcher.poweroff" );
|
|
}
|
|
}
|
|
|
|
// need to update transmitstate to prevent laser going through box when box goes outside PVS
|
|
UpdateTransmitState();
|
|
}
|
|
|
|
|
|
bool CPropWeightedCube::ShouldEnterDisabledState( void )
|
|
{
|
|
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
|
|
|
if( pPhysicsObject )
|
|
{
|
|
if( !( pPhysicsObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) && pPhysicsObject->IsAsleep() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void CPropWeightedCube::EnterDisabledState( void )
|
|
{
|
|
if ( !m_bMovementDisabled )
|
|
{
|
|
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
|
|
|
if( pPhysicsObject )
|
|
{
|
|
pPhysicsObject->EnableMotion( false );
|
|
}
|
|
|
|
m_bMovementDisabled = true;
|
|
|
|
SetThink( &CPropWeightedCube::DisabledThink );
|
|
SetNextThink( gpGlobals->curtime + reflector_cube_disabled_think_rate.GetFloat() );
|
|
}
|
|
}
|
|
|
|
|
|
void CPropWeightedCube::ExitDisabledState( void )
|
|
{
|
|
if ( m_bMovementDisabled )
|
|
{
|
|
m_bMovementDisabled = false;
|
|
|
|
EnableMotion();
|
|
}
|
|
}
|
|
|
|
void CPropWeightedCube::InputExitDisabledState( inputdata_t &in )
|
|
{
|
|
ExitDisabledState();
|
|
}
|
|
|
|
void CPropWeightedCube::OnEnteredTractorBeam( void )
|
|
{
|
|
SetThink( &CPropWeightedCube::TractorBeamThink );
|
|
SetNextThink( gpGlobals->curtime );
|
|
}
|
|
|
|
void CPropWeightedCube::OnExitedTractorBeam( void )
|
|
{
|
|
SetThink( &CPropWeightedCube::ExitTractorBeamThink );
|
|
SetNextThink( gpGlobals->curtime );
|
|
}
|
|
|
|
void CPropWeightedCube::TractorBeamThink( void )
|
|
{
|
|
if ( m_bMovementDisabled )
|
|
return;
|
|
|
|
// Stop colliding with player and freeze any rotational speed
|
|
SetCollisionGroup( COLLISION_GROUP_PLAYER_HELD );
|
|
|
|
IPhysicsObject *pPhys = VPhysicsGetObject();
|
|
if ( pPhys )
|
|
{
|
|
AngularImpulse vZeroRotation( vec3_origin );
|
|
pPhys->SetVelocity( NULL, &vZeroRotation );
|
|
}
|
|
|
|
// Give players 2 seconds to get out of the way
|
|
SetThink( &CPropWeightedCube::ExitTractorBeamThink );
|
|
SetNextThink( gpGlobals->curtime + 2.0f );
|
|
}
|
|
|
|
void CPropWeightedCube::ExitTractorBeamThink( void )
|
|
{
|
|
bool bIntersectingPlayer = false;
|
|
|
|
for( int i = 1; i <= gpGlobals->maxClients; ++i )
|
|
{
|
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
if ( pPlayer )
|
|
{
|
|
if ( Intersects( pPlayer ) )
|
|
{
|
|
bIntersectingPlayer = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bIntersectingPlayer )
|
|
{
|
|
SetThink( &CPropWeightedCube::ExitTractorBeamThink );
|
|
SetNextThink( gpGlobals->curtime + 0.2f );
|
|
}
|
|
else
|
|
{
|
|
// Start colliding with player
|
|
SetCollisionGroup( COLLISION_GROUP_WEIGHTED_CUBE );
|
|
}
|
|
}
|
|
|
|
void CPropWeightedCube::InputSetPaint( inputdata_t &in )
|
|
{
|
|
Paint( static_cast< PaintPowerType >( in.value.Int() ), Vector( 0.0f, 0.0f, 0.0f ) );
|
|
}
|
|
|
|
|
|
void CPropWeightedCube::StartTouch( CBaseEntity *pOther )
|
|
{
|
|
if( m_bMovementDisabled )
|
|
{
|
|
if( pOther->IsPlayer() )
|
|
{
|
|
Vector vecPlayerForward;
|
|
AngleVectors( pOther->EyeAngles(), &vecPlayerForward );
|
|
vecPlayerForward.NormalizeInPlace();
|
|
Vector vecCubeToPlayer = (GetAbsOrigin() - pOther->EyePosition()).Normalized();
|
|
|
|
float flPlayerLookDot = DotProduct( vecCubeToPlayer, vecPlayerForward );
|
|
float flCubeDirDot = DotProduct( Forward().Normalized(), vecPlayerForward );
|
|
|
|
//DevMsg( "Dot:%f, CubeDot:%f\n", flPlayerLookDot, flCubeDirDot );
|
|
|
|
//If the cube is in front of the player
|
|
if( ( flPlayerLookDot > 0.8f && flCubeDirDot > 0.8f ) || ( flPlayerLookDot > 0.85f ) )
|
|
{
|
|
ExitDisabledState();
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pOther->IsPlayer() )
|
|
{
|
|
m_bTouchedByPlayer = true;
|
|
}
|
|
|
|
BaseClass::StartTouch( pOther );
|
|
}
|
|
|
|
void CPropWeightedCube::SchrodingerThink( void )
|
|
{
|
|
UpdateSchrodingerSound();
|
|
|
|
//Keep thinking
|
|
SetContextThink( &CPropWeightedCube::SchrodingerThink, gpGlobals->curtime + reflector_cube_disabled_think_rate.GetFloat(), SCHRODINGER_THINK_CONTEXT );
|
|
}
|
|
|
|
void CPropWeightedCube::DisabledThink( void )
|
|
{
|
|
bool hasPaintPower = false;
|
|
if( engine->HasPaintmap() )
|
|
{
|
|
if( GetPaintedPower() != NO_POWER )
|
|
{
|
|
hasPaintPower = true;
|
|
}
|
|
else
|
|
{
|
|
for( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
|
|
{
|
|
if( !IsInactivePower( GetPaintPower(i) ) )
|
|
{
|
|
hasPaintPower = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//If the cube no longer has a laser attached to it or has a paint power
|
|
if( !HasLaser() || hasPaintPower )
|
|
{
|
|
ExitDisabledState();
|
|
return;
|
|
}
|
|
|
|
//Keep thinking
|
|
SetNextThink( gpGlobals->curtime + reflector_cube_disabled_think_rate.GetFloat() );
|
|
}
|
|
|
|
|
|
void CPropWeightedCube::InputDisablePickup( inputdata_t &in )
|
|
{
|
|
m_bPickupDisabled = true;
|
|
}
|
|
|
|
void CPropWeightedCube::InputEnablePickup( inputdata_t &in )
|
|
{
|
|
m_bPickupDisabled = false;
|
|
}
|
|
|
|
void CPropWeightedCube::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
if( m_bPickupDisabled == false )
|
|
{
|
|
BaseClass::Use( pActivator, pCaller, useType, value );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool UTIL_IsWeightedCube( CBaseEntity *pEntity )
|
|
{
|
|
if ( pEntity == NULL )
|
|
return false;
|
|
|
|
return ( FClassnameIs( pEntity, "prop_weighted_cube" ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool UTIL_IsReflectiveCube( CBaseEntity *pEntity )
|
|
{
|
|
if ( UTIL_IsWeightedCube( pEntity ) == false )
|
|
return false;
|
|
|
|
CPropWeightedCube *pCube = assert_cast<CPropWeightedCube*>( pEntity );
|
|
return ( pCube && pCube->GetCubeType() == CUBE_REFLECTIVE );
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
bool UTIL_IsSchrodinger( CBaseEntity *pEntity )
|
|
{
|
|
if ( !UTIL_IsWeightedCube( pEntity ) )
|
|
return false;
|
|
|
|
CPropWeightedCube *pCube = assert_cast<CPropWeightedCube*>( pEntity );
|
|
if ( !pCube )
|
|
return false;
|
|
|
|
return pCube->GetCubeType() == CUBE_SCHRODINGER;
|
|
}
|
|
|
|
CPropWeightedCube* UTIL_GetSchrodingerTwin( CBaseEntity *pEntity )
|
|
{
|
|
if ( !UTIL_IsSchrodinger( pEntity ) )
|
|
return NULL;
|
|
|
|
CPropWeightedCube *pCube = assert_cast<CPropWeightedCube*>( pEntity );
|
|
if ( !pCube )
|
|
return NULL;
|
|
|
|
return pCube->GetSchrodingerTwin();
|
|
}
|
|
#endif
|
|
|
|
|
|
#define PORTAL_REFLECTOR_CUBE_MODEL_NAME "models/props/reflectocube.mdl"
|
|
#define PORTAL_WEIGHT_BOX_MODEL_NAME "models/props/metal_box.mdl"
|
|
|
|
#ifndef CLIENT_DLL
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates a weighted cube of a specific type
|
|
//-----------------------------------------------------------------------------
|
|
void CPropWeightedCube::CreatePortalWeightedCube( WeightedCubeType_e objectType, bool bAtCursorPosition, const Vector &position )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION();
|
|
|
|
bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
|
|
CBaseEntity::SetAllowPrecache( true );
|
|
|
|
// Try to create entity
|
|
CPropWeightedCube *entity = ( CPropWeightedCube* )CreateEntityByName("prop_weighted_cube");
|
|
if (entity)
|
|
{
|
|
//entity->PrecacheModel( PORTAL_REFLECTOR_CUBE_MODEL_NAME );
|
|
//entity->SetModel( PORTAL_REFLECTOR_CUBE_MODEL_NAME );
|
|
entity->SetName( MAKE_STRING("cube") );
|
|
entity->AddSpawnFlags( SF_PHYSPROP_ENABLE_PICKUP_OUTPUT );
|
|
entity->m_nCubeType = objectType;
|
|
entity->m_bNewSkins = true;
|
|
entity->Precache();
|
|
|
|
if ( !bAtCursorPosition )
|
|
{
|
|
entity->SetAbsOrigin( position );
|
|
}
|
|
|
|
DispatchSpawn(entity);
|
|
|
|
if ( bAtCursorPosition )
|
|
{
|
|
// Now attempt to drop into the world
|
|
CBasePlayer* pPlayer = UTIL_GetCommandClient();
|
|
trace_t tr;
|
|
Vector forward;
|
|
pPlayer->EyeVectors( &forward );
|
|
UTIL_TraceLine(pPlayer->EyePosition(),
|
|
pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID,
|
|
pPlayer, COLLISION_GROUP_WEIGHTED_CUBE, &tr );
|
|
if ( tr.fraction != 1.0 )
|
|
{
|
|
tr.endpos.z += 12;
|
|
entity->Teleport( &tr.endpos, NULL, NULL );
|
|
UTIL_DropToFloor( entity, MASK_SOLID );
|
|
}
|
|
}
|
|
|
|
// This entity should send its object caps to the client
|
|
entity->UpdateObjectCapsCache();
|
|
|
|
}
|
|
CBaseEntity::SetAllowPrecache( allowPrecache );
|
|
}
|
|
|
|
|
|
// Console command functions
|
|
void CC_Create_PortalWeightedCube()
|
|
{
|
|
CPropWeightedCube::CreatePortalWeightedCube( CUBE_STANDARD );
|
|
}
|
|
|
|
void CC_Create_PortalCompanionCube()
|
|
{
|
|
CPropWeightedCube::CreatePortalWeightedCube( CUBE_COMPANION );
|
|
}
|
|
|
|
void CC_Create_PortalReflectorCube()
|
|
{
|
|
CPropWeightedCube::CreatePortalWeightedCube( CUBE_REFLECTIVE );
|
|
}
|
|
|
|
void CC_Create_PortalWeightedSphere()
|
|
{
|
|
CPropWeightedCube::CreatePortalWeightedCube( CUBE_SPHERE );
|
|
}
|
|
|
|
void CC_Create_PortalWeightedAntique()
|
|
{
|
|
CPropWeightedCube::CreatePortalWeightedCube( CUBE_ANTIQUE );
|
|
}
|
|
|
|
void CC_Create_PortalWeightedSchrodinger()
|
|
{
|
|
CPropWeightedCube::CreatePortalWeightedCube( CUBE_SCHRODINGER );
|
|
}
|
|
|
|
// Console commands for creating cubes
|
|
static ConCommand ent_create_portal_reflector_cube("ent_create_portal_reflector_cube", CC_Create_PortalReflectorCube, "Creates a laser reflector cube cube where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
static ConCommand ent_create_portal_companion_cube("ent_create_portal_companion_cube", CC_Create_PortalCompanionCube, "Creates a companion cube where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
static ConCommand ent_create_portal_weighted_cube("ent_create_portal_weighted_cube", CC_Create_PortalWeightedCube, "Creates a standard cube where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
static ConCommand ent_create_portal_weighted_sphere("ent_create_portal_weighted_sphere", CC_Create_PortalWeightedSphere, "Creates a weighted sphere where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
static ConCommand ent_create_portal_weighted_antique("ent_create_portal_weighted_antique", CC_Create_PortalWeightedAntique, "Creates an antique cube where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
// FIXME: Bring this back for DLC2
|
|
//static ConCommand ent_create_portal_weighted_schrodinger("ent_create_portal_weighted_schrodinger", CC_Create_PortalWeightedSchrodinger, "Creates an Schrodinger cube where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT);
|
|
|
|
#endif // CLIENT_DLL
|