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.
2059 lines
57 KiB
2059 lines
57 KiB
#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(); |
|
}
|
|
|