2060 lines
59 KiB
C++
2060 lines
59 KiB
C++
#include "cbase.h"
|
|
#include "BasePropDoor.h"
|
|
#include "ai_basenpc.h"
|
|
#include "npcevent.h"
|
|
#include "engine/IEngineSound.h"
|
|
#include "asw_door.h"
|
|
#include "locksounds.h"
|
|
#include "filters.h"
|
|
#include "physics.h"
|
|
#include "vphysics_interface.h"
|
|
#include "entityoutput.h"
|
|
#include "vcollide_parse.h"
|
|
#include "studio.h"
|
|
#include "explode.h"
|
|
#include "utlrbtree.h"
|
|
#include "physics_impact_damage.h"
|
|
#include "KeyValues.h"
|
|
#include "filesystem.h"
|
|
#include "scriptevent.h"
|
|
#include "entityblocker.h"
|
|
#include "soundent.h"
|
|
#include "EntityFlame.h"
|
|
#include "game.h"
|
|
#include "physics_prop_ragdoll.h"
|
|
#include "decals.h"
|
|
#include "hierarchy.h"
|
|
#include "shareddefs.h"
|
|
#include "physobj.h"
|
|
#include "physics_npc_solver.h"
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
#include "doors.h"
|
|
#include "asw_marine.h"
|
|
#include "asw_fx_shared.h"
|
|
#include "te_effect_dispatch.h"
|
|
#include "asw_generic_emitter_entity.h"
|
|
#include "asw_door_padding.h"
|
|
#include "asw_marine_speech.h"
|
|
#include "asw_game_resource.h"
|
|
#include "asw_marine_resource.h"
|
|
#include "asw_shareddefs.h"
|
|
#include "asw_weapon_welder_shared.h"
|
|
#include "coordsize.h"
|
|
#include "asw_trace_filter_door_crush.h"
|
|
#include "asw_player.h"
|
|
#include "cvisibilitymonitor.h"
|
|
#include "particle_parse.h"
|
|
#include "asw_fail_advice.h"
|
|
#include "asw_gamerules.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
BEGIN_DATADESC(CASW_Door)
|
|
DEFINE_KEYFIELD(m_eSpawnPosition, FIELD_INTEGER, "spawnpos"),
|
|
DEFINE_KEYFIELD(m_flDistance, FIELD_FLOAT, "distance"),
|
|
DEFINE_KEYFIELD(m_angSlideAngle, FIELD_VECTOR, "slideangle"),
|
|
DEFINE_KEYFIELD(m_flTotalSealTime, FIELD_FLOAT, "totalsealtime"),
|
|
DEFINE_KEYFIELD(m_flCurrentSealTime, FIELD_FLOAT, "currentsealtime"),
|
|
DEFINE_KEYFIELD(m_iDoorType, FIELD_INTEGER, "doortype"),
|
|
DEFINE_KEYFIELD(m_DentAmount, FIELD_INTEGER, "dentamount"),
|
|
DEFINE_KEYFIELD(m_bShowsOnScanner, FIELD_BOOLEAN, "showsonscanner" ),
|
|
DEFINE_KEYFIELD(m_bAutoOpen, FIELD_BOOLEAN, "autoopen" ),
|
|
DEFINE_KEYFIELD(m_bCanCloseToWeld, FIELD_BOOLEAN, "CanCloseToWeld" ),
|
|
DEFINE_KEYFIELD(m_bDoCutShout, FIELD_BOOLEAN, "DoCutShout" ),
|
|
DEFINE_KEYFIELD(m_bDoBreachedShout, FIELD_BOOLEAN, "DoBreachedShout" ),
|
|
DEFINE_KEYFIELD(m_bDoAutoShootChatter, FIELD_BOOLEAN, "DoAutoShootChatter" ),
|
|
|
|
DEFINE_FIELD( m_bBashable, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bShootable, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_vecGoal, FIELD_VECTOR ),
|
|
DEFINE_FIELD( m_hDoorBlocker, FIELD_EHANDLE ),
|
|
DEFINE_FIELD( m_hDoorPadding, FIELD_EHANDLE ),
|
|
DEFINE_FIELD( m_fClosingToWeldTime, FIELD_FLOAT ),
|
|
DEFINE_FIELD(m_bHasBeenWelded, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bFlipped, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bSetSide, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_vecOpenPosition, FIELD_POSITION_VECTOR ),
|
|
DEFINE_FIELD( m_vecClosedPosition, FIELD_POSITION_VECTOR ),
|
|
DEFINE_FIELD( m_vecBoundsMin, FIELD_VECTOR ),
|
|
DEFINE_FIELD( m_vecBoundsMax, FIELD_VECTOR ),
|
|
|
|
DEFINE_FIELD( m_fLastMarineShootTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_fMarineShootCounter, FIELD_FLOAT ),
|
|
DEFINE_FIELD( m_bDoneDoorShout, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_fLastMomentFlipDamage, FIELD_FLOAT ),
|
|
|
|
DEFINE_FIELD( m_fSkillMarineHelping, FIELD_TIME ),
|
|
DEFINE_FIELD( m_bSkillMarineHelping, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bDoorFallen, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_FIELD( m_bRecommendedSeal, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bWasWeldedByMarine, FIELD_BOOLEAN ),
|
|
|
|
DEFINE_THINKFUNC( RunAnimation ),
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "NPCNear", InputNPCNear ),
|
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "EnableAutoOpen", InputEnableAutoOpen ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "DisableAutoOpen", InputDisableAutoOpen ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "RecommendWeld", InputRecommendWeld ),
|
|
|
|
DEFINE_OUTPUT( m_OnFullySealed, "OnFullySealed" ),
|
|
DEFINE_OUTPUT( m_OnFullyCut, "OnFullyCut" ),
|
|
DEFINE_OUTPUT( m_OnDestroyed, "OnDestroyed" ),
|
|
END_DATADESC()
|
|
|
|
IMPLEMENT_SERVERCLASS_ST(CASW_Door, DT_ASW_Door)
|
|
SendPropFloat (SENDINFO(m_flTotalSealTime)),
|
|
SendPropFloat (SENDINFO(m_flCurrentSealTime)),
|
|
SendPropInt (SENDINFO(m_iDoorStrength)),
|
|
SendPropInt (SENDINFO(m_iDoorType)),
|
|
SendPropInt (SENDINFO(m_iHealth)),
|
|
SendPropInt (SENDINFO(m_lifeState)),
|
|
SendPropBool (SENDINFO(m_bAutoOpen)),
|
|
SendPropBool (SENDINFO(m_bBashable)),
|
|
SendPropBool (SENDINFO(m_bShootable)),
|
|
SendPropBool (SENDINFO(m_bCanCloseToWeld)),
|
|
SendPropBool (SENDINFO(m_bRecommendedSeal)),
|
|
SendPropBool (SENDINFO(m_bWasWeldedByMarine)),
|
|
SendPropFloat (SENDINFO(m_fLastMomentFlipDamage)),
|
|
SendPropVector (SENDINFO(m_vecClosedPosition), -1, SPROP_COORD),
|
|
SendPropBool (SENDINFO(m_bSkillMarineHelping)),
|
|
END_SEND_TABLE()
|
|
|
|
LINK_ENTITY_TO_CLASS(asw_door, CASW_Door);
|
|
|
|
ConVar asw_door_drone_damage_scale("asw_door_drone_damage_scale", "2.0f", FCVAR_CHEAT, "Damage scale for drones hitting doors");
|
|
ConVar asw_door_seal_damage_reduction("asw_door_seal_damage_reduction", "0.6f", FCVAR_CHEAT, "Alien damage scale when door is fully sealed");
|
|
ConVar asw_door_physics("asw_door_physics", "0", FCVAR_CHEAT, "If set, doors will turn into vphysics objects upon death.");
|
|
extern ConVar asw_debug_marine_chatter;
|
|
extern ConVar asw_difficulty_alien_damage_step;
|
|
|
|
#define SINGLE_DOOR "models/swarm/doors/swarm_singledoor.mdl"
|
|
#define RIGHT_DOOR "models/props/doors/heavy_doors/doorright.mdl"
|
|
#define LEFT_DOOR "models/props/doors/heavy_doors/doorleft.mdl"
|
|
#define SINGLE_DOOR_FLIPPED "models/swarm/doors/swarm_singledoor_flipped.mdl"
|
|
|
|
static const char *s_pDoorAnimThink = "DoorAnimThink";
|
|
|
|
enum
|
|
{
|
|
AE_DOOR_FALL = 1, // The door has fallen
|
|
};
|
|
|
|
namespace
|
|
{
|
|
void UTIL_ComputeAABBForBounds( const Vector &mins1, const Vector &maxs1, const Vector &mins2, const Vector &maxs2, Vector *destMins, Vector *destMaxs )
|
|
{
|
|
// Find the minimum extents
|
|
(*destMins)[0] = MIN( mins1[0], mins2[0] );
|
|
(*destMins)[1] = MIN( mins1[1], mins2[1] );
|
|
(*destMins)[2] = MIN( mins1[2], mins2[2] );
|
|
|
|
// Find the maximum extents
|
|
(*destMaxs)[0] = MAX( maxs1[0], maxs2[0] );
|
|
(*destMaxs)[1] = MAX( maxs1[1], maxs2[1] );
|
|
(*destMaxs)[2] = MAX( maxs1[2], maxs2[2] );
|
|
}
|
|
}
|
|
|
|
|
|
CASW_Door::~CASW_Door( void )
|
|
{
|
|
// Remove our door blocker entity
|
|
if ( m_hDoorBlocker != NULL )
|
|
{
|
|
UTIL_Remove( m_hDoorBlocker );
|
|
}
|
|
if ( m_hDoorPadding != NULL )
|
|
{
|
|
UTIL_Remove( m_hDoorPadding );
|
|
}
|
|
}
|
|
|
|
void CASW_Door::Precache()
|
|
{
|
|
PrecacheScriptSound( "ASW_Door.Dented" );
|
|
PrecacheScriptSound( "ASW_Door.MeleeHit" );
|
|
PrecacheScriptSound( "ASW_Welder.WeldDeny" );
|
|
PrecacheModel(SINGLE_DOOR);
|
|
PrecacheModel(SINGLE_DOOR_FLIPPED);
|
|
PrecacheModel(RIGHT_DOOR);
|
|
PrecacheModel(LEFT_DOOR);
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
void CASW_Door::Spawn()
|
|
{
|
|
if (m_flDistance == 0)
|
|
{
|
|
m_flDistance = 90;
|
|
}
|
|
m_fLastMomentFlipDamage = 0;
|
|
m_iFallingStage = 0;
|
|
m_flDistance = fabs(m_flDistance);
|
|
|
|
const char * szModelName = STRING( GetModelName() );
|
|
m_bRotateOnFlip = !Q_stricmp( szModelName, SINGLE_DOOR );
|
|
|
|
// Calculate our closed and open positions
|
|
m_vecClosedPosition = GetAbsOrigin();
|
|
QAngle door_angle = GetLocalAngles();
|
|
door_angle += m_angSlideAngle;
|
|
Vector v(m_flDistance, 0, 0);
|
|
matrix3x4_t door_angle_matrix;
|
|
Vector offset;
|
|
AngleMatrix( door_angle, door_angle_matrix );
|
|
VectorRotate( v, door_angle_matrix, offset );
|
|
m_vecOpenPosition = GetAbsOrigin() + offset;
|
|
m_fClosingToWeldTime = 0;
|
|
m_nHardwareType = -1; // Suppress base class warnings
|
|
m_nPhysicsMaterial = physprops->GetSurfaceIndex( "metal" );
|
|
|
|
// Call this last! It relies on stuff we calculated above.
|
|
BaseClass::Spawn();
|
|
|
|
// Figure out our volumes of movement as this door opens
|
|
CalculateDoorVolume( m_vecOpenPosition, m_vecClosedPosition, &m_vecBoundsMin, &m_vecBoundsMax );
|
|
|
|
if (m_flTotalSealTime <= 0)
|
|
m_flTotalSealTime = 10.0f;
|
|
|
|
// set door strength and skin according to breakable or not:
|
|
if (m_iDoorType == 0) // normal weak door
|
|
{
|
|
m_bBashable = true;
|
|
m_bShootable = true;
|
|
m_iDoorStrength = ASW_DOOR_NORMAL_HEALTH;
|
|
m_nSkin = 0;
|
|
}
|
|
else if (m_iDoorType == 1)
|
|
{
|
|
m_bBashable = true;
|
|
m_bShootable = true;
|
|
m_iDoorStrength = ASW_DOOR_REINFORCED_HEALTH;
|
|
m_nSkin = 1;
|
|
}
|
|
else if (m_iDoorType == 2) // indestructable
|
|
{
|
|
m_bBashable = false;
|
|
m_bShootable = false;
|
|
m_iDoorStrength = 0;
|
|
m_nSkin = 2;
|
|
}
|
|
|
|
//m_iDoorStrength = 100; //temp for testing
|
|
|
|
m_bSetSide = false;
|
|
m_bFlipped = false;
|
|
m_bDoneChatter = false;
|
|
m_fChatterCounter = 0;
|
|
|
|
if ( m_DentAmount == ASWDD_PARTIAL_PREFLIP )
|
|
{
|
|
m_DentAmount = ASWDD_PARTIAL;
|
|
FlipDoor();
|
|
}
|
|
else if ( m_DentAmount == ASWDD_COMPLETE_PREFLIP )
|
|
{
|
|
m_DentAmount = ASWDD_COMPLETE;
|
|
FlipDoor();
|
|
}
|
|
|
|
if ( m_iDoorStrength > 0 )
|
|
{
|
|
m_takedamage = DAMAGE_YES;
|
|
m_iHealth = m_iDoorStrength;
|
|
|
|
if ( m_DentAmount == ASWDD_PARTIAL )
|
|
{
|
|
m_iHealth = ASW_DOOR_PARTIAL_DENT_HEALTH * m_iDoorStrength;
|
|
SetDentSequence();
|
|
}
|
|
else if ( m_DentAmount == ASWDD_COMPLETE )
|
|
{
|
|
m_iHealth = ASW_DOOR_COMPLETE_DENT_HEALTH * m_iDoorStrength;
|
|
SetDentSequence();
|
|
}
|
|
SetMaxHealth(m_iHealth);
|
|
}
|
|
else
|
|
{
|
|
m_takedamage = DAMAGE_YES; // has to receive damage events so marines can be informed they're shooting a junk item
|
|
m_iHealth = 1;
|
|
}
|
|
|
|
// if this door isn't sealed, we don't do cut shouting
|
|
if ( m_flCurrentSealTime <= 0 )
|
|
{
|
|
m_bDoCutShout = false;
|
|
}
|
|
|
|
m_fLastFullyWeldedSound = 0;
|
|
|
|
// create our padding:
|
|
m_hDoorPadding = CASW_Door_Padding::CreateDoorPadding(this);
|
|
|
|
if ( VPhysicsGetObject() )
|
|
{
|
|
VPhysicsGetObject()->SetMaterialIndex( physprops->GetSurfaceIndex("metal") );
|
|
}
|
|
|
|
SetContextThink( &CASW_Door::RunAnimation, gpGlobals->curtime + 0.1f, s_pDoorAnimThink );
|
|
|
|
if ( m_flCurrentSealTime > 0.0f )
|
|
{
|
|
VisibilityMonitor_AddEntity( this, asw_visrange_generic.GetFloat() * 0.9f, &CASW_Door::WeldedVismonCallback, NULL );
|
|
}
|
|
else
|
|
{
|
|
VisibilityMonitor_AddEntity( this, asw_visrange_generic.GetFloat() * 0.9f, &CASW_Door::DestroyVismonCallback, &CASW_Door::DestroyVismonEvaluator );
|
|
}
|
|
}
|
|
|
|
bool CASW_Door::DestroyVismonEvaluator( CBaseEntity *pVisibleEntity, CBasePlayer *pViewingPlayer )
|
|
{
|
|
CASW_Door *pDoor = static_cast< CASW_Door* >( pVisibleEntity );
|
|
|
|
if ( !pDoor->m_bShootable )
|
|
return false;
|
|
|
|
if ( pDoor->m_bDoAutoShootChatter )
|
|
return true;
|
|
|
|
if ( pDoor->m_iHealth <= 0 )
|
|
return false;
|
|
|
|
if ( static_cast<float>( pDoor->m_iHealth ) / pDoor->GetMaxHealth() > 0.4f )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CASW_Door::DestroyVismonCallback( CBaseEntity *pVisibleEntity, CBasePlayer *pViewingPlayer )
|
|
{
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "door_recommend_destroy" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "userid", pViewingPlayer->GetUserID() );
|
|
event->SetInt( "entindex", pVisibleEntity->entindex() );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CASW_Door::WeldedVismonCallback( CBaseEntity *pVisibleEntity, CBasePlayer *pViewingPlayer )
|
|
{
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "door_welded_visible" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "userid", pViewingPlayer->GetUserID() );
|
|
event->SetInt( "subject", pVisibleEntity->entindex() );
|
|
event->SetString( "entityname", STRING( pVisibleEntity->GetEntityName() ) );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CASW_Door::RunAnimation()
|
|
{
|
|
m_flPlaybackRate = 1.0f;
|
|
StudioFrameAdvance();
|
|
DispatchAnimEvents( this );
|
|
SetContextThink( &CASW_Door::RunAnimation, gpGlobals->curtime + 0.1f, s_pDoorAnimThink );
|
|
|
|
m_bSkillMarineHelping = (m_fSkillMarineHelping >= gpGlobals->curtime - 0.2f);
|
|
}
|
|
|
|
// is this door open or not?
|
|
bool CASW_Door::IsOpen( void )
|
|
{
|
|
//return ( m_vecGoal == m_vecOpenPosition );
|
|
Vector diff = GetAbsOrigin() - m_vecClosedPosition;
|
|
float dist = diff.LengthSqr();
|
|
return (dist > 2);
|
|
}
|
|
|
|
bool CASW_Door::IsMoving()
|
|
{
|
|
return GetLocalVelocity().LengthSqr() > 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::OnDoorOpened( void )
|
|
{
|
|
if ( m_hDoorBlocker != NULL )
|
|
{
|
|
// Allow passage through this blocker while open
|
|
m_hDoorBlocker->AddSolidFlags( FSOLID_NOT_SOLID );
|
|
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( GetAbsOrigin(), m_hDoorBlocker->CollisionProp()->OBBMins(), m_hDoorBlocker->CollisionProp()->OBBMaxs(), 0, 255, 0, true, 1.0f );
|
|
}
|
|
}
|
|
if ( m_hDoorPadding != NULL )
|
|
{
|
|
m_hDoorPadding->AddSolidFlags( FSOLID_NOT_SOLID );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::OnDoorClosed( void )
|
|
{
|
|
if ( m_hDoorBlocker != NULL )
|
|
{
|
|
// Destroy the blocker that was preventing NPCs from getting in our way.
|
|
UTIL_Remove( m_hDoorBlocker );
|
|
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( GetAbsOrigin(), m_hDoorBlocker->CollisionProp()->OBBMins(), m_hDoorBlocker->CollisionProp()->OBBMaxs(), 0, 255, 0, true, 1.0f );
|
|
}
|
|
}
|
|
if ( m_hDoorPadding != NULL )
|
|
{
|
|
m_hDoorPadding->RemoveSolidFlags( FSOLID_NOT_SOLID );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns whether the way is clear for the door to close.
|
|
// Input : state - Which sides to check, forward, backward, or both.
|
|
// Output : Returns true if the door can close, false if the way is blocked.
|
|
//-----------------------------------------------------------------------------
|
|
bool CASW_Door::DoorCanClose( bool bAutoClose )
|
|
{
|
|
if ( GetMaster() != NULL )
|
|
return GetMaster()->DoorCanClose( bAutoClose );
|
|
|
|
// Check all slaves
|
|
if ( HasSlaves() )
|
|
{
|
|
int numDoors = m_hDoorList.Count();
|
|
|
|
CASW_Door *pLinkedDoor = NULL;
|
|
|
|
// Check all links as well
|
|
for ( int i = 0; i < numDoors; i++ )
|
|
{
|
|
pLinkedDoor = dynamic_cast<CASW_Door *>((CBasePropDoor *)m_hDoorList[i]);
|
|
|
|
if ( pLinkedDoor != NULL )
|
|
{
|
|
if ( !pLinkedDoor->CheckDoorClear() )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// See if our path of movement is clear to allow us to shut
|
|
return CheckDoorClear();
|
|
}
|
|
|
|
void CASW_Door::CalculateDoorVolume( Vector OpenPosition, Vector ClosedPosition, Vector *destMins, Vector *destMaxs )
|
|
{
|
|
// Save our current position and move to our start position
|
|
Vector savePosition = GetAbsOrigin();
|
|
SetAbsOrigin(ClosedPosition);
|
|
|
|
// Find our AABB at the closed state
|
|
Vector closedMins, closedMaxs;
|
|
CollisionProp()->WorldSpaceAABB( &closedMins, &closedMaxs );
|
|
|
|
SetAbsOrigin(OpenPosition);
|
|
|
|
// Find our AABB at the open state
|
|
Vector openMins, openMaxs;
|
|
CollisionProp()->WorldSpaceAABB( &openMins, &openMaxs );
|
|
|
|
// Reset our position to our starting position
|
|
SetAbsOrigin(savePosition);
|
|
|
|
// Find the minimum extents
|
|
UTIL_ComputeAABBForBounds( closedMins, closedMaxs, openMins, openMaxs, destMins, destMaxs );
|
|
|
|
// Move this back into local space
|
|
*destMins -= GetAbsOrigin();
|
|
*destMaxs -= GetAbsOrigin();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::OnRestore( void )
|
|
{
|
|
BaseClass::OnRestore();
|
|
|
|
// Figure out our volumes of movement as this door opens
|
|
CalculateDoorVolume( m_vecOpenPosition, m_vecClosedPosition, &m_vecBoundsMin, &m_vecBoundsMax );
|
|
}
|
|
|
|
class CASWTraceFilterDoor : public CTraceFilterEntitiesOnly
|
|
{
|
|
public:
|
|
// It does have a base, but we'll never network anything below here..
|
|
DECLARE_CLASS_NOBASE( CASWTraceFilterDoor );
|
|
|
|
CASWTraceFilterDoor( const IHandleEntity *pDoor, const IHandleEntity *passentity, int collisionGroup )
|
|
: m_pDoor(pDoor), m_pPassEnt(passentity), m_collisionGroup(collisionGroup)
|
|
{
|
|
}
|
|
|
|
virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
|
|
{
|
|
if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
|
|
return false;
|
|
|
|
if ( !PassServerEntityFilter( pHandleEntity, m_pDoor ) )
|
|
return false;
|
|
|
|
if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
|
|
return false;
|
|
|
|
// Don't test if the game code tells us we should ignore this collision...
|
|
CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
|
|
|
|
if ( pEntity )
|
|
{
|
|
if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
|
|
return false;
|
|
|
|
if ( !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
|
|
return false;
|
|
|
|
// If objects are small enough and can move, close on them
|
|
if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
|
|
{
|
|
IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject();
|
|
Assert(pPhysics);
|
|
|
|
// Must either be squashable or very light
|
|
if ( pPhysics->IsMoveable() && pPhysics->GetMass() < 32 )
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
const IHandleEntity *m_pDoor;
|
|
const IHandleEntity *m_pPassEnt;
|
|
int m_collisionGroup;
|
|
};
|
|
|
|
inline void TraceHull_Door( const CBasePropDoor *pDoor, const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &hullMin,
|
|
const Vector &hullMax, unsigned int mask, const CBaseEntity *ignore,
|
|
int collisionGroup, trace_t *ptr )
|
|
{
|
|
Ray_t ray;
|
|
ray.Init( vecAbsStart, vecAbsEnd, hullMin, hullMax );
|
|
CASWTraceFilterDoor traceFilter( pDoor, ignore, collisionGroup );
|
|
enginetrace->TraceRay( ray, mask, &traceFilter, ptr );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : forward -
|
|
// mask -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CASW_Door::CheckDoorClear()
|
|
{
|
|
// Look for blocking entities, ignoring ourselves and the entity that opened us.
|
|
Vector vecClosed = m_vecClosedPosition;
|
|
trace_t tr;
|
|
TraceHull_Door( this, vecClosed, vecClosed, m_vecBoundsMin, m_vecBoundsMax, MASK_SOLID, GetActivator(), COLLISION_GROUP_NONE, &tr );
|
|
if ( tr.allsolid || tr.startsolid )
|
|
{
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( vecClosed, m_vecBoundsMin, m_vecBoundsMax, 255, 0, 0, true, 10.0f );
|
|
|
|
if ( tr.m_pEnt )
|
|
{
|
|
NDebugOverlay::Box( tr.m_pEnt->GetAbsOrigin(), tr.m_pEnt->CollisionProp()->OBBMins(), tr.m_pEnt->CollisionProp()->OBBMaxs(), 220, 220, 0, true, 10.0f );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( vecClosed, m_vecBoundsMin, m_vecBoundsMax, 0, 255, 0, true, 10.0f );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Puts the door in its appropriate position for spawning.
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::DoorTeleportToSpawnPosition()
|
|
{
|
|
Vector vecSpawn;
|
|
|
|
// The Start Open spawnflag trumps the choices field
|
|
if ( HasSpawnFlags( SF_DOOR_START_OPEN_OBSOLETE ) || m_eSpawnPosition == DOOR_SPAWN_OPEN )
|
|
{
|
|
vecSpawn = m_vecOpenPosition;
|
|
SetDoorState( DOOR_STATE_OPEN );
|
|
}
|
|
else if ( m_eSpawnPosition == DOOR_SPAWN_CLOSED )
|
|
{
|
|
vecSpawn = m_vecClosedPosition;
|
|
SetDoorState( DOOR_STATE_CLOSED );
|
|
}
|
|
else
|
|
{
|
|
// Bogus spawn position setting!
|
|
Assert( false );
|
|
vecSpawn = m_vecClosedPosition;
|
|
SetDoorState( DOOR_STATE_CLOSED );
|
|
}
|
|
|
|
SetAbsOrigin( vecSpawn );
|
|
|
|
// Doesn't relink; that's done in Spawn.
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: After moving, set position to the exact final position, call "move done" function.
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::MoveDone()
|
|
{
|
|
SetAbsOrigin(m_vecGoal);
|
|
SetLocalVelocity(vec3_origin);
|
|
SetMoveDoneTime(-1);
|
|
BaseClass::MoveDone();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Calculate m_vecVelocity and m_flNextThink to reach vecDest from
|
|
// GetLocalOrigin() traveling at flSpeed. Just like LinearMove, but rotational.
|
|
// Input : vecDestPosition -
|
|
// flSpeed -
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::SlideMove(const Vector &vecDestPosition, float flSpeed)
|
|
{
|
|
ASSERTSZ(flSpeed != 0, "SlideMove: no speed is defined!");
|
|
|
|
// no moving if our door is dead
|
|
//if (m_lifeState = LIFE_DEAD)
|
|
//{
|
|
//MoveDone();
|
|
//return;
|
|
//}
|
|
|
|
m_vecGoal = vecDestPosition;
|
|
|
|
// Already there?
|
|
if (vecDestPosition == GetAbsOrigin())
|
|
{
|
|
MoveDone();
|
|
return;
|
|
}
|
|
|
|
// Set destdelta to the vector needed to move.
|
|
Vector vecDestDelta = vecDestPosition - GetAbsOrigin();
|
|
|
|
// Divide by speed to get time to reach dest
|
|
float flTravelTime = vecDestDelta.Length() / flSpeed;
|
|
|
|
// Call MoveDone when destination position is reached.
|
|
SetMoveDoneTime(flTravelTime);
|
|
|
|
// Scale the destdelta vector by the time spent traveling to get velocity.
|
|
SetLocalVelocity(vecDestDelta * (1.0 / flTravelTime));
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::BeginOpening(CBaseEntity *pOwner)
|
|
{
|
|
Vector vecOpen = m_vecOpenPosition;
|
|
|
|
// turn off our padding, so it doesn't drag marines along
|
|
if ( m_hDoorPadding != NULL )
|
|
{
|
|
m_hDoorPadding->AddSolidFlags( FSOLID_NOT_SOLID );
|
|
}
|
|
|
|
// Create the door blocker
|
|
Vector mins, maxs;
|
|
mins = m_vecBoundsMin;
|
|
maxs = m_vecBoundsMax;
|
|
|
|
if ( m_hDoorBlocker != NULL )
|
|
{
|
|
UTIL_Remove( m_hDoorBlocker );
|
|
}
|
|
|
|
// Create a blocking entity to keep random entities out of our movement path
|
|
m_hDoorBlocker = CEntityBlocker::Create( GetAbsOrigin(), mins, maxs, pOwner, false );
|
|
|
|
Vector volumeCenter = ((mins+maxs) * 0.5f) + GetAbsOrigin();
|
|
|
|
// Ignoring the Z
|
|
float volumeRadius = MAX( fabs(mins.x), maxs.x );
|
|
volumeRadius = MAX( volumeRadius, MAX( fabs(mins.y), maxs.y ) );
|
|
|
|
// Debug
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Cross3D( volumeCenter, -Vector(volumeRadius,volumeRadius,volumeRadius), Vector(volumeRadius,volumeRadius,volumeRadius), 255, 0, 0, true, 1.0f );
|
|
}
|
|
|
|
// Make respectful entities move away from our path
|
|
CSoundEnt::InsertSound( SOUND_MOVE_AWAY, volumeCenter, volumeRadius, 0.5f, pOwner );
|
|
|
|
// Do final setup
|
|
if ( m_hDoorBlocker != NULL )
|
|
{
|
|
// Only block NPCs
|
|
m_hDoorBlocker->SetCollisionGroup( COLLISION_GROUP_DOOR_BLOCKER );
|
|
|
|
// If we hit something while opening, just stay unsolid until we try again
|
|
if ( CheckDoorClear() == false )
|
|
{
|
|
m_hDoorBlocker->AddSolidFlags( FSOLID_NOT_SOLID );
|
|
}
|
|
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( GetAbsOrigin(), m_hDoorBlocker->CollisionProp()->OBBMins(), m_hDoorBlocker->CollisionProp()->OBBMaxs(), 255, 0, 0, true, 1.0f );
|
|
}
|
|
}
|
|
|
|
SlideMove(vecOpen, m_flSpeed);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::BeginClosing( void )
|
|
{
|
|
if ( m_hDoorBlocker != NULL )
|
|
{
|
|
// Become solid again unless we're already being blocked
|
|
if ( CheckDoorClear() )
|
|
{
|
|
m_hDoorBlocker->RemoveSolidFlags( FSOLID_NOT_SOLID );
|
|
}
|
|
|
|
if ( g_debug_doors.GetBool() )
|
|
{
|
|
NDebugOverlay::Box( GetAbsOrigin(), m_hDoorBlocker->CollisionProp()->OBBMins(), m_hDoorBlocker->CollisionProp()->OBBMaxs(), 255, 0, 0, true, 1.0f );
|
|
}
|
|
}
|
|
|
|
SlideMove(m_vecClosedPosition, m_flSpeed);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::DoorStop( void )
|
|
{
|
|
SetLocalVelocity( vec3_origin );
|
|
SetMoveDoneTime( -1 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Restart a door moving that was temporarily paused
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::DoorResume( void )
|
|
{
|
|
// Restart our linear movement
|
|
SlideMove( m_vecGoal, m_flSpeed );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : vecMoveDir -
|
|
// opendata -
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::GetNPCOpenData(CAI_BaseNPC *pNPC, opendata_t &opendata)
|
|
{
|
|
// dvs: TODO: finalize open position, direction, activity
|
|
Vector vecForward;
|
|
Vector vecRight;
|
|
AngleVectors(GetAbsAngles(), &vecForward, &vecRight, NULL);
|
|
|
|
//
|
|
// Figure out where the NPC should stand to open this door,
|
|
// and what direction they should face.
|
|
//
|
|
opendata.vecStandPos = GetAbsOrigin() - (vecRight * 24);
|
|
opendata.vecStandPos.z -= 54;
|
|
|
|
Vector vecNPCOrigin = pNPC->GetAbsOrigin();
|
|
|
|
if (pNPC->GetAbsOrigin().Dot(vecForward) > GetAbsOrigin().Dot(vecForward))
|
|
{
|
|
// In front of the door relative to the door's forward vector.
|
|
opendata.vecStandPos += vecForward * 64;
|
|
opendata.vecFaceDir = -vecForward;
|
|
}
|
|
else
|
|
{
|
|
// Behind the door relative to the door's forward vector.
|
|
opendata.vecStandPos -= vecForward * 64;
|
|
opendata.vecFaceDir = vecForward;
|
|
}
|
|
|
|
opendata.eActivity = ACT_OPEN_DOOR;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns how long it will take this door to open.
|
|
//-----------------------------------------------------------------------------
|
|
float CASW_Door::GetOpenInterval()
|
|
{
|
|
// set destdelta to the vector needed to move
|
|
Vector vecDestDelta = m_vecOpenPosition - GetAbsOrigin();
|
|
|
|
// divide by speed to get time to reach dest
|
|
return vecDestDelta.Length() / m_flSpeed;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Draw any debug text overlays
|
|
// Output : Current text offset from the top
|
|
//-----------------------------------------------------------------------------
|
|
int CASW_Door::DrawDebugTextOverlays(void)
|
|
{
|
|
int text_offset = BaseClass::DrawDebugTextOverlays();
|
|
|
|
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
|
{
|
|
char tempstr[512];
|
|
Q_snprintf(tempstr, sizeof(tempstr),"Avelocity: %.2f %.2f %.2f", GetLocalVelocity().x, GetLocalVelocity().y, GetLocalVelocity().z);
|
|
NDebugOverlay::EntityText(entindex(), text_offset, tempstr, 0);
|
|
text_offset++;
|
|
|
|
if ( IsDoorOpen() )
|
|
{
|
|
Q_strncpy(tempstr, "DOOR STATE: OPEN", sizeof(tempstr));
|
|
}
|
|
else if ( IsDoorClosed() )
|
|
{
|
|
Q_strncpy(tempstr, "DOOR STATE: CLOSED", sizeof(tempstr));
|
|
}
|
|
else if ( IsDoorOpening() )
|
|
{
|
|
Q_strncpy(tempstr, "DOOR STATE: OPENING", sizeof(tempstr));
|
|
}
|
|
else if ( IsDoorClosing() )
|
|
{
|
|
Q_strncpy(tempstr, "DOOR STATE: CLOSING", sizeof(tempstr));
|
|
}
|
|
else if ( IsDoorAjar() )
|
|
{
|
|
Q_strncpy(tempstr, "DOOR STATE: AJAR", sizeof(tempstr));
|
|
}
|
|
NDebugOverlay::EntityText(entindex(), text_offset, tempstr, 0);
|
|
text_offset++;
|
|
}
|
|
|
|
return text_offset;
|
|
}
|
|
|
|
|
|
void CASW_Door::InputNPCNear( inputdata_t &inputdata )
|
|
{
|
|
// a marine or an alien has come near the door
|
|
Msg("[S] NPC near door\n");
|
|
}
|
|
|
|
void CASW_Door::ActivateUseIcon( CASW_Marine* pMarine, int nHoldType )
|
|
{
|
|
if ( nHoldType == ASW_USE_HOLD_START )
|
|
return;
|
|
|
|
// player has used this item
|
|
}
|
|
|
|
// returns how sealed this door is, from 0 to 1.0
|
|
float CASW_Door::GetSealAmount()
|
|
{
|
|
if ( m_flTotalSealTime <= 0 )
|
|
return 0;
|
|
|
|
return ( m_flCurrentSealTime / m_flTotalSealTime );
|
|
}
|
|
|
|
void CASW_Door::SetCurrentSealTime( float fTime )
|
|
{
|
|
m_flCurrentSealTime = fTime;
|
|
}
|
|
|
|
void CASW_Door::SetTotalSealTime(float fTime)
|
|
{
|
|
m_flTotalSealTime = fTime;
|
|
}
|
|
|
|
bool CASW_Door::IsDoorLocked()
|
|
{
|
|
bool bDented = ( m_DentAmount > ASWDD_PARTIAL );
|
|
if ( !bDented && HasSlaves() )
|
|
{
|
|
int numDoors = m_hDoorList.Count();
|
|
for ( int i = 0; i < numDoors; i++ )
|
|
{
|
|
CASW_Door *pSlave = dynamic_cast<CASW_Door*>( m_hDoorList[ i ].Get() );
|
|
if ( pSlave && pSlave->m_DentAmount > ASWDD_PARTIAL )
|
|
{
|
|
bDented = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return BaseClass::IsDoorLocked() || bDented || ( GetCurrentSealTime() > 0 );
|
|
}
|
|
|
|
void CASW_Door::WeldDoor( bool bSeal, float fAmount, CASW_Marine *pMarine )
|
|
{
|
|
if ( !pMarine )
|
|
return;
|
|
|
|
float fCurrentSealTime = GetCurrentSealTime();
|
|
|
|
if ( !bSeal )
|
|
{
|
|
fAmount = -fAmount;
|
|
}
|
|
else
|
|
{
|
|
m_bWasWeldedByMarine = true;
|
|
}
|
|
|
|
// once a door has been welded a bit, don't have anyone complain about it being sealed
|
|
m_bDoCutShout = false;
|
|
|
|
if ( m_fChatterCounter * fAmount < 0 ) // reset the chatter counter if we switch weld direction
|
|
{
|
|
m_fChatterCounter = 0;
|
|
m_bDoneChatter = false;
|
|
}
|
|
if ( !m_bDoneChatter )
|
|
{
|
|
m_fChatterCounter += fAmount;
|
|
if ( m_fChatterCounter > 1.0f )
|
|
{
|
|
pMarine->GetMarineSpeech()->Chatter( CHATTER_SEALING_DOOR );
|
|
m_bDoneChatter = true;
|
|
}
|
|
else if ( m_fChatterCounter < -1.0f )
|
|
{
|
|
pMarine->GetMarineSpeech()->Chatter( CHATTER_CUTTING_DOOR );
|
|
m_bDoneChatter = true;
|
|
}
|
|
}
|
|
|
|
float fNewTime = fCurrentSealTime + fAmount;
|
|
if ( fNewTime <= 0 )
|
|
{
|
|
if ( fCurrentSealTime > 0)
|
|
{
|
|
m_OnFullyCut.FireOutput(pMarine, this);
|
|
// door completely opened
|
|
if (IsAutoOpen())
|
|
{
|
|
variant_t emptyVariant;
|
|
AcceptInput("Open", pMarine, this, emptyVariant, 0);
|
|
}
|
|
}
|
|
fNewTime = 0;
|
|
}
|
|
|
|
if ( fNewTime >= GetTotalSealTime() )
|
|
{
|
|
if ( fCurrentSealTime < fNewTime )
|
|
{
|
|
// door is completely sealed
|
|
m_OnFullySealed.FireOutput(pMarine, this);
|
|
}
|
|
//Msg(" Fully sealed, cur=%f last=%f inh=%d commander=%d\n", gpGlobals->curtime, m_fLastFullyWeldedSound,
|
|
//pMarine->IsInhabited(), pMarine->GetCommander() != NULL );
|
|
if ( gpGlobals->curtime > ( m_fLastFullyWeldedSound + 1.0f ) && pMarine->IsInhabited() && pMarine->GetCommander() )
|
|
{
|
|
m_fLastFullyWeldedSound = gpGlobals->curtime;
|
|
//Msg("Playing deny sound time=%f!\n", m_fLastFullyWeldedSound);
|
|
// play a deny sound to the marine's owner
|
|
pMarine->GetCommander()->EmitPrivateSound( "ASW_Welder.WeldDeny", true );
|
|
}
|
|
fNewTime = GetTotalSealTime();
|
|
}
|
|
|
|
if ( fCurrentSealTime != fNewTime )
|
|
{
|
|
// sparks now spawn from the welder weapon
|
|
|
|
if ( fCurrentSealTime > 0.0f && fNewTime <= 0.0f )
|
|
{
|
|
// Door is now unsealed!
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "door_unwelded" );
|
|
if ( event )
|
|
{
|
|
CASW_Player *pPlayer = NULL;
|
|
pPlayer = pMarine->GetCommander();
|
|
|
|
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
|
|
event->SetInt( "entindex", entindex() );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
else if ( fCurrentSealTime < 1.0f && fNewTime >= 1.0f )
|
|
{
|
|
// Door is now sealed!
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "door_welded" );
|
|
if ( event )
|
|
{
|
|
CASW_Player *pPlayer = NULL;
|
|
pPlayer = pMarine->GetCommander();
|
|
|
|
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
|
|
event->SetInt( "entindex", entindex() );
|
|
event->SetInt( "inhabited", pMarine->IsInhabited() );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
|
|
SetCurrentSealTime( fNewTime );
|
|
|
|
if ( !m_bHasBeenWelded && bSeal && m_flCurrentSealTime > 0.0f )
|
|
{
|
|
m_bHasBeenWelded = true;
|
|
ASWFailAdvice()->OnMarineWeldedDoor();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CASW_Door::CloseForWeld(CASW_Marine* pMarine)
|
|
{
|
|
if (m_bCanCloseToWeld)
|
|
{
|
|
variant_t emptyVariant;
|
|
AcceptInput("Close", pMarine, this, emptyVariant, 0);
|
|
m_fClosingToWeldTime = gpGlobals->curtime + 1.0f;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CASW_Door::AutoOpen(CBaseEntity* pOther)
|
|
{
|
|
if (gpGlobals->curtime > m_fClosingToWeldTime)
|
|
{
|
|
variant_t emptyVariant;
|
|
AcceptInput("Open", pOther, this, emptyVariant, 0);
|
|
}
|
|
}
|
|
|
|
// the point a marine should look to weld this door
|
|
// sparks come from here also
|
|
|
|
#define DOOR_CORNER_DISTANCE 62.0f
|
|
#define DOOR_HEIGHT 135.0f
|
|
Vector CASW_Door::GetWeldFacingPoint(CBaseEntity* pOther)
|
|
{
|
|
// work out which side of the door the marine is on
|
|
Vector diff = pOther->GetAbsOrigin() - GetAbsOrigin();
|
|
VectorNormalize(diff);
|
|
QAngle angDoorFacing = GetAbsAngles();
|
|
Vector vecDoorFacing = vec3_origin;
|
|
AngleVectors(angDoorFacing, &vecDoorFacing);
|
|
bool bFrontSide = (DotProduct(vecDoorFacing, diff) > 0);
|
|
|
|
// depending on the side, get one of the corners
|
|
angDoorFacing.y -= bFrontSide ? 81 : 102;
|
|
AngleVectors(angDoorFacing, &vecDoorFacing);
|
|
Vector result = GetAbsOrigin() + vecDoorFacing * DOOR_CORNER_DISTANCE;
|
|
|
|
// correct by height
|
|
result.z += DOOR_HEIGHT * (1.0f - GetSealAmount());
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CASW_Door::KeyValue( const char *szKeyName, const char *szValue )
|
|
{
|
|
if ( FStrEq(szKeyName, "health") )
|
|
{
|
|
// skip over the CBaseProp KeyValue which discards health setting
|
|
return CBaseAnimating::KeyValue( szKeyName, szValue );
|
|
}
|
|
|
|
return BaseClass::KeyValue( szKeyName, szValue );
|
|
}
|
|
|
|
int CASW_Door::OnTakeDamage( const CTakeDamageInfo &info )
|
|
{
|
|
Vector vecTemp;
|
|
|
|
if ( !edict() || !m_takedamage )
|
|
return 0;
|
|
|
|
if ( (m_takedamage != DAMAGE_EVENTS_ONLY) && info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_MARINE)
|
|
{
|
|
CASW_Marine* pMarine = dynamic_cast<CASW_Marine*>(info.GetAttacker());
|
|
if (pMarine)
|
|
pMarine->HurtJunkItem(this, info);
|
|
}
|
|
|
|
if (info.GetDamageType() == DMG_SLASH || info.GetDamageType() == DMG_CLUB) // if an alien claw attack, then check if it's bashable
|
|
{
|
|
EmitSound("ASW_Door.MeleeHit");
|
|
|
|
if (!m_bBashable)
|
|
return 0;
|
|
}
|
|
else if (!m_bShootable) // otherwise, check if it's shootable
|
|
return 0;
|
|
|
|
// door doesn't take damage when it's open
|
|
// todo: take damage, but not below a dent boundary?
|
|
//if (IsDoorOpen() || IsDoorOpening())
|
|
if (!IsDoorClosed())
|
|
return 0;
|
|
|
|
// stop buzzers from knocking down the door in 1 swoop (their physics??)
|
|
if (info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_BUZZER)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( info.GetInflictor() )
|
|
{
|
|
vecTemp = info.GetInflictor()->WorldSpaceCenter() - ( WorldSpaceCenter() );
|
|
}
|
|
else
|
|
{
|
|
vecTemp.Init( 1, 0, 0 );
|
|
}
|
|
|
|
// this global is still used for glass and other non-NPC killables, along with decals.
|
|
g_vecAttackDir = vecTemp;
|
|
VectorNormalize(g_vecAttackDir);
|
|
|
|
m_vLastDamageDir = g_vecAttackDir;
|
|
|
|
if ( m_takedamage != DAMAGE_EVENTS_ONLY )
|
|
{
|
|
CTakeDamageInfo newInfo(info);
|
|
|
|
float damage = info.GetDamage();
|
|
// reduce damage from shotguns and mining laser
|
|
if (info.GetDamageType() & DMG_ENERGYBEAM)
|
|
{
|
|
damage *= 0.4f; // still makes the mining laser the best weapon for taking down doors
|
|
}
|
|
if (info.GetDamageType() & DMG_BUCKSHOT)
|
|
{
|
|
damage *= 0.42f; // makes level 3 skill vindicator take around 16 seconds, normal shotgun around 26 (worse than rifle :/)
|
|
}
|
|
|
|
if ( info.GetInflictor() && info.GetInflictor()->Classify() == CLASS_ASW_T75 )
|
|
{
|
|
damage *= 7.0f; // take extra damage from T75
|
|
}
|
|
|
|
if (info.GetAttacker())
|
|
{
|
|
if (info.GetAttacker()->Classify() == CLASS_ASW_DRONE) // scale alien damage
|
|
{
|
|
// Undo difficulty damage adjust
|
|
float fDiff = ASWGameRules()->GetMissionDifficulty() - 5;
|
|
float f = 1.0 + fDiff * asw_difficulty_alien_damage_step.GetFloat();
|
|
damage /= f;
|
|
|
|
damage *= asw_door_drone_damage_scale.GetFloat();
|
|
}
|
|
else if (info.GetAttacker()->Classify() == CLASS_ASW_MARINE
|
|
&& info.GetDamageType() & DMG_CLUB) // make doors immune to kick damage
|
|
{
|
|
damage *= 0;
|
|
}
|
|
|
|
// reduce damage from welding
|
|
if (GetTotalSealTime() > 0)
|
|
{
|
|
float fSeal = GetCurrentSealTime() / GetTotalSealTime();
|
|
if (fSeal > 0 && fSeal <= 1.0f)
|
|
{
|
|
// up to X% damage reduction
|
|
fSeal *= asw_door_seal_damage_reduction.GetFloat();
|
|
damage *= (1.0f - fSeal);
|
|
}
|
|
}
|
|
}
|
|
|
|
newInfo.SetDamage(damage);
|
|
|
|
CheckForDoorShootChatter(newInfo);
|
|
|
|
// do the damage
|
|
m_iHealth -= damage;
|
|
|
|
//Msg("Door health now %d (%d) seq %d frame %f DoorOpen=%d DoorOpening=%d DoorClosed=%d DoorClosing=%d\n",
|
|
//m_iHealth, (int) m_DentAmount, GetSequence(), GetCycle(),
|
|
//IsDoorOpen(), IsDoorOpening(), IsDoorClosed(), IsDoorClosing() );
|
|
|
|
if (m_iHealth <= 0)
|
|
{
|
|
const int needed_flip_damage = 50;
|
|
// can't kill the door if it's not facing the right way
|
|
|
|
//change this to check if the door is denting the right way
|
|
//Msg( "Door dead. needs flip = %d flipped = %d\n", DoorNeedsFlip(), m_bFlipped );
|
|
|
|
if ( DoorNeedsFlip() )
|
|
{
|
|
//Msg( " keeping door alive\n" );
|
|
m_iHealth = 1;
|
|
m_fLastMomentFlipDamage += newInfo.GetDamage();
|
|
if (m_fLastMomentFlipDamage >= needed_flip_damage)
|
|
{
|
|
m_fLastMomentFlipDamage = 0;
|
|
// put it in the middle and the run setdentsequence to actually flip it
|
|
m_DentAmount = ASWDD_NONE;
|
|
SetDentSequence();
|
|
|
|
// now make sure it's completely dented in this new direction (looks weird if the door straightens itself out)
|
|
m_DentAmount = ASWDD_COMPLETE;
|
|
SetDentSequence();
|
|
m_fLastMomentFlipDamage = -needed_flip_damage;
|
|
}
|
|
}
|
|
/*
|
|
else if (m_DentAmount != ASWDD_COMPLETE) // not fully dented yet, so can't fall over, but it's facing the right way at least
|
|
{
|
|
m_iHealth = 1;
|
|
m_fLastMomentFlipDamage += newInfo.GetDamage();
|
|
if (m_fLastMomentFlipDamage >= needed_flip_damage)
|
|
{
|
|
m_fLastMomentFlipDamage = 0;
|
|
if (m_DentAmount == ASWDD_NONE)// move it 1 step towards fully dented
|
|
m_DentAmount = ASWDD_PARTIAL;
|
|
else if (m_DentAmount == ASWDD_PARTIAL)
|
|
m_DentAmount = ASWDD_COMPLETE;
|
|
SetDentSequence();
|
|
}
|
|
}
|
|
*/
|
|
else // door is facing the right way and is fully dented, let's make it fall over
|
|
{
|
|
if (m_fLastMomentFlipDamage < 0) // count up a small amount of damage so it doesn't fall immediately after changing facing
|
|
{
|
|
m_fLastMomentFlipDamage += newInfo.GetDamage();
|
|
}
|
|
else
|
|
{
|
|
Event_Killed( newInfo );
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
else // door is still alive
|
|
{
|
|
// figure out how dented the door is
|
|
if (m_iHealth > ASW_DOOR_PARTIAL_DENT_HEALTH * m_iDoorStrength)
|
|
{
|
|
m_DentAmount = ASWDD_NONE;
|
|
}
|
|
else if (m_iHealth > ASW_DOOR_COMPLETE_DENT_HEALTH * m_iDoorStrength)
|
|
{
|
|
m_DentAmount = ASWDD_PARTIAL;
|
|
}
|
|
else
|
|
{
|
|
m_DentAmount = ASWDD_COMPLETE;
|
|
}
|
|
|
|
SetDentSequence();
|
|
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool CASW_Door::DoorNeedsFlip( void )
|
|
{
|
|
// check if we need to rotate the prop to make the right side anim
|
|
Vector vecFacing;
|
|
AngleVectors(GetAbsAngles(), &vecFacing);
|
|
float dot = DotProduct(vecFacing, m_vLastDamageDir );
|
|
bool bNeedFlip = (dot > 0);
|
|
if (bNeedFlip != m_bFlipped)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CASW_Door::SetDentSequence()
|
|
{
|
|
|
|
if (m_lifeState == LIFE_DEAD)
|
|
return;
|
|
|
|
if (m_DentAmount == ASWDD_NONE) // always check for flipping if the door is in the 'middle' position
|
|
{
|
|
if (DoorNeedsFlip())
|
|
FlipDoor();
|
|
|
|
m_bSetSide = true;
|
|
}
|
|
|
|
// if we are a new door use body groups
|
|
if (!stricmp(STRING(GetModelName()), RIGHT_DOOR) || !stricmp(STRING(GetModelName()), LEFT_DOOR) )
|
|
{
|
|
SetDoorDamage();
|
|
return;
|
|
}
|
|
|
|
int iSeq;
|
|
|
|
switch(m_DentAmount)
|
|
{
|
|
case ASWDD_COMPLETE:
|
|
iSeq = LookupSequence("dent2");
|
|
break;
|
|
case ASWDD_PARTIAL:
|
|
iSeq = LookupSequence("dent1");
|
|
break;
|
|
default:
|
|
iSeq = LookupSequence("still");
|
|
break;
|
|
}
|
|
|
|
// only do denting when the door is shut
|
|
if (iSeq != GetSequence() && !IsDoorOpen() && !IsDoorOpening())
|
|
{
|
|
//Msg("Setting door sequence to %d and cycle to 0\n", iSeq);
|
|
ResetSequence(iSeq);
|
|
ResetClientsideFrame();
|
|
//SetCycle(0); // should make this kinda stuff clientside?
|
|
// spawn some smoke and stuff, since the door just got dented a bit
|
|
DoorSmoke();
|
|
}
|
|
}
|
|
|
|
void CASW_Door::SetDoorDamage()
|
|
{
|
|
int iDamageGroup;
|
|
switch(m_DentAmount)
|
|
{
|
|
case ASWDD_COMPLETE:
|
|
iDamageGroup = 4;
|
|
break;
|
|
case ASWDD_PARTIAL:
|
|
iDamageGroup = 3;
|
|
break;
|
|
default:
|
|
iDamageGroup = 0;
|
|
break;
|
|
}
|
|
|
|
// apply flip
|
|
if ( m_bFlipped )
|
|
{
|
|
if ( iDamageGroup == 3 )
|
|
iDamageGroup = 1;
|
|
else if ( iDamageGroup == 4 )
|
|
iDamageGroup = 2;
|
|
}
|
|
|
|
// only do denting when the door is shut
|
|
if (iDamageGroup != GetSequence() && !IsDoorOpen() && !IsDoorOpening())
|
|
{
|
|
SetBodygroup( 0 , iDamageGroup );
|
|
// spawn some smoke and stuff, since the door just got dented a bit
|
|
DoorSmoke();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void CASW_Door::DoorSmoke()
|
|
{
|
|
CASW_Emitter* pEmitter = (CASW_Emitter*) CreateEntityByName("asw_emitter");
|
|
if (pEmitter)
|
|
{
|
|
pEmitter->UseTemplate("doorsmoke1");
|
|
UTIL_SetOrigin( pEmitter, GetAbsOrigin() );
|
|
pEmitter->SetAbsAngles(GetAbsAngles());
|
|
pEmitter->Spawn();
|
|
//pEmitter->SetParent( this );
|
|
}
|
|
EmitSound("ASW_Door.Dented");
|
|
}
|
|
|
|
void CASW_Door::Event_Killed( const CTakeDamageInfo &info )
|
|
{
|
|
m_OnDestroyed.FireOutput(info.GetInflictor(), this);
|
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "door_destroyed" );
|
|
if ( event )
|
|
{
|
|
CBasePlayer *pPlayer = NULL;
|
|
CASW_Marine *pMarine = dynamic_cast< CASW_Marine* >( info.GetAttacker() );
|
|
if ( pMarine )
|
|
{
|
|
pPlayer = pMarine->GetCommander();
|
|
}
|
|
|
|
event->SetInt( "userid", ( pPlayer ? pPlayer->GetUserID() : 0 ) );
|
|
event->SetInt( "entindex", entindex() );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
|
|
if( info.GetAttacker() )
|
|
{
|
|
info.GetAttacker()->Event_KilledOther(this, info);
|
|
}
|
|
|
|
// check if marines should shout about this door being bashed down
|
|
// check there's another marine nearby
|
|
if (m_bDoBreachedShout && info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_DRONE
|
|
&& ASWGameResource())
|
|
{
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
// count how many marines are nearby
|
|
int iNearby = 0;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i);
|
|
CASW_Marine *pMarine = pMR ? pMR->GetMarineEntity() : NULL;
|
|
if (pMarine && (GetAbsOrigin().DistTo(pMarine->GetAbsOrigin()) < 800)
|
|
&& pMarine->GetHealth() > 0)
|
|
iNearby++;
|
|
}
|
|
|
|
if (iNearby >= 0)
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Doing door breached chatter\n");
|
|
int iChatter = random->RandomInt(0, iNearby-1);
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i);
|
|
CASW_Marine *pMarine = pMR ? pMR->GetMarineEntity() : NULL;
|
|
if (pMarine && (GetAbsOrigin().DistTo(pMarine->GetAbsOrigin()) < 600)
|
|
&& pMarine->GetHealth() > 0)
|
|
{
|
|
if (iChatter <= 0)
|
|
{
|
|
pMarine->GetMarineSpeech()->QueueChatter(CHATTER_BREACHED_DOOR, gpGlobals->curtime + 0.5f, gpGlobals->curtime + 1.5f);
|
|
break;
|
|
}
|
|
iChatter--;
|
|
}
|
|
}
|
|
}
|
|
else // only one marine nearby, pretend we did the shout so we don't waste time checking again
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Skipping door breached chatter, no marines nearby\n");
|
|
}
|
|
}
|
|
|
|
m_bDoorFallen = true;
|
|
m_takedamage = DAMAGE_NO;
|
|
//Msg("Door broken!\n");
|
|
m_lifeState = LIFE_DEAD;
|
|
//ResetSequence(LookupSequence("fall"));
|
|
|
|
// remove any padding childs
|
|
CBaseEntity *child;
|
|
// iterate through all children
|
|
for ( child = FirstMoveChild(); child != NULL; child = child->NextMovePeer() )
|
|
{
|
|
if( FClassnameIs( child, "asw_door_padding" ) )
|
|
{
|
|
child->SetParent( NULL );
|
|
child->SetSolid( SOLID_NONE );
|
|
UTIL_Remove(child);
|
|
}
|
|
}
|
|
m_hDoorPadding = NULL;
|
|
|
|
DoorSmoke();
|
|
// turn into a physics prop
|
|
if (asw_door_physics.GetBool())
|
|
{
|
|
VPhysicsDestroyObject();
|
|
ASWCreateVPhysics();
|
|
if (VPhysicsGetObject())
|
|
{
|
|
Vector vecFacing;
|
|
QAngle angFacing = GetAbsAngles();
|
|
if ( m_bFlipped && m_bRotateOnFlip )
|
|
angFacing[YAW] += 180;
|
|
AngleVectors(angFacing, &vecFacing);
|
|
VPhysicsGetObject()->ApplyForceOffset( vecFacing * 50000, WorldSpaceCenter() );
|
|
}
|
|
else
|
|
{
|
|
DevMsg("Error, failed to create door physics object\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int iSeq;
|
|
if (!stricmp(STRING(GetModelName()), RIGHT_DOOR) || !stricmp(STRING(GetModelName()), LEFT_DOOR))
|
|
{
|
|
if ( m_bFlipped )
|
|
{
|
|
iSeq = LookupSequence("door_rear_fall");
|
|
}
|
|
else
|
|
{
|
|
iSeq = LookupSequence("door_front_fall");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iSeq = LookupSequence("fall");
|
|
}
|
|
|
|
//VPhysicsDestroyObject();
|
|
//m_bUseHitboxesForRenderBox = true;
|
|
CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
|
|
if (iSeq != GetSequence())
|
|
{
|
|
ResetSequence(iSeq);
|
|
ResetClientsideFrame();
|
|
DoorSmoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CASW_Door::ASWCreateVPhysics()
|
|
{
|
|
|
|
// Create the object in the physics system
|
|
bool asleep = HasSpawnFlags( SF_PHYSPROP_START_ASLEEP ) ? true : false;
|
|
|
|
solid_t tmpSolid;
|
|
PhysModelParseSolid( tmpSolid, this, GetModelIndex() );
|
|
|
|
//if ( m_massScale > 0 )
|
|
//{
|
|
//tmpSolid.params.mass *= m_massScale;
|
|
//}
|
|
|
|
//if ( m_inertiaScale > 0 )
|
|
//{
|
|
//tmpSolid.params.inertia *= m_inertiaScale;
|
|
//if ( tmpSolid.params.inertia < 0.5 )
|
|
//tmpSolid.params.inertia = 0.5;
|
|
//}
|
|
|
|
PhysGetMassCenterOverride( this, modelinfo->GetVCollide( GetModelIndex() ), tmpSolid );
|
|
//PhysSolidOverride( tmpSolid, m_iszOverrideScript );
|
|
|
|
IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_VPHYSICS, 0, asleep, &tmpSolid );
|
|
|
|
if ( !pPhysicsObject )
|
|
{
|
|
SetSolid( SOLID_NONE );
|
|
SetMoveType( MOVETYPE_NONE );
|
|
Warning("ERROR!: Can't create physics object for %s\n", STRING( GetModelName() ) );
|
|
}
|
|
else
|
|
{
|
|
//if ( m_damageType == 1 )
|
|
//{
|
|
//PhysSetGameFlags( pPhysicsObject, FVPHYSICS_DMG_SLICE );
|
|
//}
|
|
if ( HasSpawnFlags( SF_PHYSPROP_MOTIONDISABLED ) ) //|| m_damageToEnableMotion > 0 || m_flForceToEnableMotion > 0 )
|
|
{
|
|
pPhysicsObject->EnableMotion( false );
|
|
}
|
|
}
|
|
|
|
// fix up any noncompliant blades.
|
|
if( HasInteraction( PROPINTER_PHYSGUN_LAUNCH_SPIN_Z ) )
|
|
{
|
|
if( !(VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_DMG_SLICE) )
|
|
{
|
|
PhysSetGameFlags( pPhysicsObject, FVPHYSICS_DMG_SLICE );
|
|
|
|
#if 0
|
|
if( g_pDeveloper->GetInt() )
|
|
{
|
|
// Highlight them in developer mode.
|
|
m_debugOverlays |= (OVERLAY_TEXT_BIT|OVERLAY_BBOX_BIT);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if( HasInteraction( PROPINTER_PHYSGUN_DAMAGE_NONE ) )
|
|
{
|
|
PhysSetGameFlags( pPhysicsObject, FVPHYSICS_NO_IMPACT_DMG );
|
|
}
|
|
|
|
if ( HasSpawnFlags(SF_PHYSPROP_PREVENT_PICKUP) )
|
|
{
|
|
PhysSetGameFlags(pPhysicsObject, FVPHYSICS_NO_PLAYER_PICKUP);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CASW_Door::VPhysicsUpdate( IPhysicsObject *pPhysics )
|
|
{
|
|
BaseClass::VPhysicsUpdate( pPhysics );
|
|
//m_bAwake = !pPhysics->IsAsleep();
|
|
NetworkStateChanged();
|
|
if ( HasSpawnFlags( SF_PHYSPROP_START_ASLEEP ) )
|
|
{
|
|
//if ( m_bAwake )
|
|
//{
|
|
//m_OnAwakened.FireOutput(this, this);
|
|
//RemoveSpawnFlags( SF_PHYSPROP_START_ASLEEP );
|
|
//}
|
|
}
|
|
|
|
// If we're asleep, clear the player thrown flag
|
|
//if ( m_bThrownByPlayer && !m_bAwake )
|
|
//{
|
|
//m_bThrownByPlayer = false;
|
|
//}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Mass / mass center
|
|
//-----------------------------------------------------------------------------
|
|
void CASW_Door::GetMassCenter( Vector *pMassCenter )
|
|
{
|
|
if ( !VPhysicsGetObject() )
|
|
{
|
|
pMassCenter->Init();
|
|
return;
|
|
}
|
|
|
|
Vector vecLocal = VPhysicsGetObject()->GetMassCenterLocalSpace();
|
|
VectorTransform( vecLocal, EntityToWorldTransform(), *pMassCenter );
|
|
}
|
|
|
|
float CASW_Door::GetMass() const
|
|
{
|
|
return VPhysicsGetObject() ? VPhysicsGetObject()->GetMass() : 1.0f;
|
|
}
|
|
|
|
void CASW_Door::FlipDoor()
|
|
{
|
|
if (!Q_stricmp(STRING(GetModelName()), RIGHT_DOOR))
|
|
{
|
|
//Msg( "flipping right door\n" );
|
|
m_bFlipped = !m_bFlipped;
|
|
SetModel(RIGHT_DOOR);
|
|
}
|
|
else if (!Q_stricmp(STRING(GetModelName()), LEFT_DOOR))
|
|
{
|
|
//Msg( "flipping left door\n" );
|
|
m_bFlipped = !m_bFlipped;
|
|
SetModel(LEFT_DOOR);
|
|
}
|
|
else if (!Q_stricmp(STRING(GetModelName()), SINGLE_DOOR))
|
|
{
|
|
m_bFlipped = true;
|
|
SetModel(SINGLE_DOOR_FLIPPED);
|
|
}
|
|
else if (!Q_stricmp(STRING(GetModelName()), SINGLE_DOOR_FLIPPED))
|
|
{
|
|
m_bFlipped = false;
|
|
SetModel(SINGLE_DOOR);
|
|
}
|
|
else
|
|
{
|
|
m_bFlipped = !m_bFlipped;
|
|
}
|
|
}
|
|
|
|
// always send this entity to players (until we have a radius based network cull thing...)
|
|
int CASW_Door::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
|
{
|
|
return FL_EDICT_ALWAYS;
|
|
}
|
|
|
|
void CASW_Door::InputEnableAutoOpen( inputdata_t &inputdata )
|
|
{
|
|
m_bAutoOpen = true;
|
|
}
|
|
|
|
void CASW_Door::InputDisableAutoOpen( inputdata_t &inputdata )
|
|
{
|
|
m_bAutoOpen = false;
|
|
}
|
|
|
|
void CASW_Door::InputRecommendWeld( inputdata_t &inputdata )
|
|
{
|
|
m_bRecommendedSeal = true;
|
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "door_recommend_weld" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "entindex", entindex() );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
|
|
// called by door areas that want automatic door shoot or weld chatter
|
|
void CASW_Door::DoAutoDoorShootChatter(CASW_Marine *pMarine)
|
|
{
|
|
// check there's another marine nearby
|
|
if (!m_bDoneDoorShout && ASWGameResource() && pMarine && pMarine->GetHealth() > 0)
|
|
{
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
// count how many are nearby and see if any have a welder
|
|
int iNearby = 0;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i);
|
|
CASW_Marine *pOtherMarine = pMR ? pMR->GetMarineEntity() : NULL;
|
|
if (pOtherMarine && (GetAbsOrigin().DistTo(pOtherMarine->GetAbsOrigin()) < 600)
|
|
&& pOtherMarine->GetHealth() > 0)
|
|
{
|
|
iNearby++;
|
|
}
|
|
}
|
|
|
|
if (iNearby >= 2) // changed to only need the 1 marine...
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Doing door shout hint\n");
|
|
if (pMarine->GetMarineSpeech()->Chatter(CHATTER_REQUEST_SHOOT_DOOR))
|
|
{
|
|
m_bDoneDoorShout = true;
|
|
}
|
|
}
|
|
else // no other marines nearby, do a shoot shout or a cut shown depending on if we have a welder
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Skipping door shout, only 1 marine nearby\n");
|
|
|
|
CASW_Weapon_Welder *pWelder = dynamic_cast<CASW_Weapon_Welder*>(pMarine->GetASWWeapon(2));
|
|
if (pWelder && pMarine->GetMarineSpeech()->Chatter(CHATTER_CUTTING_DOOR))
|
|
{
|
|
m_bDoCutShout = false;
|
|
m_bDoneDoorShout = true;
|
|
}
|
|
else if (pMarine->GetMarineSpeech()->Chatter(CHATTER_REQUEST_SHOOT_DOOR))
|
|
{
|
|
m_bDoneDoorShout = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CASW_Door::CheckForDoorShootChatter( const CTakeDamageInfo &info )
|
|
{
|
|
if (m_bDoneDoorShout)
|
|
return;
|
|
|
|
if (m_takedamage != DAMAGE_YES || GetHealth() <= 0 || !m_bShootable)
|
|
return;
|
|
|
|
if (info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_MARINE)
|
|
{
|
|
// reduce our block counter by the number of seconds that have passed since a marine last shot us in the front
|
|
if (m_fLastMarineShootTime != 0 && m_fMarineShootCounter > 0)
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Reducing shoot counter by %f\n", (gpGlobals->curtime - m_fLastMarineShootTime) * 20.0f);
|
|
m_fMarineShootCounter -= (gpGlobals->curtime - m_fLastMarineShootTime) * 20.0f; // counter ticks completely down after 30 seconds
|
|
if (m_fMarineShootCounter < 0)
|
|
m_fMarineShootCounter = 0;
|
|
}
|
|
m_fMarineShootCounter += info.GetDamage();
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("m_fMarineShootCounter = %f\n", m_fMarineShootCounter);
|
|
if (m_fMarineShootCounter > 100) // roughly 1 second of rifle firing on normal
|
|
{
|
|
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(info.GetAttacker());
|
|
if (!pMarine)
|
|
return;
|
|
|
|
// check there's another marine nearby
|
|
if ( ASWGameResource() )
|
|
{
|
|
CASW_Game_Resource *pGameResource = ASWGameResource();
|
|
// count how many are nearby
|
|
int iNearby = 0;
|
|
for (int i=0;i<pGameResource->GetMaxMarineResources();i++)
|
|
{
|
|
CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i);
|
|
CASW_Marine *pOtherMarine = pMR ? pMR->GetMarineEntity() : NULL;
|
|
if (pOtherMarine && (GetAbsOrigin().DistTo(pOtherMarine->GetAbsOrigin()) < 600)
|
|
&& pOtherMarine->GetHealth() > 0)
|
|
iNearby++;
|
|
}
|
|
|
|
if (iNearby >= 2)
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Doing door shout hint\n");
|
|
if (pMarine->GetMarineSpeech()->Chatter(CHATTER_REQUEST_SHOOT_DOOR))
|
|
m_bDoneDoorShout = true;
|
|
}
|
|
else // only one marine nearby, pretend we did the shout so we don't waste time checking again
|
|
{
|
|
if (asw_debug_marine_chatter.GetBool())
|
|
Msg("Skipping door shout, only 1 marine nearby\n");
|
|
m_bDoneDoorShout = true;
|
|
}
|
|
}
|
|
m_fMarineShootCounter = 0;
|
|
}
|
|
m_fLastMarineShootTime = gpGlobals->curtime;
|
|
}
|
|
}
|
|
|
|
void CASW_Door::HandleAnimEvent(animevent_t *pEvent)
|
|
{
|
|
int nEvent = pEvent->Event();
|
|
|
|
if ( nEvent == AE_START_SCRIPTED_EFFECT)
|
|
{
|
|
m_iFallingStage++;
|
|
//Msg("Door fallen!\n");
|
|
VPhysicsDestroyObject();
|
|
|
|
// crush anyone in the way for this stage
|
|
FallCrush();
|
|
|
|
// spawn a blocker to cover the door's fallen position
|
|
if (m_iFallingStage == 2)
|
|
CASW_Fallen_Door_Padding::CreateFallenDoorPadding(this);
|
|
|
|
|
|
//SetMoveType( MOVETYPE_NONE );
|
|
|
|
// adjust our collision bounds
|
|
//Vector vecMaxs = Vector(60,185,21);
|
|
//Vector vecMins = Vector(-60,42,0);
|
|
//SetSolid( SOLID_BBOX );
|
|
//SetCollisionGroup( COLLISION_GROUP_NONE );
|
|
//SetCollisionBounds( vecMins, vecMaxs );
|
|
//VPhysicsInitShadow(false, false);
|
|
//RemoveSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
|
|
//VPhysicsInitFallenShadow(false, false);
|
|
//CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
|
|
return;
|
|
}
|
|
|
|
BaseClass::HandleAnimEvent( pEvent );
|
|
}
|
|
|
|
// attempt to create a vphysics object on the floor in the place the door fall animation goes - doesn't work though
|
|
IPhysicsObject *CASW_Door::VPhysicsInitFallenShadow( bool allowPhysicsMovement, bool allowPhysicsRotation, solid_t *pSolid )
|
|
{
|
|
if ( !edict() || IsMarkedForDeletion() )
|
|
return NULL;
|
|
|
|
// No physics
|
|
if ( GetSolid() == SOLID_NONE )
|
|
return NULL;
|
|
|
|
const Vector &origin = GetAbsOrigin();
|
|
QAngle angles = GetAbsAngles();
|
|
//angles[PITCH] += 90;
|
|
Vector vecMaxs = Vector(60,185,21);
|
|
Vector vecMins = Vector(-60,42,0);
|
|
Vector absMin = Vector(-300, -300, -300);
|
|
Vector absMax = Vector(300, 300, 300);
|
|
CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &absMin, &absMax );
|
|
//pPhysicsObject = PhysModelCreateOBB( this, vecMins, vecMaxs, origin, angles, false );
|
|
|
|
|
|
IPhysicsObject *pPhysicsObject = PhysModelCreate( this, GetModelIndex(), origin, angles, pSolid );
|
|
/*
|
|
if ( GetSolid() == SOLID_BBOX )
|
|
{
|
|
// adjust these so the game tracing epsilons match the physics minimum separation distance
|
|
// this will shrink the vphysics version of the model by the difference in epsilons
|
|
float radius = 0.25f - DIST_EPSILON;
|
|
Vector mins = WorldAlignMins() + Vector(radius, radius, radius);
|
|
Vector maxs = WorldAlignMaxs() - Vector(radius, radius, radius);
|
|
pPhysicsObject = PhysModelCreateBox( this, mins, maxs, origin, false );
|
|
angles = vec3_angle;
|
|
}
|
|
else if ( GetSolid() == SOLID_OBB )
|
|
{
|
|
pPhysicsObject = PhysModelCreateOBB( this, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), origin, angles, false );
|
|
}
|
|
else
|
|
{
|
|
pPhysicsObject = PhysModelCreate( this, GetModelIndex(), origin, angles, pSolid );
|
|
}
|
|
*/
|
|
if ( !pPhysicsObject )
|
|
return NULL;
|
|
|
|
VPhysicsSetObject( pPhysicsObject );
|
|
pPhysicsObject->UpdateShadow( origin, angles, false, 0 );
|
|
pPhysicsObject->SetShadow( 1e4, 1e4, allowPhysicsMovement, allowPhysicsRotation );
|
|
pPhysicsObject->UpdateShadow( origin, angles, false, 0 );
|
|
return pPhysicsObject;
|
|
}
|
|
|
|
extern ConVar ai_show_hull_attacks;
|
|
void CASW_Door::FallCrush()
|
|
{
|
|
Vector vForward;
|
|
QAngle ang = GetAbsAngles();
|
|
ang[PITCH] = 0;
|
|
ang[ROLL] = 0;
|
|
|
|
// if ( DoorNeedsFlip() )
|
|
// {
|
|
// ang[YAW] += 180;
|
|
// }
|
|
|
|
if ( m_bFlipped )
|
|
{
|
|
ang[YAW] += 180;
|
|
}
|
|
|
|
AngleVectors(ang, &vForward);
|
|
// adjust the sweep of destruction by how far fallen over the door is
|
|
float length = 80.0f;
|
|
float start_offset = 99;
|
|
if ( m_iFallingStage == 1 )
|
|
{
|
|
length *= 0.333f;
|
|
start_offset = 40;
|
|
}
|
|
if ( m_iFallingStage == 2 )
|
|
{
|
|
length *= 0.666f;
|
|
start_offset = 49;
|
|
}
|
|
|
|
Vector vStart = GetAbsOrigin() + vForward * start_offset;
|
|
Vector vEnd = vStart + vForward * length;
|
|
|
|
// compute a bounding box that encompasses the door, no matter its angle
|
|
float wide = 60;
|
|
float deep = 10;
|
|
float tall = 140;
|
|
Vector pre_top_left(-deep, -wide, 0);
|
|
Vector pre_top_right(deep, -wide, 0);
|
|
Vector pre_bottom_right(deep, wide, tall);
|
|
Vector pre_bottom_left(-deep, wide, tall);
|
|
Vector tl, tr, br, bl;
|
|
|
|
matrix3x4_t fRotateMatrix;
|
|
AngleMatrix(ang, fRotateMatrix);
|
|
VectorRotate( pre_top_left, fRotateMatrix, tl);
|
|
VectorRotate( pre_top_right, fRotateMatrix, tr);
|
|
VectorRotate( pre_bottom_right, fRotateMatrix, br);
|
|
VectorRotate( pre_bottom_left, fRotateMatrix, bl);
|
|
|
|
Vector mins, maxs;
|
|
mins.x = MIN(MIN(tl.x, tr.x), MIN(br.x, bl.x));
|
|
mins.y = MIN(MIN(tl.y, tr.y), MIN(br.y, bl.y));
|
|
mins.z = 0;
|
|
maxs.x = MAX(MAX(tl.x, tr.x), MAX(br.x, bl.x));
|
|
maxs.y = MAX(MAX(tl.y, tr.y), MAX(br.y, bl.y));
|
|
maxs.z = tall;
|
|
|
|
CTakeDamageInfo dmgInfo( this, this, 1000, DMG_SLASH );
|
|
dmgInfo.SetDamageForce( vForward * 100.0f );
|
|
dmgInfo.SetDamagePosition( vEnd );
|
|
CASW_Trace_Filter_Door_Crush traceFilter( this, COLLISION_GROUP_NONE, &dmgInfo, 1.0f, true );
|
|
|
|
trace_t trace;
|
|
UTIL_TraceHull( vStart, vEnd, mins, maxs, MASK_SHOT_HULL, &traceFilter, &trace );
|
|
|
|
if ( ai_show_hull_attacks.GetBool() )
|
|
{
|
|
NDebugOverlay::SweptBox(vStart, vEnd, mins, maxs, QAngle(0, 0, 0), 255,255, 0, 255, 4.0f);
|
|
NDebugOverlay::Line(vStart + Vector(0,0, 30), vEnd + Vector(0,0, 30), 255, 255, 0, true, 3.9f);
|
|
}
|
|
}
|
|
|
|
/*
|
|
bool CASW_Door::ASWCreateFallenVPhysics()
|
|
{
|
|
// Create the object in the physics system
|
|
bool asleep = HasSpawnFlags( SF_PHYSPROP_START_ASLEEP ) ? true : false;
|
|
|
|
solid_t tmpSolid;
|
|
PhysModelParseSolid( tmpSolid, this, GetModelIndex() );
|
|
PhysGetMassCenterOverride( this, modelinfo->GetVCollide( GetModelIndex() ), tmpSolid );
|
|
|
|
IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_VPHYSICS, 0, asleep, &tmpSolid );
|
|
|
|
if ( !pPhysicsObject )
|
|
{
|
|
SetSolid( SOLID_NONE );
|
|
SetMoveType( MOVETYPE_NONE );
|
|
Warning("ERROR!: Can't create physics object for %s\n", STRING( GetModelName() ) );
|
|
}
|
|
else
|
|
{
|
|
pPhysicsObject->EnableMotion( false );
|
|
}
|
|
|
|
return true;
|
|
}*/
|
|
|
|
// extent contains the volume encompassing open + closed states
|
|
void CASW_Door::ComputeDoorExtent( Extent *extent, unsigned int extentType )
|
|
{
|
|
if ( !extent )
|
|
return;
|
|
|
|
// FIXMEL4DTOMAINMERGE needs implementation
|
|
/*
|
|
if ( extentType & DOOR_EXTENT_CLOSED )
|
|
{
|
|
Extent closedExtent;
|
|
CalculateDoorVolume( m_angRotationClosed, m_angRotationClosed, &extent->lo, &extent->hi );
|
|
|
|
if ( extentType & DOOR_EXTENT_OPEN )
|
|
{
|
|
Extent openExtent;
|
|
UTIL_ComputeAABBForBounds( m_vecForwardBoundsMin, m_vecForwardBoundsMax, m_vecBackBoundsMin, m_vecBackBoundsMax, &openExtent.lo, &openExtent.hi );
|
|
extent->Encompass( openExtent );
|
|
}
|
|
}
|
|
else if ( extentType & DOOR_EXTENT_OPEN )
|
|
{
|
|
UTIL_ComputeAABBForBounds( m_vecForwardBoundsMin, m_vecForwardBoundsMax, m_vecBackBoundsMin, m_vecBackBoundsMax, &extent->lo, &extent->hi );
|
|
}
|
|
*/
|
|
|
|
// FIXMEL4DTOMAINMERGE hack hack
|
|
Extent closedExtent;
|
|
CalculateDoorVolume( vec3_origin, vec3_origin, &extent->lo, &extent->hi );
|
|
|
|
extent->lo += GetAbsOrigin();
|
|
extent->hi += GetAbsOrigin();
|
|
}
|