480 lines
14 KiB
C++
480 lines
14 KiB
C++
//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: Declares the base class for paint power users that are props.
|
|
//
|
|
//===========================================================================//
|
|
#ifndef PROP_PAINT_POWER_USER_H
|
|
#define PROP_PAINT_POWER_USER_H
|
|
|
|
#include "vphysics/friction.h"
|
|
#include "vphysics/constraints.h"
|
|
#include "player_pickup.h"
|
|
#include "paintable_entity.h"
|
|
|
|
#ifndef CLIENT_DLL
|
|
#include "portal/weapon_physcannon.h"
|
|
#endif
|
|
|
|
#include "portal_util_shared.h"
|
|
#include "portal_base2d_shared.h"
|
|
|
|
#include "paint_power_user.h"
|
|
#include "stick_partner.h"
|
|
|
|
#include "material_index_data_ops_proxy.h"
|
|
|
|
char const* const PROP_PAINT_POWER_USER_DATA_CLASS_NAME = "PropPaintPowerUser";
|
|
|
|
char const* const UPDATE_PAINT_POWER_CONTEXT = "UpdatePaintPowers";
|
|
|
|
const float PROP_PAINT_POWER_USER_PICKUP_DROP_TIME = 0.5f;
|
|
|
|
//=============================================================================
|
|
// class PropPaintPowerUser
|
|
// Purpose: Base class for props which use paint powers.
|
|
//=============================================================================
|
|
template< typename BasePropType >
|
|
class PropPaintPowerUser : public PaintPowerUser< CPaintableEntity< BasePropType > > // Derive from PaintPowerUser but add CPaintableEntity.
|
|
{
|
|
DECLARE_CLASS( PropPaintPowerUser< BasePropType >, PaintPowerUser< CPaintableEntity< BasePropType > > );
|
|
DECLARE_DATADESC();
|
|
static const datamap_t DataMapInit();
|
|
|
|
public:
|
|
//-------------------------------------------------------------------------
|
|
// Constructor/Virtual Destructor
|
|
//-------------------------------------------------------------------------
|
|
PropPaintPowerUser();
|
|
virtual ~PropPaintPowerUser();
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Prop Overrides
|
|
//-------------------------------------------------------------------------
|
|
virtual void Spawn();
|
|
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
|
|
virtual void VPhysicsUpdate( IPhysicsObject *pPhysics );
|
|
virtual void UpdatePaintPowersFromContacts();
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Paintable Entity Overrides
|
|
//-------------------------------------------------------------------------
|
|
virtual void Paint( PaintPowerType type, const Vector& worldContactPt );
|
|
|
|
protected:
|
|
int m_nOriginalMaterialIndex; // Cached physics material index (it changes)
|
|
int m_PrePaintedPower; // Power to start with on load
|
|
|
|
typedef typename BaseClass::PaintPowerInfoVector BaseClass_PaintPowerInfoVector;
|
|
|
|
virtual void ChooseActivePaintPowers( BaseClass_PaintPowerInfoVector& activePowers );
|
|
|
|
static int GetSpeedMaterialIndex();
|
|
|
|
private:
|
|
//-------------------------------------------------------------------------
|
|
// Private Data
|
|
//-------------------------------------------------------------------------
|
|
bool m_bHeldByPlayer;
|
|
float m_flPickedUpTime;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Paint Power Effects
|
|
//-------------------------------------------------------------------------
|
|
virtual PaintPowerState ActivateSpeedPower( PaintPowerInfo_t& powerInfo );
|
|
virtual PaintPowerState UseSpeedPower( PaintPowerInfo_t& powerInfo );
|
|
virtual PaintPowerState DeactivateSpeedPower( PaintPowerInfo_t& powerInfo );
|
|
|
|
virtual PaintPowerState ActivateBouncePower( PaintPowerInfo_t& powerInfo );
|
|
virtual PaintPowerState UseBouncePower( PaintPowerInfo_t& powerInfo );
|
|
virtual PaintPowerState DeactivateBouncePower( PaintPowerInfo_t& powerInfo );
|
|
};
|
|
|
|
|
|
//=============================================================================
|
|
// PropPaintPowerUser Implementation
|
|
//=============================================================================
|
|
|
|
// OMFG HACK: Define the data description table. The current macros don't work with templatized classes.
|
|
// OMFG TODO: Write a generic macro to work with templatized classes.
|
|
template< typename BasePropType >
|
|
datamap_t PropPaintPowerUser<BasePropType>::m_DataMap = PropPaintPowerUser<BasePropType>::DataMapInit();
|
|
|
|
template< typename BasePropType >
|
|
datamap_t* PropPaintPowerUser<BasePropType>::GetDataDescMap()
|
|
{
|
|
return &m_DataMap;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
datamap_t* PropPaintPowerUser<BasePropType>::GetBaseMap()
|
|
{
|
|
datamap_t *pResult;
|
|
DataMapAccess((BaseClass *)NULL, &pResult);
|
|
return pResult;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
const datamap_t PropPaintPowerUser<BasePropType>::DataMapInit()
|
|
{
|
|
typedef PropPaintPowerUser<BasePropType> classNameTypedef;
|
|
static CDatadescGeneratedNameHolder nameHolder(PROP_PAINT_POWER_USER_DATA_CLASS_NAME);
|
|
static typedescription_t dataDesc[] =
|
|
{
|
|
DEFINE_KEYFIELD( m_PrePaintedPower, FIELD_INTEGER, "PaintPower" ),
|
|
DEFINE_CUSTOM_FIELD( m_nOriginalMaterialIndex, &GetMaterialIndexDataOpsProxy() )
|
|
};
|
|
|
|
datamap_t dataMap = { dataDesc, SIZE_OF_ARRAY(dataDesc), PROP_PAINT_POWER_USER_DATA_CLASS_NAME, PropPaintPowerUser<BasePropType>::GetBaseMap() };
|
|
return dataMap;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
PropPaintPowerUser<BasePropType>::PropPaintPowerUser()
|
|
: m_PrePaintedPower(NO_POWER),
|
|
m_flPickedUpTime( 0.0f ),
|
|
m_bHeldByPlayer( false )
|
|
{
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
PropPaintPowerUser<BasePropType>::~PropPaintPowerUser()
|
|
{
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
void PropPaintPowerUser<BasePropType>::Spawn()
|
|
{
|
|
BaseClass::Spawn();
|
|
|
|
// Store our material index
|
|
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
m_nOriginalMaterialIndex = pPhysObject->GetMaterialIndex();
|
|
}
|
|
|
|
this->AddFlag( FL_AFFECTED_BY_PAINT );
|
|
if( m_PrePaintedPower != NO_POWER )
|
|
{
|
|
this->Paint( (PaintPowerType)m_PrePaintedPower, vec3_origin );
|
|
}
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
void PropPaintPowerUser<BasePropType>::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
|
|
{
|
|
if( engine->HasPaintmap() )
|
|
{
|
|
CBaseEntity* pOther = pEvent->pEntities[!index];
|
|
|
|
PaintPowerInfo_t contact;
|
|
|
|
// Get data out of the event
|
|
Vector vNormal, vPoint;
|
|
pEvent->pInternalData->GetSurfaceNormal( vNormal );
|
|
pEvent->pInternalData->GetContactPoint( vPoint );
|
|
|
|
// Fill out contact info
|
|
contact.m_SurfaceNormal = -vNormal;
|
|
contact.m_ContactPoint = vPoint;
|
|
contact.m_HandleToOther.Set( pOther );
|
|
|
|
// Add info to paint power info
|
|
this->AddSurfacePaintPowerInfo( contact, 0 );
|
|
}
|
|
|
|
BaseClass::VPhysicsCollision( index, pEvent );
|
|
}
|
|
|
|
template< typename BasePropType >
|
|
void PropPaintPowerUser<BasePropType>::VPhysicsUpdate( IPhysicsObject *pPhysics )
|
|
{
|
|
if( engine->HasPaintmap() )
|
|
{
|
|
UpdatePaintPowersFromContacts();
|
|
}
|
|
|
|
BaseClass::VPhysicsUpdate( pPhysics );
|
|
}
|
|
|
|
template< typename BasePropType >
|
|
void PropPaintPowerUser<BasePropType>::UpdatePaintPowersFromContacts()
|
|
{
|
|
//If the prop is held by a player
|
|
if( GetPlayerHoldingEntity( this ) )
|
|
{
|
|
//If the prop was not already held by a player
|
|
if( !m_bHeldByPlayer )
|
|
{
|
|
m_bHeldByPlayer = true;
|
|
|
|
//Set the timer
|
|
m_flPickedUpTime = gpGlobals->curtime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bHeldByPlayer = false;
|
|
m_flPickedUpTime = 0.0f;
|
|
}
|
|
|
|
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
IPhysicsFrictionSnapshot* pSnapShot = pPhysObject->CreateFrictionSnapshot();
|
|
while( pSnapShot->IsValid() )
|
|
{
|
|
PaintPowerInfo_t contact;
|
|
|
|
IPhysicsObject *pOther = pSnapShot->GetObject(1);
|
|
CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
|
|
Assert(pOtherEntity);
|
|
|
|
if( pOtherEntity != NULL )
|
|
{
|
|
// Get data out of the event
|
|
Vector vNormal, vPoint;
|
|
pSnapShot->GetSurfaceNormal( vNormal );
|
|
pSnapShot->GetContactPoint( vPoint );
|
|
|
|
// Fill out contact info
|
|
contact.m_SurfaceNormal = -vNormal;
|
|
contact.m_ContactPoint = vPoint;
|
|
contact.m_HandleToOther.Set( pOtherEntity );
|
|
|
|
// Add info to paint power info
|
|
this->AddSurfacePaintPowerInfo( contact, 0 );
|
|
}
|
|
|
|
pSnapShot->NextFrictionData();
|
|
}
|
|
|
|
pPhysObject->DestroyFrictionSnapshot( pSnapShot );
|
|
|
|
// Figure out paint powers
|
|
this->UpdatePaintPowers();
|
|
}
|
|
else
|
|
{
|
|
// Clear all current data
|
|
this->ClearSurfacePaintPowerInfo();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template< typename BasePropType >
|
|
void PropPaintPowerUser<BasePropType>::ChooseActivePaintPowers( BaseClass_PaintPowerInfoVector& activePowers )
|
|
{
|
|
this->MapSurfacesToPowers();
|
|
|
|
// Get the contacts
|
|
PaintPowerConstRange powerRange = this->GetSurfacePaintPowerInfo();
|
|
size_t count = powerRange.second - powerRange.first;
|
|
|
|
// Set our desired paint power to be our current painted color
|
|
PaintPowerInfo_t desiredPower;
|
|
desiredPower.m_PaintPowerType = NO_POWER;
|
|
|
|
// Get the first active power, since props can only have one at a time
|
|
const PaintPowerInfo_t* pHighestPriorityActivePower = this->FindHighestPriorityActivePaintPower();
|
|
PaintPowerInfo_t currentPower = pHighestPriorityActivePower ? *pHighestPriorityActivePower : PaintPowerInfo_t( Vector(0, 0, 1), this->GetAbsOrigin(), 0 );
|
|
|
|
PaintPowerType paintedPower = this->GetPaintedPower();
|
|
|
|
// If we're touching something
|
|
if( count != 0 )
|
|
{
|
|
this->PrioritySortSurfacePaintPowerInfo( &DescendingPaintPriorityCompare );
|
|
|
|
// Default our desired color to be our current painted color so this will default
|
|
// as our power if all else fails
|
|
if( paintedPower != NO_POWER )
|
|
{
|
|
for( PaintPowerConstIter i = powerRange.first; i != powerRange.second; ++i )
|
|
{
|
|
if( i->m_PaintPowerType != INVALID_PAINT_POWER )
|
|
{
|
|
desiredPower = *i;
|
|
desiredPower.m_PaintPowerType = paintedPower;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go through all the surfaces and try to find a power to use
|
|
for( PaintPowerConstIter i = powerRange.first; i != powerRange.second; ++i )
|
|
{
|
|
const PaintPowerInfo_t& powerInfo = *i;
|
|
|
|
if( currentPower.m_PaintPowerType == SPEED_POWER )
|
|
{
|
|
// Always take bounce when currently using speed
|
|
if( powerInfo.m_PaintPowerType == BOUNCE_POWER )
|
|
{
|
|
desiredPower = powerInfo;
|
|
}
|
|
// Take speed if others are not present
|
|
else if( desiredPower.m_PaintPowerType == NO_POWER &&
|
|
powerInfo.m_PaintPowerType == SPEED_POWER )
|
|
{
|
|
desiredPower = powerInfo;
|
|
}
|
|
}
|
|
else if( powerInfo.m_PaintPowerType != NO_POWER &&
|
|
powerInfo.m_PaintPowerType < desiredPower.m_PaintPowerType )
|
|
{ // Accept whatever it was if it's of higher priority
|
|
desiredPower = powerInfo;
|
|
}
|
|
}//for
|
|
}//if count
|
|
|
|
// Add the power to the active list
|
|
activePowers.AddToTail( desiredPower );
|
|
}
|
|
|
|
template< typename BasePropType >
|
|
PaintPowerState PropPaintPowerUser<BasePropType>::ActivateSpeedPower( PaintPowerInfo_t& powerInfo )
|
|
{
|
|
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
pPhysObject->SetMaterialIndex( ThisClass::GetSpeedMaterialIndex() );
|
|
}
|
|
|
|
return ACTIVE_PAINT_POWER;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
PaintPowerState PropPaintPowerUser<BasePropType>::UseSpeedPower( PaintPowerInfo_t& powerInfo )
|
|
{
|
|
return ACTIVE_PAINT_POWER;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
PaintPowerState PropPaintPowerUser<BasePropType>::DeactivateSpeedPower( PaintPowerInfo_t& powerInfo )
|
|
{
|
|
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
pPhysObject->SetMaterialIndex( m_nOriginalMaterialIndex );
|
|
}
|
|
|
|
return INACTIVE_PAINT_POWER;
|
|
}
|
|
|
|
|
|
extern ConVar sv_wall_bounce_trade;
|
|
extern ConVar bounce_paint_wall_jump_upward_speed;
|
|
extern ConVar bounce_paint_min_speed;
|
|
|
|
template< typename BasePropType >
|
|
PaintPowerState PropPaintPowerUser<BasePropType>::ActivateBouncePower( PaintPowerInfo_t& info )
|
|
{
|
|
IPhysicsObject* pPhysObject = this->VPhysicsGetObject();
|
|
if( pPhysObject )
|
|
{
|
|
float flTrade = sv_wall_bounce_trade.GetFloat(); // We trade some outward velocity for upward velocity
|
|
const Vector vUp = Vector(0,0,1);
|
|
Vector vBounceVel(0,0,0);
|
|
|
|
// Cancel out velocity going into the surface
|
|
Vector velocity;
|
|
AngularImpulse angularVel;
|
|
pPhysObject->GetVelocity( &velocity, &angularVel);
|
|
velocity -= info.m_SurfaceNormal * DotProduct( velocity, info.m_SurfaceNormal );
|
|
|
|
// Cancel out downward velocity (allows for going up parallel walls)
|
|
velocity -= vUp * DotProduct( velocity, vUp );
|
|
|
|
// Store this for later
|
|
Vector velNorm = velocity;
|
|
velNorm.NormalizeInPlace();
|
|
|
|
float flNormDot = DotProduct( vUp, info.m_SurfaceNormal );
|
|
|
|
float flBounceScale = 0.f;
|
|
// Add upward velocity if surface normal is facing up, relative to the player
|
|
if( flNormDot > -0.1f )
|
|
{
|
|
// Extra upward wall bounce velocity
|
|
flBounceScale = (1.f - DotProduct( vUp, info.m_SurfaceNormal ));
|
|
vBounceVel += vUp * bounce_paint_wall_jump_upward_speed.GetFloat() * flBounceScale;
|
|
}
|
|
else // Downward facing wall. Add velocity in the XY plane
|
|
{
|
|
// Vector pointing out of the surface in the XY plane
|
|
Vector vOut = ( info.m_SurfaceNormal - (vUp * DotProduct( vUp, info.m_SurfaceNormal )) ).Normalized();
|
|
|
|
// Extra lateral velocity off the wall
|
|
flBounceScale = DotProduct( vOut, info.m_SurfaceNormal );
|
|
vBounceVel += vOut * bounce_paint_wall_jump_upward_speed.GetFloat() * flBounceScale;
|
|
}
|
|
|
|
// Calculate how much bounce velocity is left to spend after the lateral
|
|
float fWallBounceScale = flTrade + ( (1.f - flBounceScale) * (1.0 - flTrade) );
|
|
// Velocity off of the surface
|
|
vBounceVel += info.m_SurfaceNormal * bounce_paint_min_speed.GetFloat() * fWallBounceScale;
|
|
|
|
// If we're going to bounce straight up, add some random XY velocity. Bouncing straight up
|
|
// doesn't look natural.
|
|
if( vBounceVel.x == 0.f && vBounceVel.y == 0.f )
|
|
{
|
|
vBounceVel += Vector( RandomFloat(-80.f, 80.f), RandomFloat(-80.f, 80.f), 0.f );
|
|
}
|
|
|
|
// Dont let our velocity fight the new bounce velocity
|
|
velocity -= vBounceVel.Normalized() * DotProduct( velocity, vBounceVel.Normalized() );
|
|
|
|
velocity += vBounceVel;
|
|
|
|
// Add velocity to physics object
|
|
pPhysObject->SetVelocity( &velocity, &angularVel );
|
|
}
|
|
|
|
return DEACTIVATING_PAINT_POWER;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
PaintPowerState PropPaintPowerUser<BasePropType>::UseBouncePower( PaintPowerInfo_t& powerInfo )
|
|
{
|
|
return DEACTIVATING_PAINT_POWER;
|
|
}
|
|
|
|
|
|
template< typename BasePropType >
|
|
PaintPowerState PropPaintPowerUser<BasePropType>::DeactivateBouncePower( PaintPowerInfo_t& powerInfo )
|
|
{
|
|
return INACTIVE_PAINT_POWER;
|
|
}
|
|
|
|
template< typename BasePropType >
|
|
int PropPaintPowerUser<BasePropType>::GetSpeedMaterialIndex()
|
|
{
|
|
static int s_SpeedMaterialIndex = physprops->GetSurfaceIndex( "ice" );
|
|
return s_SpeedMaterialIndex;
|
|
}
|
|
|
|
template< typename BasePropType >
|
|
void PropPaintPowerUser<BasePropType>::Paint( PaintPowerType type, const Vector& worldContactPt )
|
|
{
|
|
BaseClass::Paint( type, worldContactPt );
|
|
|
|
IPhysicsObject* pPhysicsObject = this->VPhysicsGetObject();
|
|
if( pPhysicsObject != NULL && pPhysicsObject->IsAsleep() )
|
|
{
|
|
pPhysicsObject->Wake();
|
|
}
|
|
}
|
|
|
|
#endif // ifndef PROP_PAINT_POWER_USER_H
|