mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-12 11:49:09 +00:00
1
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "point_camera.h"
|
||||
#include "modelentities.h"
|
||||
#include "info_camera_link.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CFuncMonitor : public CFuncBrush
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_CLASS( CFuncMonitor, CFuncBrush );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
public:
|
||||
virtual void Activate();
|
||||
virtual void UpdateOnRemove();
|
||||
|
||||
private:
|
||||
void InputSetCamera(inputdata_t &inputdata);
|
||||
void SetCameraByName(const char *szName);
|
||||
void ReleaseCameraLink();
|
||||
|
||||
EHANDLE m_hInfoCameraLink;
|
||||
};
|
||||
|
||||
// automatically hooks in the system's callbacks
|
||||
BEGIN_DATADESC( CFuncMonitor )
|
||||
|
||||
DEFINE_FIELD( m_hInfoCameraLink, FIELD_EHANDLE ),
|
||||
|
||||
// Outputs
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetCamera", InputSetCamera ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_monitor, CFuncMonitor );
|
||||
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CFuncMonitor, DT_FuncMonitor )
|
||||
END_SEND_TABLE()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called after all entities have spawned and after a load game.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFuncMonitor::Activate()
|
||||
{
|
||||
BaseClass::Activate();
|
||||
SetCameraByName(STRING(m_target));
|
||||
}
|
||||
|
||||
void CFuncMonitor::UpdateOnRemove()
|
||||
{
|
||||
ReleaseCameraLink();
|
||||
BaseClass::UpdateOnRemove();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Frees the camera.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFuncMonitor::ReleaseCameraLink()
|
||||
{
|
||||
if ( m_hInfoCameraLink )
|
||||
{
|
||||
UTIL_Remove( m_hInfoCameraLink );
|
||||
m_hInfoCameraLink = NULL;
|
||||
|
||||
// Keep the target up-to-date for save/load
|
||||
m_target = NULL_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sets camera
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFuncMonitor::SetCameraByName(const char *szName)
|
||||
{
|
||||
ReleaseCameraLink();
|
||||
CBaseEntity *pBaseEnt = gEntList.FindEntityByName( NULL, szName );
|
||||
if( pBaseEnt )
|
||||
{
|
||||
CPointCamera *pCamera = dynamic_cast<CPointCamera *>( pBaseEnt );
|
||||
if( pCamera )
|
||||
{
|
||||
// Keep the target up-to-date for save/load
|
||||
m_target = MAKE_STRING( szName );
|
||||
m_hInfoCameraLink = CreateInfoCameraLink( this, pCamera );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CFuncMonitor::InputSetCamera(inputdata_t &inputdata)
|
||||
{
|
||||
SetCameraByName( inputdata.value.String() );
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "entitylist.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "npc_citizen17.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define MAX_ALLIES 10
|
||||
|
||||
class CAI_AllyManager : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CAI_AllyManager, CBaseEntity );
|
||||
|
||||
public:
|
||||
void Spawn();
|
||||
|
||||
void CountAllies( int *pTotal, int *pMedics );
|
||||
|
||||
private:
|
||||
int m_iMaxAllies;
|
||||
int m_iMaxMedics;
|
||||
|
||||
int m_iAlliesLast;
|
||||
int m_iMedicsLast;
|
||||
public:
|
||||
void WatchCounts();
|
||||
|
||||
// Input functions
|
||||
void InputSetMaxAllies( inputdata_t &inputdata );
|
||||
void InputSetMaxMedics( inputdata_t &inputdata );
|
||||
void InputReplenish( inputdata_t &inputdata );
|
||||
|
||||
// Outputs
|
||||
COutputEvent m_SpawnAlly[ MAX_ALLIES ];
|
||||
COutputEvent m_SpawnMedicAlly;
|
||||
COutputEvent m_OnZeroAllies;
|
||||
COutputEvent m_OnZeroMedicAllies;
|
||||
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
ConVar ai_ally_manager_debug("ai_ally_manager_debug", "0" );
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( ai_ally_manager, CAI_AllyManager );
|
||||
|
||||
BEGIN_DATADESC( CAI_AllyManager )
|
||||
DEFINE_KEYFIELD( m_iMaxAllies, FIELD_INTEGER, "maxallies" ),
|
||||
DEFINE_KEYFIELD( m_iMaxMedics, FIELD_INTEGER, "maxmedics" ),
|
||||
DEFINE_FIELD( m_iAlliesLast, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iMedicsLast, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_THINKFUNC( WatchCounts ),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxAllies", InputSetMaxAllies ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxMedics", InputSetMaxMedics ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Replenish", InputReplenish ),
|
||||
|
||||
// Outputs
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 0 ], "SpawnAlly0" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 1 ], "SpawnAlly1" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 2 ], "SpawnAlly2" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 3 ], "SpawnAlly3" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 4 ], "SpawnAlly4" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 5 ], "SpawnAlly5" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 6 ], "SpawnAlly6" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 7 ], "SpawnAlly7" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 8 ], "SpawnAlly8" ),
|
||||
DEFINE_OUTPUT( m_SpawnAlly[ 9 ], "SpawnAlly9" ),
|
||||
|
||||
DEFINE_OUTPUT( m_SpawnMedicAlly, "SpawnMedicAlly" ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnZeroAllies, "OnZeroAllies" ),
|
||||
DEFINE_OUTPUT( m_OnZeroMedicAllies, "OnZeroMedicAllies" ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_AllyManager::Spawn()
|
||||
{
|
||||
SetThink( &CAI_AllyManager::WatchCounts );
|
||||
SetNextThink( gpGlobals->curtime + 1.0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_AllyManager::WatchCounts()
|
||||
{
|
||||
// Count the number of allies with the player right now.
|
||||
int iCurrentAllies;
|
||||
int iCurrentMedics;
|
||||
|
||||
CountAllies( &iCurrentAllies, &iCurrentMedics );
|
||||
|
||||
if ( !iCurrentAllies && m_iAlliesLast )
|
||||
m_OnZeroAllies.FireOutput( this, this, 0 );
|
||||
|
||||
if ( !iCurrentMedics && m_iMedicsLast )
|
||||
m_OnZeroMedicAllies.FireOutput( this, this, 0 );
|
||||
|
||||
m_iAlliesLast = iCurrentAllies;
|
||||
m_iMedicsLast = iCurrentMedics;
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 1.0 );
|
||||
|
||||
if ( ai_ally_manager_debug.GetBool() )
|
||||
DevMsg( "Ally manager counts %d allies, %d of which are medics\n", iCurrentAllies, iCurrentMedics );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_AllyManager::CountAllies( int *pTotal, int *pMedics )
|
||||
{
|
||||
(*pTotal) = (*pMedics) = 0;
|
||||
|
||||
if ( !AI_IsSinglePlayer() )
|
||||
{
|
||||
// @TODO (toml 10-22-04): no MP support right now
|
||||
return;
|
||||
}
|
||||
|
||||
const Vector & vPlayerPos = UTIL_GetLocalPlayer()->GetAbsOrigin();
|
||||
CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs();
|
||||
int nAIs = g_AI_Manager.NumAIs();
|
||||
|
||||
for ( int i = 0; i < nAIs; i++ )
|
||||
{
|
||||
if ( ppAIs[i]->IsAlive() && ppAIs[i]->IsPlayerAlly() )
|
||||
{
|
||||
// Vital allies do not count.
|
||||
if( ppAIs[i]->Classify() == CLASS_PLAYER_ALLY_VITAL )
|
||||
continue;
|
||||
|
||||
// They only count if I can use them.
|
||||
if( ppAIs[i]->HasSpawnFlags(SF_CITIZEN_NOT_COMMANDABLE) )
|
||||
continue;
|
||||
|
||||
// They only count if I can use them.
|
||||
if( ppAIs[i]->IRelationType( UTIL_GetLocalPlayer() ) != D_LI )
|
||||
continue;
|
||||
|
||||
// Skip distant NPCs
|
||||
if ( !ppAIs[i]->IsInPlayerSquad() &&
|
||||
!UTIL_FindClientInPVS( ppAIs[i]->edict() ) &&
|
||||
( ( ppAIs[i]->GetAbsOrigin() - vPlayerPos ).LengthSqr() > 150*12 ||
|
||||
fabsf( ppAIs[i]->GetAbsOrigin().z - vPlayerPos.z ) > 192 ) )
|
||||
continue;
|
||||
|
||||
if( FClassnameIs( ppAIs[i], "npc_citizen" ) )
|
||||
{
|
||||
CNPC_Citizen *pCitizen = assert_cast<CNPC_Citizen *>(ppAIs[i]);
|
||||
if ( !pCitizen->CanJoinPlayerSquad() )
|
||||
continue;
|
||||
|
||||
if ( pCitizen->WasInPlayerSquad() && !pCitizen->IsInPlayerSquad() )
|
||||
continue;
|
||||
|
||||
if ( ppAIs[i]->HasSpawnFlags( SF_CITIZEN_MEDIC ) )
|
||||
(*pMedics)++;
|
||||
}
|
||||
|
||||
(*pTotal)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_AllyManager::InputSetMaxAllies( inputdata_t &inputdata )
|
||||
{
|
||||
m_iMaxAllies = inputdata.value.Int();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_AllyManager::InputSetMaxMedics( inputdata_t &inputdata )
|
||||
{
|
||||
m_iMaxMedics = inputdata.value.Int();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_AllyManager::InputReplenish( inputdata_t &inputdata )
|
||||
{
|
||||
// Count the number of allies with the player right now.
|
||||
int iCurrentAllies;
|
||||
int iCurrentMedics;
|
||||
|
||||
CountAllies( &iCurrentAllies, &iCurrentMedics );
|
||||
|
||||
// TOTAL number of allies to be replaced.
|
||||
int iReplaceAllies = m_iMaxAllies - iCurrentAllies;
|
||||
|
||||
// The number of total allies that should be medics.
|
||||
int iReplaceMedics = m_iMaxMedics - iCurrentMedics;
|
||||
|
||||
if( iReplaceMedics > iReplaceAllies )
|
||||
{
|
||||
// Clamp medics.
|
||||
iReplaceMedics = iReplaceAllies;
|
||||
}
|
||||
|
||||
// Medics.
|
||||
if( m_iMaxMedics > 0 )
|
||||
{
|
||||
|
||||
if( iReplaceMedics > MAX_ALLIES )
|
||||
{
|
||||
// This error is fatal now. (sjb)
|
||||
Msg("**ERROR! ai_allymanager - ReplaceMedics > MAX_ALLIES\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ai_ally_manager_debug.GetBool() )
|
||||
DevMsg( "Ally manager spawning %d medics\n", iReplaceMedics );
|
||||
|
||||
int i;
|
||||
for( i = 0 ; i < iReplaceMedics ; i++ )
|
||||
{
|
||||
m_SpawnMedicAlly.FireOutput( this, this, 0 );
|
||||
|
||||
// Don't forget to count this guy against the number of
|
||||
// allies to be replenished.
|
||||
iReplaceAllies--;
|
||||
}
|
||||
}
|
||||
|
||||
// Allies
|
||||
if( iReplaceAllies < 1 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( iReplaceAllies > MAX_ALLIES )
|
||||
{
|
||||
Msg("**ERROR! ai_allymanager - ReplaceAllies > MAX_ALLIES\n" );
|
||||
iReplaceAllies = MAX_ALLIES;
|
||||
}
|
||||
|
||||
if ( ai_ally_manager_debug.GetBool() )
|
||||
DevMsg( "Ally manager spawning %d regulars\n", iReplaceAllies );
|
||||
|
||||
int i;
|
||||
for( i = 0 ; i < iReplaceAllies ; i++ )
|
||||
{
|
||||
m_SpawnAlly[ i ].FireOutput( this, this, 0 );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,318 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_BEHAVIOR_ACTBUSY_H
|
||||
#define AI_BEHAVIOR_ACTBUSY_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_behavior.h"
|
||||
#include "ai_goalentity.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
enum
|
||||
{
|
||||
ACTBUSY_TYPE_DEFAULT = 0,
|
||||
ACTBUSY_TYPE_COMBAT,
|
||||
};
|
||||
|
||||
enum busyinterrupt_t
|
||||
{
|
||||
BA_INT_NONE, // Nothing breaks us out of this
|
||||
BA_INT_DANGER, // Only danger signals interrupts this busy anim. The player will be ignored.
|
||||
BA_INT_PLAYER, // The Player's presence interrupts this busy anim
|
||||
BA_INT_AMBUSH, // We're waiting to ambush enemies. Don't break on danger sounds in front of us.
|
||||
BA_INT_COMBAT, // Only break out if we're shot at.
|
||||
BA_INT_ZOMBIESLUMP, // Zombies who are slumped on the ground.
|
||||
BA_INT_SIEGE_DEFENSE,
|
||||
};
|
||||
|
||||
enum busyanimparts_t
|
||||
{
|
||||
BA_BUSY,
|
||||
BA_ENTRY,
|
||||
BA_EXIT,
|
||||
|
||||
BA_MAX_ANIMS,
|
||||
};
|
||||
|
||||
struct busyanim_t
|
||||
{
|
||||
string_t iszName;
|
||||
Activity iActivities[BA_MAX_ANIMS];
|
||||
string_t iszSequences[BA_MAX_ANIMS];
|
||||
string_t iszSounds[BA_MAX_ANIMS];
|
||||
float flMinTime; // Min time spent in this busy animation
|
||||
float flMaxTime; // Max time spent in this busy animation. 0 means continue until interrupted.
|
||||
busyinterrupt_t iBusyInterruptType;
|
||||
bool bUseAutomovement;
|
||||
};
|
||||
|
||||
struct busysafezone_t
|
||||
{
|
||||
Vector vecMins;
|
||||
Vector vecMaxs;
|
||||
};
|
||||
|
||||
#define NO_MAX_TIME -1
|
||||
|
||||
class CAI_ActBusyGoal;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAI_ActBusyBehavior : public CAI_SimpleBehavior
|
||||
{
|
||||
DECLARE_CLASS( CAI_ActBusyBehavior, CAI_SimpleBehavior );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
CAI_ActBusyBehavior();
|
||||
|
||||
enum
|
||||
{
|
||||
// Schedules
|
||||
SCHED_ACTBUSY_START_BUSYING = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_ACTBUSY_BUSY,
|
||||
SCHED_ACTBUSY_STOP_BUSYING,
|
||||
SCHED_ACTBUSY_LEAVE,
|
||||
SCHED_ACTBUSY_TELEPORT_TO_BUSY,
|
||||
NEXT_SCHEDULE,
|
||||
|
||||
// Tasks
|
||||
TASK_ACTBUSY_PLAY_BUSY_ANIM = BaseClass::NEXT_TASK,
|
||||
TASK_ACTBUSY_PLAY_ENTRY,
|
||||
TASK_ACTBUSY_PLAY_EXIT,
|
||||
TASK_ACTBUSY_TELEPORT_TO_BUSY,
|
||||
TASK_ACTBUSY_WALK_PATH_TO_BUSY,
|
||||
TASK_ACTBUSY_GET_PATH_TO_ACTBUSY,
|
||||
TASK_ACTBUSY_VERIFY_EXIT,
|
||||
NEXT_TASK,
|
||||
|
||||
// Conditions
|
||||
COND_ACTBUSY_LOST_SEE_ENTITY = BaseClass::NEXT_CONDITION,
|
||||
COND_ACTBUSY_AWARE_OF_ENEMY_IN_SAFE_ZONE,
|
||||
COND_ACTBUSY_ENEMY_TOO_CLOSE,
|
||||
NEXT_CONDITION,
|
||||
};
|
||||
|
||||
virtual const char *GetName() { return "ActBusy"; }
|
||||
|
||||
void Enable( CAI_ActBusyGoal *pGoal, float flRange, bool bVisibleOnly );
|
||||
void OnRestore();
|
||||
void SetBusySearchRange( float flRange );
|
||||
void Disable( void );
|
||||
void ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintNode = NULL, float flMaxTime = NO_MAX_TIME, bool bVisibleOnly = false, bool bTeleportToBusy = false, bool bUseNearestBusy = false, CBaseEntity *pSeeEntity = NULL, Activity activity = ACT_INVALID );
|
||||
void ForceActBusyLeave( bool bVisibleOnly = false );
|
||||
void StopBusying( void );
|
||||
bool IsStopBusying();
|
||||
CAI_Hint *FindActBusyHintNode( void );
|
||||
CAI_Hint *FindCombatActBusyHintNode( void );
|
||||
CAI_Hint *FindCombatActBusyTeleportHintNode( void );
|
||||
bool CanSelectSchedule( void );
|
||||
bool IsCurScheduleOverridable( void );
|
||||
bool ShouldIgnoreSound( CSound *pSound );
|
||||
void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
void GatherConditions( void );
|
||||
void BuildScheduleTestBits( void );
|
||||
void EndScheduleSelection( void );
|
||||
Activity NPC_TranslateActivity( Activity nActivity );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
void CheckAndCleanupOnExit( void );
|
||||
bool FValidateHintType( CAI_Hint *pHint );
|
||||
bool ActBusyNodeStillActive( void );
|
||||
bool IsMovingToBusy( void ) { return m_bMovingToBusy; }
|
||||
bool IsEnabled( void ) { return m_bEnabled; }
|
||||
float GetReasonableFacingDist( void ) { return 0; } // Actbusy ignores reasonable facing
|
||||
bool IsInterruptable( void );
|
||||
bool ShouldPlayerAvoid( void );
|
||||
void SetUseRenderBounds( bool bUseBounds ) { m_bUseRenderBoundsForCollision = bUseBounds; }
|
||||
void ComputeAndSetRenderBounds();
|
||||
bool CanFlinch( void );
|
||||
bool CanRunAScriptedNPCInteraction( bool bForced );
|
||||
void OnScheduleChange();
|
||||
bool QueryHearSound( CSound *pSound );
|
||||
void OnSeeEntity( CBaseEntity *pEntity );
|
||||
bool NeedsToPlayExitAnim() { return m_bNeedsToPlayExitAnim; }
|
||||
|
||||
// Returns true if the current NPC is acting busy, or moving to an actbusy
|
||||
bool IsActive( void );
|
||||
// Returns true if the current NPC is actually acting busy (i.e. inside an act busy anim)
|
||||
bool IsInsideActBusy( void ) { return m_bBusy; }
|
||||
|
||||
// Combat act busy stuff
|
||||
bool IsCombatActBusy();
|
||||
void CollectSafeZoneVolumes( CAI_ActBusyGoal *pActBusyGoal );
|
||||
bool IsInSafeZone( CBaseEntity *pEntity );
|
||||
int CountEnemiesInSafeZone();
|
||||
|
||||
private:
|
||||
virtual int SelectSchedule( void );
|
||||
int SelectScheduleForLeaving( void );
|
||||
int SelectScheduleWhileNotBusy( int iBase );
|
||||
int SelectScheduleWhileBusy( void );
|
||||
virtual void StartTask( const Task_t *pTask );
|
||||
virtual void RunTask( const Task_t *pTask );
|
||||
void NotifyBusyEnding( void );
|
||||
bool HasAnimForActBusy( int iActBusy, busyanimparts_t AnimPart );
|
||||
bool PlayAnimForActBusy( busyanimparts_t AnimPart );
|
||||
void PlaySoundForActBusy( busyanimparts_t AnimPart );
|
||||
|
||||
private:
|
||||
bool m_bEnabled;
|
||||
bool m_bForceActBusy;
|
||||
Activity m_ForcedActivity;
|
||||
bool m_bTeleportToBusy;
|
||||
bool m_bUseNearestBusy;
|
||||
bool m_bLeaving;
|
||||
bool m_bVisibleOnly;
|
||||
bool m_bUseRenderBoundsForCollision;
|
||||
float m_flForcedMaxTime;
|
||||
bool m_bBusy;
|
||||
bool m_bMovingToBusy;
|
||||
bool m_bNeedsToPlayExitAnim;
|
||||
float m_flNextBusySearchTime;
|
||||
float m_flEndBusyAt;
|
||||
float m_flBusySearchRange;
|
||||
bool m_bInQueue;
|
||||
int m_iCurrentBusyAnim;
|
||||
CHandle<CAI_ActBusyGoal> m_hActBusyGoal;
|
||||
bool m_bNeedToSetBounds;
|
||||
EHANDLE m_hSeeEntity;
|
||||
float m_fTimeLastSawSeeEntity;
|
||||
bool m_bExitedBusyToDueLostSeeEntity;
|
||||
bool m_bExitedBusyToDueSeeEnemy;
|
||||
|
||||
int m_iNumConsecutivePathFailures; // Count how many times we failed to find a path to a node, so we can consider teleporting.
|
||||
bool m_bAutoFireWeapon;
|
||||
float m_flDeferUntil;
|
||||
int m_iNumEnemiesInSafeZone;
|
||||
|
||||
CUtlVector<busysafezone_t>m_SafeZones;
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A level tool to control the actbusy behavior.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAI_ActBusyGoal : public CAI_GoalEntity
|
||||
{
|
||||
DECLARE_CLASS( CAI_ActBusyGoal, CAI_GoalEntity );
|
||||
public:
|
||||
CAI_ActBusyGoal()
|
||||
{
|
||||
// Support legacy maps, where this value used to be set from a constant (with a value of 1).
|
||||
// Now designers can specify whatever they want in Hammer. Take care of old maps by setting
|
||||
// this in the constructor. (sjb)
|
||||
m_flSeeEntityTimeout = 1;
|
||||
}
|
||||
|
||||
virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCStartedBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCLeft( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCLostSeeEntity( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCSeeEnemy( CAI_BaseNPC *pNPC );
|
||||
|
||||
int GetType() { return m_iType; }
|
||||
bool IsCombatActBusyTeleportAllowed() { return m_bAllowCombatActBusyTeleport; }
|
||||
|
||||
protected:
|
||||
CAI_ActBusyBehavior *GetBusyBehaviorForNPC( const char *pszActorName, CBaseEntity *pActivator, CBaseEntity *pCaller, const char *sInputName );
|
||||
CAI_ActBusyBehavior *GetBusyBehaviorForNPC( CBaseEntity *pEntity, const char *sInputName );
|
||||
|
||||
void EnableGoal( CAI_BaseNPC *pAI );
|
||||
|
||||
// Inputs
|
||||
virtual void InputActivate( inputdata_t &inputdata );
|
||||
virtual void InputDeactivate( inputdata_t &inputdata );
|
||||
void InputSetBusySearchRange( inputdata_t &inputdata );
|
||||
void InputForceNPCToActBusy( inputdata_t &inputdata );
|
||||
void InputForceThisNPCToActBusy( inputdata_t &inputdata );
|
||||
void InputForceThisNPCToLeave( inputdata_t &inputdata );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
protected:
|
||||
float m_flBusySearchRange;
|
||||
bool m_bVisibleOnly;
|
||||
int m_iType;
|
||||
bool m_bAllowCombatActBusyTeleport;
|
||||
|
||||
public:
|
||||
// Let the actbusy behavior query these so we don't have to duplicate the data.
|
||||
string_t m_iszSeeEntityName;
|
||||
float m_flSeeEntityTimeout;
|
||||
string_t m_iszSafeZoneVolume;
|
||||
int m_iSightMethod;
|
||||
|
||||
protected:
|
||||
COutputEHANDLE m_OnNPCStartedBusy;
|
||||
COutputEHANDLE m_OnNPCFinishedBusy;
|
||||
COutputEHANDLE m_OnNPCLeft;
|
||||
COutputEHANDLE m_OnNPCLostSeeEntity;
|
||||
COutputEHANDLE m_OnNPCSeeEnemy;
|
||||
};
|
||||
|
||||
// Maximum number of nodes allowed in an actbusy queue
|
||||
#define MAX_QUEUE_NODES 20
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A level tool to control the actbusy behavior to create NPC queues
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAI_ActBusyQueueGoal : public CAI_ActBusyGoal
|
||||
{
|
||||
DECLARE_CLASS( CAI_ActBusyQueueGoal, CAI_ActBusyGoal );
|
||||
public:
|
||||
virtual void Spawn( void );
|
||||
virtual void DrawDebugGeometryOverlays( void );
|
||||
virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCStartedBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC );
|
||||
virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC );
|
||||
|
||||
virtual void InputActivate( inputdata_t &inputdata );
|
||||
void InputPlayerStartedBlocking( inputdata_t &inputdata );
|
||||
void InputPlayerStoppedBlocking( inputdata_t &inputdata );
|
||||
void InputMoveQueueUp( inputdata_t &inputdata );
|
||||
|
||||
void PushNPCBackInQueue( CAI_BaseNPC *pNPC, int iStartingNode );
|
||||
void RemoveNPCFromQueue( CAI_BaseNPC *pNPC );
|
||||
void RecalculateQueueCount( void );
|
||||
void QueueThink( void );
|
||||
void MoveQueueUp( void );
|
||||
void MoveQueueUpThink( void );
|
||||
bool NodeIsOccupied( int i );
|
||||
CAI_BaseNPC *GetNPCOnNode( int iNode );
|
||||
CAI_ActBusyBehavior *GetQueueBehaviorForNPC( CAI_BaseNPC *pNPC );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
int m_iCurrentQueueCount;
|
||||
CHandle<CAI_Hint> m_hNodes[ MAX_QUEUE_NODES ];
|
||||
bool m_bPlayerBlockedNodes[ MAX_QUEUE_NODES ];
|
||||
EHANDLE m_hExitNode;
|
||||
EHANDLE m_hExitingNPC;
|
||||
bool m_bForceReachFront;
|
||||
|
||||
// Read from mapdata
|
||||
string_t m_iszNodes[ MAX_QUEUE_NODES ];
|
||||
string_t m_iszExitNode;
|
||||
|
||||
// Outputs
|
||||
COutputInt m_OnQueueMoved;
|
||||
COutputEHANDLE m_OnNPCLeftQueue;
|
||||
COutputEHANDLE m_OnNPCStartedLeavingQueue;
|
||||
};
|
||||
|
||||
#endif // AI_BEHAVIOR_ACTBUSY_H
|
||||
@@ -0,0 +1,776 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_behavior_functank.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_senses.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// How long to fire a func tank before running schedule selection again.
|
||||
#define FUNCTANK_FIRE_TIME 5.0f
|
||||
|
||||
BEGIN_DATADESC( CAI_FuncTankBehavior )
|
||||
DEFINE_FIELD( m_hFuncTank, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_bMounted, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flBusyTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_bSpottedPlayerOutOfCover, FIELD_BOOLEAN ),
|
||||
END_DATADESC();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//--k---------------------------------------------------------------------------
|
||||
CAI_FuncTankBehavior::CAI_FuncTankBehavior()
|
||||
{
|
||||
m_hFuncTank = NULL;
|
||||
m_bMounted = false;
|
||||
m_flBusyTime = 0.0f;
|
||||
m_bSpottedPlayerOutOfCover = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Deconstructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CAI_FuncTankBehavior::~CAI_FuncTankBehavior()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_FuncTankBehavior::CanSelectSchedule()
|
||||
{
|
||||
// If we don't have a func_tank do not bother with conditions, schedules, etc.
|
||||
if ( !m_hFuncTank )
|
||||
return false;
|
||||
|
||||
// Are you alive, in a script?
|
||||
if ( !GetOuter()->IsInterruptable() )
|
||||
return false;
|
||||
|
||||
// Commander is giving you orders?
|
||||
if ( GetOuter()->HasCondition( COND_RECEIVED_ORDERS ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::BeginScheduleSelection()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::EndScheduleSelection()
|
||||
{
|
||||
if ( m_bMounted )
|
||||
{
|
||||
Dismount();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::PrescheduleThink()
|
||||
{
|
||||
BaseClass::PrescheduleThink();
|
||||
|
||||
if ( !HasCondition(COND_SEE_PLAYER) )
|
||||
{
|
||||
m_bSpottedPlayerOutOfCover = false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_FuncTankBehavior::SelectSchedule()
|
||||
{
|
||||
// This shouldn't get called with an m_hFuncTank, see CanSelectSchedule.
|
||||
Assert( m_hFuncTank );
|
||||
|
||||
// If we've been told to dismount, or we are out of ammo - dismount.
|
||||
if ( HasCondition( COND_FUNCTANK_DISMOUNT ) || m_hFuncTank->GetAmmoCount() == 0 )
|
||||
{
|
||||
if ( m_bMounted )
|
||||
{
|
||||
Dismount();
|
||||
}
|
||||
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
// If we are not mounted to a func_tank look for one.
|
||||
if ( !IsMounted() )
|
||||
{
|
||||
return SCHED_MOVE_TO_FUNCTANK;
|
||||
}
|
||||
|
||||
// If we have an enemy, it's in the viewcone & we have LOS to it
|
||||
if ( GetEnemy() )
|
||||
{
|
||||
// Tell the func tank whenever we see the player for the first time since not seeing him for a while
|
||||
if ( HasCondition( COND_NEW_ENEMY ) && GetEnemy()->IsPlayer() && !m_bSpottedPlayerOutOfCover )
|
||||
{
|
||||
m_bSpottedPlayerOutOfCover = true;
|
||||
m_hFuncTank->NPC_JustSawPlayer( GetEnemy() );
|
||||
}
|
||||
|
||||
// Fire at the enemy.
|
||||
return SCHED_FIRE_FUNCTANK;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scan for enemies.
|
||||
return SCHED_SCAN_WITH_FUNCTANK;
|
||||
}
|
||||
|
||||
return SCHED_IDLE_STAND;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : activity -
|
||||
// Output : Activity
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CAI_FuncTankBehavior::NPC_TranslateActivity( Activity activity )
|
||||
{
|
||||
// If I'm on the gun, I play the idle manned gun animation
|
||||
if ( m_bMounted )
|
||||
return ACT_IDLE_MANNEDGUN;
|
||||
|
||||
return BaseClass::NPC_TranslateActivity( activity );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::Dismount( void )
|
||||
{
|
||||
SetBusy( gpGlobals->curtime + AI_FUNCTANK_BEHAVIOR_BUSYTIME );
|
||||
|
||||
Assert( m_hFuncTank );
|
||||
|
||||
if ( m_hFuncTank )
|
||||
{
|
||||
GetOuter()->SpeakSentence( FUNCTANK_SENTENCE_DISMOUNTING );
|
||||
|
||||
Assert( m_hFuncTank->IsMarkedForDeletion() || m_hFuncTank->GetController() == GetOuter() );
|
||||
|
||||
m_hFuncTank->NPC_SetInRoute( false );
|
||||
if ( m_hFuncTank->GetController() == GetOuter() )
|
||||
m_hFuncTank->StopControl();
|
||||
SetFuncTank( NULL );
|
||||
}
|
||||
|
||||
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_UNHOLSTERED );
|
||||
|
||||
m_bMounted = false;
|
||||
|
||||
// Set this condition to force breakout of any func_tank behavior schedules
|
||||
SetCondition( COND_FUNCTANK_DISMOUNT );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_FuncTankBehavior::OnTakeDamage_Alive( const CTakeDamageInfo &info )
|
||||
{
|
||||
int iResult = BaseClass::OnTakeDamage_Alive( info );
|
||||
if ( !iResult )
|
||||
return 0;
|
||||
|
||||
// If we've been hit by the player, and the player's not targetable
|
||||
// by our func_tank, get off the tank.
|
||||
CBaseEntity *pAttacker = info.GetAttacker();
|
||||
bool bValidDismountAttacker = (pAttacker && pAttacker->IsPlayer());
|
||||
|
||||
#ifdef HL2_EPISODIC
|
||||
bValidDismountAttacker = true;
|
||||
#endif
|
||||
|
||||
if ( m_hFuncTank && bValidDismountAttacker == true )
|
||||
{
|
||||
if ( !m_hFuncTank->IsEntityInViewCone( pAttacker ) )
|
||||
{
|
||||
SetCondition( COND_FUNCTANK_DISMOUNT );
|
||||
}
|
||||
}
|
||||
|
||||
return iResult;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_FUNCTANK_ANNOUNCE_SCAN:
|
||||
{
|
||||
if ( random->RandomInt( 0, 3 ) == 0 )
|
||||
{
|
||||
GetOuter()->SpeakSentence( FUNCTANK_SENTENCE_SCAN_FOR_ENEMIES );
|
||||
}
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_GET_PATH_TO_FUNCTANK:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
|
||||
Vector vecManPos;
|
||||
m_hFuncTank->NPC_FindManPoint( vecManPos );
|
||||
AI_NavGoal_t goal( vecManPos );
|
||||
goal.pTarget = m_hFuncTank;
|
||||
if ( GetNavigator()->SetGoal( goal ) )
|
||||
{
|
||||
GetNavigator()->SetArrivalDirection( m_hFuncTank->GetAbsAngles() );
|
||||
TaskComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskFail("NO PATH");
|
||||
|
||||
// Don't try and use me again for a while
|
||||
SetBusy( gpGlobals->curtime + AI_FUNCTANK_BEHAVIOR_BUSYTIME );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TASK_FACE_FUNCTANK:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we've reached the func_tank
|
||||
Vector vecManPos;
|
||||
m_hFuncTank->NPC_FindManPoint( vecManPos );
|
||||
|
||||
// More leniency in Z.
|
||||
Vector vecDelta = (vecManPos - GetAbsOrigin());
|
||||
if ( fabs(vecDelta.x) > 16 || fabs(vecDelta.y) > 16 || fabs(vecDelta.z) > 48 )
|
||||
{
|
||||
TaskFail( "Not correctly on func_tank man point" );
|
||||
m_hFuncTank->NPC_InterruptRoute();
|
||||
return;
|
||||
}
|
||||
|
||||
GetMotor()->SetIdealYawToTarget( m_hFuncTank->GetAbsOrigin() );
|
||||
GetOuter()->SetTurnActivity();
|
||||
break;
|
||||
}
|
||||
|
||||
case TASK_HOLSTER_WEAPON:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( GetOuter()->IsWeaponHolstered() || !GetOuter()->CanHolsterWeapon() )
|
||||
{
|
||||
GetOuter()->SpeakSentence( FUNCTANK_SENTENCE_JUST_MOUNTED );
|
||||
|
||||
// We are at the correct position and facing for the func_tank, mount it.
|
||||
m_hFuncTank->StartControl( GetOuter() );
|
||||
GetOuter()->ClearEnemyMemory();
|
||||
m_bMounted = true;
|
||||
TaskComplete();
|
||||
|
||||
GetOuter()->SetIdealActivity( ACT_IDLE_MANNEDGUN );
|
||||
}
|
||||
else
|
||||
{
|
||||
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TASK_FIRE_FUNCTANK:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
GetOuter()->m_flWaitFinished = gpGlobals->curtime + FUNCTANK_FIRE_TIME;
|
||||
break;
|
||||
}
|
||||
case TASK_SCAN_LEFT_FUNCTANK:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
|
||||
GetMotor()->SetIdealYawToTarget( m_hFuncTank->GetAbsOrigin() );
|
||||
|
||||
float flCenterYaw = m_hFuncTank->YawCenterWorld();
|
||||
float flYawRange = m_hFuncTank->YawRange();
|
||||
float flScanAmount = random->RandomFloat( 0, flYawRange );
|
||||
QAngle vecTargetAngles( 0, UTIL_AngleMod( flCenterYaw + flScanAmount ), 0 );
|
||||
|
||||
/*
|
||||
float flCenterPitch = m_hFuncTank->YawCenterWorld();
|
||||
float flPitchRange = m_hFuncTank->PitchRange();
|
||||
float flPitch = random->RandomFloat( -flPitchRange, flPitchRange );
|
||||
QAngle vecTargetAngles( flCenterPitch + flPitch, UTIL_AngleMod( flCenterYaw + flScanAmount ), 0 );
|
||||
*/
|
||||
|
||||
Vector vecTargetForward;
|
||||
AngleVectors( vecTargetAngles, &vecTargetForward );
|
||||
Vector vecTarget = GetOuter()->EyePosition() + (vecTargetForward * 256);
|
||||
GetOuter()->AddLookTarget( vecTarget, 1.0, 2.0, 0.2 );
|
||||
|
||||
m_hFuncTank->NPC_SetIdleAngle( vecTarget );
|
||||
|
||||
break;
|
||||
}
|
||||
case TASK_SCAN_RIGHT_FUNCTANK:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
|
||||
GetMotor()->SetIdealYawToTarget( m_hFuncTank->GetAbsOrigin() );
|
||||
|
||||
float flCenterYaw = m_hFuncTank->YawCenterWorld();
|
||||
float flYawRange = m_hFuncTank->YawRange();
|
||||
float flScanAmount = random->RandomFloat( 0, flYawRange );
|
||||
QAngle vecTargetAngles( 0, UTIL_AngleMod( flCenterYaw - flScanAmount ), 0 );
|
||||
|
||||
/*
|
||||
float flCenterPitch = m_hFuncTank->YawCenterWorld();
|
||||
float flPitchRange = m_hFuncTank->PitchRange();
|
||||
float flPitch = random->RandomFloat( -flPitchRange, flPitchRange );
|
||||
QAngle vecTargetAngles( flCenterPitch + flPitch, UTIL_AngleMod( flCenterYaw - flScanAmount ), 0 );
|
||||
*/
|
||||
|
||||
Vector vecTargetForward;
|
||||
AngleVectors( vecTargetAngles, &vecTargetForward );
|
||||
Vector vecTarget = GetOuter()->EyePosition() + (vecTargetForward * 256);
|
||||
GetOuter()->AddLookTarget( vecTarget, 1.0, 2.0, 0.2 );
|
||||
|
||||
m_hFuncTank->NPC_SetIdleAngle( vecTarget );
|
||||
|
||||
break;
|
||||
}
|
||||
case TASK_FORGET_ABOUT_FUNCTANK:
|
||||
{
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskFail( FAIL_NO_TARGET );
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_FACE_FUNCTANK:
|
||||
{
|
||||
Assert( m_hFuncTank );
|
||||
|
||||
GetMotor()->UpdateYaw();
|
||||
|
||||
if ( GetOuter()->FacingIdeal() )
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TASK_HOLSTER_WEAPON:
|
||||
{
|
||||
Assert( m_hFuncTank );
|
||||
|
||||
if ( GetOuter()->IsWeaponHolstered() )
|
||||
{
|
||||
GetOuter()->SpeakSentence( FUNCTANK_SENTENCE_JUST_MOUNTED );
|
||||
|
||||
// We are at the correct position and facing for the func_tank, mount it.
|
||||
m_hFuncTank->StartControl( GetOuter() );
|
||||
GetOuter()->ClearEnemyMemory();
|
||||
m_bMounted = true;
|
||||
TaskComplete();
|
||||
|
||||
GetOuter()->SetIdealActivity( ACT_IDLE_MANNEDGUN );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case TASK_FIRE_FUNCTANK:
|
||||
{
|
||||
Assert( m_hFuncTank );
|
||||
|
||||
if( GetOuter()->m_flWaitFinished < gpGlobals->curtime )
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
|
||||
if ( m_hFuncTank->NPC_HasEnemy() )
|
||||
{
|
||||
GetOuter()->SetLastAttackTime( gpGlobals->curtime );
|
||||
m_hFuncTank->NPC_Fire();
|
||||
|
||||
// The NPC may have decided to stop using the func_tank, because it's out of ammo.
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
TaskComplete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
|
||||
Assert( m_hFuncTank );
|
||||
|
||||
if ( m_hFuncTank->GetAmmoCount() == 0 )
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TASK_SCAN_LEFT_FUNCTANK:
|
||||
case TASK_SCAN_RIGHT_FUNCTANK:
|
||||
{
|
||||
GetMotor()->UpdateYaw();
|
||||
if ( GetOuter()->FacingIdeal() )
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TASK_FORGET_ABOUT_FUNCTANK:
|
||||
{
|
||||
m_hFuncTank->NPC_InterruptRoute();
|
||||
SetBusy( gpGlobals->curtime + AI_FUNCTANK_BEHAVIOR_BUSYTIME );
|
||||
TaskComplete();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( m_hFuncTank )
|
||||
{
|
||||
Dismount();
|
||||
}
|
||||
Assert( !m_hFuncTank );
|
||||
|
||||
BaseClass::Event_Killed( info );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::UpdateOnRemove( void )
|
||||
{
|
||||
if ( m_hFuncTank )
|
||||
{
|
||||
Dismount();
|
||||
}
|
||||
|
||||
BaseClass::UpdateOnRemove();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::SetFuncTank( CHandle<CFuncTank> hFuncTank )
|
||||
{
|
||||
if ( m_hFuncTank && !hFuncTank )
|
||||
{
|
||||
SetBusy( gpGlobals->curtime + AI_FUNCTANK_BEHAVIOR_BUSYTIME );
|
||||
SetCondition( COND_FUNCTANK_DISMOUNT );
|
||||
}
|
||||
|
||||
m_hFuncTank = hFuncTank;
|
||||
GetOuter()->ClearSchedule( "Setting a new func_tank" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::AimGun( void )
|
||||
{
|
||||
if ( m_bMounted && m_hFuncTank)
|
||||
{
|
||||
Vector vecForward;
|
||||
AngleVectors( m_hFuncTank->GetAbsAngles(), &vecForward );
|
||||
GetOuter()->SetAim( vecForward );
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::AimGun();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_FuncTankBehavior::GatherConditions()
|
||||
{
|
||||
BaseClass::GatherConditions();
|
||||
|
||||
// Since we can't pathfind, if we can't see the enemy, he's eluded us
|
||||
// So we deliberately ignore unreachability
|
||||
if ( GetEnemy() && !HasCondition(COND_SEE_ENEMY) )
|
||||
{
|
||||
if ( gpGlobals->curtime - GetOuter()->GetEnemyLastTimeSeen() >= 3.0f )
|
||||
{
|
||||
GetOuter()->MarkEnemyAsEluded();
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_hFuncTank )
|
||||
{
|
||||
m_bMounted = false;
|
||||
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_UNHOLSTERED );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity *CAI_FuncTankBehavior::BestEnemy( void )
|
||||
{
|
||||
// Only use this BestEnemy call when we are on the manned gun.
|
||||
if ( !m_hFuncTank ||!IsMounted() )
|
||||
return BaseClass::BestEnemy();
|
||||
|
||||
CBaseEntity *pBestEnemy = NULL;
|
||||
int iBestDistSq = MAX_COORD_RANGE * MAX_COORD_RANGE; // so first visible entity will become the closest.
|
||||
int iBestPriority = -1000;
|
||||
bool bBestUnreachable = false; // Forces initial check
|
||||
bool bBestSeen = false;
|
||||
bool bUnreachable = false;
|
||||
int iDistSq;
|
||||
|
||||
AIEnemiesIter_t iter;
|
||||
|
||||
// Get the current npc for checking from.
|
||||
CAI_BaseNPC *pNPC = GetOuter();
|
||||
if ( !pNPC )
|
||||
return NULL;
|
||||
|
||||
for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) )
|
||||
{
|
||||
CBaseEntity *pEnemy = pEMemory->hEnemy;
|
||||
if ( !pEnemy || !pEnemy->IsAlive() )
|
||||
continue;
|
||||
|
||||
// UNDONE: Move relationship checks into IsValidEnemy?
|
||||
if ( ( pEnemy->GetFlags() & FL_NOTARGET ) ||
|
||||
( pNPC->IRelationType( pEnemy ) != D_HT && pNPC->IRelationType( pEnemy ) != D_FR ) ||
|
||||
!IsValidEnemy( pEnemy ) )
|
||||
continue;
|
||||
|
||||
if ( pEMemory->timeLastSeen < pNPC->GetAcceptableTimeSeenEnemy() )
|
||||
continue;
|
||||
|
||||
if ( pEMemory->timeValidEnemy > gpGlobals->curtime )
|
||||
continue;
|
||||
|
||||
// Skip enemies that have eluded me to prevent infinite loops
|
||||
if ( GetEnemies()->HasEludedMe( pEnemy ) )
|
||||
continue;
|
||||
|
||||
// Establish the reachability of this enemy
|
||||
bUnreachable = pNPC->IsUnreachable( pEnemy );
|
||||
|
||||
// Check view cone of the view tank here.
|
||||
bUnreachable = !m_hFuncTank->IsEntityInViewCone( pEnemy );
|
||||
if ( !bUnreachable )
|
||||
{
|
||||
// It's in the viewcone. Now make sure we have LOS to it.
|
||||
bUnreachable = !m_hFuncTank->HasLOSTo( pEnemy );
|
||||
}
|
||||
|
||||
// If best is reachable and current is unreachable, skip the unreachable enemy regardless of priority
|
||||
if ( !bBestUnreachable && bUnreachable )
|
||||
continue;
|
||||
|
||||
// If best is unreachable and current is reachable, always pick the current regardless of priority
|
||||
if ( bBestUnreachable && !bUnreachable )
|
||||
{
|
||||
bBestSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) ); // @TODO (toml 04-02-03): Need to optimize CanSeeEntity() so multiple calls in frame do not recalculate, rather cache
|
||||
iBestPriority = pNPC->IRelationPriority( pEnemy );
|
||||
iBestDistSq = (pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
|
||||
pBestEnemy = pEnemy;
|
||||
bBestUnreachable = bUnreachable;
|
||||
}
|
||||
// If both are unreachable or both are reachable, chose enemy based on priority and distance
|
||||
else if ( pNPC->IRelationPriority( pEnemy ) > iBestPriority )
|
||||
{
|
||||
// this entity is disliked MORE than the entity that we
|
||||
// currently think is the best visible enemy. No need to do
|
||||
// a distance check, just get mad at this one for now.
|
||||
iBestPriority = pNPC->IRelationPriority ( pEnemy );
|
||||
iBestDistSq = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
|
||||
pBestEnemy = pEnemy;
|
||||
bBestUnreachable = bUnreachable;
|
||||
}
|
||||
else if ( pNPC->IRelationPriority( pEnemy ) == iBestPriority )
|
||||
{
|
||||
// this entity is disliked just as much as the entity that
|
||||
// we currently think is the best visible enemy, so we only
|
||||
// get mad at it if it is closer.
|
||||
iDistSq = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr();
|
||||
|
||||
bool bCloser = ( iDistSq < iBestDistSq ) ;
|
||||
|
||||
if ( bCloser || !bBestSeen )
|
||||
{
|
||||
// @TODO (toml 04-02-03): Need to optimize FVisible() so multiple calls in frame do not recalculate, rather cache
|
||||
bool fSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) );
|
||||
if ( ( bCloser && ( fSeen || !bBestSeen ) ) || ( !bCloser && !bBestSeen && fSeen ) )
|
||||
{
|
||||
bBestSeen = fSeen;
|
||||
iBestDistSq = iDistSq;
|
||||
iBestPriority = pNPC->IRelationPriority( pEnemy );
|
||||
pBestEnemy = pEnemy;
|
||||
bBestUnreachable = bUnreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pBestEnemy;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Custom AI schedule data
|
||||
//
|
||||
|
||||
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FuncTankBehavior )
|
||||
|
||||
DECLARE_TASK( TASK_GET_PATH_TO_FUNCTANK )
|
||||
DECLARE_TASK( TASK_FACE_FUNCTANK )
|
||||
DECLARE_TASK( TASK_HOLSTER_WEAPON )
|
||||
DECLARE_TASK( TASK_FIRE_FUNCTANK )
|
||||
DECLARE_TASK( TASK_SCAN_LEFT_FUNCTANK )
|
||||
DECLARE_TASK( TASK_SCAN_RIGHT_FUNCTANK )
|
||||
DECLARE_TASK( TASK_FORGET_ABOUT_FUNCTANK )
|
||||
DECLARE_TASK( TASK_FUNCTANK_ANNOUNCE_SCAN )
|
||||
|
||||
DECLARE_CONDITION( COND_FUNCTANK_DISMOUNT )
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_MOVE_TO_FUNCTANK,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE: SCHED_FAIL_MOVE_TO_FUNCTANK"
|
||||
" TASK_GET_PATH_TO_FUNCTANK 0"
|
||||
" TASK_SPEAK_SENTENCE 1000" // FUNCTANK_SENTENCE_MOVE_TO_MOUNT
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_FUNCTANK 0"
|
||||
" TASK_HOLSTER_WEAPON 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_FUNCTANK_DISMOUNT"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_FIRE_FUNCTANK,
|
||||
|
||||
" Tasks"
|
||||
" TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
|
||||
" TASK_FIRE_FUNCTANK 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_ENEMY_DEAD"
|
||||
" COND_LOST_ENEMY"
|
||||
" COND_ENEMY_OCCLUDED"
|
||||
" COND_WEAPON_BLOCKED_BY_FRIEND"
|
||||
" COND_WEAPON_SIGHT_OCCLUDED"
|
||||
" COND_FUNCTANK_DISMOUNT"
|
||||
)
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_SCAN_WITH_FUNCTANK,
|
||||
|
||||
" Tasks"
|
||||
" TASK_FUNCTANK_ANNOUNCE_SCAN 0"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_WAIT 4"
|
||||
" TASK_SCAN_LEFT_FUNCTANK 0"
|
||||
" TASK_WAIT 4"
|
||||
" TASK_SCAN_RIGHT_FUNCTANK 0"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_PROVOKED"
|
||||
" COND_FUNCTANK_DISMOUNT"
|
||||
)
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_FAIL_MOVE_TO_FUNCTANK,
|
||||
|
||||
" Tasks"
|
||||
" TASK_FORGET_ABOUT_FUNCTANK 0"
|
||||
""
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|
||||
@@ -0,0 +1,120 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_BEHAVIOR_FUNCTANK_H
|
||||
#define AI_BEHAVIOR_FUNCTANK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "simtimer.h"
|
||||
#include "ai_behavior.h"
|
||||
#include "func_tank.h"
|
||||
|
||||
#define AI_FUNCTANK_BEHAVIOR_BUSYTIME 10.0f
|
||||
|
||||
enum
|
||||
{
|
||||
FUNCTANK_SENTENCE_MOVE_TO_MOUNT = SENTENCE_BASE_BEHAVIOR_INDEX,
|
||||
FUNCTANK_SENTENCE_JUST_MOUNTED,
|
||||
FUNCTANK_SENTENCE_SCAN_FOR_ENEMIES,
|
||||
FUNCTANK_SENTENCE_DISMOUNTING,
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAI_FuncTankBehavior : public CAI_SimpleBehavior
|
||||
{
|
||||
DECLARE_CLASS( CAI_FuncTankBehavior, CAI_SimpleBehavior );
|
||||
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
// Contructor/Deconstructor
|
||||
CAI_FuncTankBehavior();
|
||||
~CAI_FuncTankBehavior();
|
||||
|
||||
void UpdateOnRemove();
|
||||
|
||||
// Identifier
|
||||
const char *GetName() { return "FuncTank"; }
|
||||
|
||||
// Schedule
|
||||
bool CanSelectSchedule();
|
||||
void BeginScheduleSelection();
|
||||
void EndScheduleSelection();
|
||||
void PrescheduleThink();
|
||||
|
||||
Activity NPC_TranslateActivity( Activity activity );
|
||||
|
||||
// Conditions:
|
||||
virtual void GatherConditions();
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_MOVE_TO_FUNCTANK = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_FIRE_FUNCTANK,
|
||||
SCHED_SCAN_WITH_FUNCTANK,
|
||||
SCHED_FAIL_MOVE_TO_FUNCTANK,
|
||||
};
|
||||
|
||||
// Tasks
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_GET_PATH_TO_FUNCTANK = BaseClass::NEXT_TASK,
|
||||
TASK_FACE_FUNCTANK,
|
||||
TASK_HOLSTER_WEAPON,
|
||||
TASK_FIRE_FUNCTANK,
|
||||
TASK_SCAN_LEFT_FUNCTANK,
|
||||
TASK_SCAN_RIGHT_FUNCTANK,
|
||||
TASK_FORGET_ABOUT_FUNCTANK,
|
||||
TASK_FUNCTANK_ANNOUNCE_SCAN,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
COND_FUNCTANK_DISMOUNT = BaseClass::NEXT_CONDITION,
|
||||
NEXT_CONDITION,
|
||||
};
|
||||
|
||||
// Combat.
|
||||
CBaseEntity *BestEnemy( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
bool HasFuncTank( void ) { return ( m_hFuncTank != NULL ); }
|
||||
void SetFuncTank( CHandle<CFuncTank> hFuncTank );
|
||||
CFuncTank *GetFuncTank() { return m_hFuncTank; }
|
||||
void AimGun( void );
|
||||
|
||||
void Dismount( void );
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
|
||||
// Time.
|
||||
void SetBusy( float flTime ) { m_flBusyTime = flTime; }
|
||||
bool IsBusy( void ) { return ( gpGlobals->curtime < m_flBusyTime ); }
|
||||
|
||||
bool IsMounted( void ) { return m_bMounted; }
|
||||
|
||||
private:
|
||||
|
||||
// Schedule
|
||||
int SelectSchedule();
|
||||
|
||||
private:
|
||||
|
||||
CHandle<CFuncTank> m_hFuncTank;
|
||||
bool m_bMounted;
|
||||
float m_flBusyTime;
|
||||
bool m_bSpottedPlayerOutOfCover;
|
||||
};
|
||||
|
||||
#endif // AI_BEHAVIOR_FUNCTANK_H
|
||||
@@ -0,0 +1,132 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_behavior_holster.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
BEGIN_DATADESC( CAI_HolsterBehavior )
|
||||
DEFINE_FIELD( m_bWeaponOut, FIELD_BOOLEAN ),
|
||||
END_DATADESC();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CAI_HolsterBehavior::CAI_HolsterBehavior()
|
||||
{
|
||||
// m_AssaultCue = CUE_NO_ASSAULT;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_HolsterBehavior::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
default:
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_HolsterBehavior::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
default:
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_HolsterBehavior::CanSelectSchedule()
|
||||
{
|
||||
if ( !GetOuter()->IsInterruptable() )
|
||||
return false;
|
||||
|
||||
if ( GetOuter()->HasCondition( COND_RECEIVED_ORDERS ) )
|
||||
return false;
|
||||
|
||||
if ( GetEnemy() )
|
||||
{
|
||||
// make sure weapon is out
|
||||
if (!m_bWeaponOut)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_HolsterBehavior::SelectSchedule()
|
||||
{
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_HolsterBehavior )
|
||||
|
||||
DECLARE_TASK( TASK_HOLSTER_WEAPON )
|
||||
DECLARE_TASK( TASK_DRAW_WEAPON )
|
||||
|
||||
// DECLARE_CONDITION( COND_ )
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HOLSTER_WEAPON,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_HOLSTER_WEAPON 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_DRAW_WEAPON,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_DRAW_WEAPON 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|
||||
@@ -0,0 +1,76 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Deal with weapon being out
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#ifndef AI_BEHAVIOR_HOLSTER_H
|
||||
#define AI_BEHAVIOR_HOLSTER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_behavior.h"
|
||||
|
||||
class CAI_HolsterBehavior : public CAI_SimpleBehavior
|
||||
{
|
||||
DECLARE_CLASS( CAI_HolsterBehavior, CAI_SimpleBehavior );
|
||||
|
||||
public:
|
||||
CAI_HolsterBehavior();
|
||||
|
||||
virtual const char *GetName() { return "Holster"; }
|
||||
|
||||
virtual bool CanSelectSchedule();
|
||||
//virtual void BeginScheduleSelection();
|
||||
//virtual void EndScheduleSelection();
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
//void BuildScheduleTestBits();
|
||||
//int TranslateSchedule( int scheduleType );
|
||||
//void OnStartSchedule( int scheduleType );
|
||||
|
||||
//void InitializeBehavior();
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_HOLSTER_WEAPON = BaseClass::NEXT_SCHEDULE, // Try to get out of the player's way
|
||||
SCHED_DRAW_WEAPON,
|
||||
NEXT_SCHEDULE,
|
||||
|
||||
TASK_HOLSTER_WEAPON = BaseClass::NEXT_TASK,
|
||||
TASK_DRAW_WEAPON,
|
||||
NEXT_TASK,
|
||||
|
||||
/*
|
||||
COND_PUT_CONDITIONS_HERE = BaseClass::NEXT_CONDITION,
|
||||
NEXT_CONDITION,
|
||||
*/
|
||||
};
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
|
||||
|
||||
public:
|
||||
|
||||
private:
|
||||
virtual int SelectSchedule();
|
||||
|
||||
bool m_bWeaponOut;
|
||||
|
||||
//---------------------------------
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif // AI_BEHAVIOR_HOLSTER_H
|
||||
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "entitylist.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "ai_behavior_operator.h"
|
||||
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// >OPERATOR BEHAVIOR
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
BEGIN_DATADESC( CAI_OperatorBehavior )
|
||||
DEFINE_FIELD( m_hGoalEntity, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_hPositionEnt, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_hContextTarget, FIELD_EHANDLE ),
|
||||
DEFINE_EMBEDDED( m_WatchSeeEntity ),
|
||||
END_DATADESC();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CAI_OperatorBehavior::CAI_OperatorBehavior()
|
||||
{
|
||||
m_hPositionEnt.Set(NULL);
|
||||
m_hGoalEntity.Set(NULL);
|
||||
m_hContextTarget.Set(NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
#define POSITION_ENT_ALWAYS_SEE_DIST Square(120)
|
||||
bool CAI_OperatorBehavior::CanSeePositionEntity()
|
||||
{
|
||||
CAI_BaseNPC *pOuter = GetOuter();
|
||||
|
||||
Assert( m_hPositionEnt.Get() != NULL );
|
||||
|
||||
// early out here.
|
||||
if( !pOuter->QuerySeeEntity(m_hPositionEnt) )
|
||||
{
|
||||
m_WatchSeeEntity.Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bSpotted = (pOuter->EyePosition().DistToSqr(m_hPositionEnt->GetAbsOrigin()) <= POSITION_ENT_ALWAYS_SEE_DIST);
|
||||
if ( !bSpotted )
|
||||
{
|
||||
bSpotted = ( pOuter->FInViewCone(m_hPositionEnt) && pOuter->FVisible(m_hPositionEnt) );
|
||||
}
|
||||
|
||||
if (bSpotted )
|
||||
{
|
||||
// If we haven't seen it up until now, start a timer. If we have seen it, wait for the
|
||||
// timer to finish. This prevents edge cases where turning on the flashlight makes
|
||||
// NPC spot the position entity a frame before she spots an enemy.
|
||||
if ( !m_WatchSeeEntity.IsRunning() )
|
||||
{
|
||||
m_WatchSeeEntity.Start( 0.3,0.31 );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !m_WatchSeeEntity.Expired() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
m_WatchSeeEntity.Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_OperatorBehavior::IsAtPositionEntity()
|
||||
{
|
||||
Vector myPos = GetAbsOrigin();
|
||||
Vector objectPos = m_hPositionEnt->GetAbsOrigin();
|
||||
|
||||
Vector vecDir = objectPos - myPos;
|
||||
|
||||
return (vecDir.Length2D() <= 12.0f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorBehavior::GatherConditionsNotActive()
|
||||
{
|
||||
if( m_hPositionEnt )
|
||||
{
|
||||
// If we're not currently the active behavior, we have a position ent, and the
|
||||
// NPC can see it, coax the AI out of IDLE/ALERT schedules with this condition.
|
||||
if( CanSeePositionEntity() )
|
||||
{
|
||||
SetCondition( COND_IDLE_INTERRUPT );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorBehavior::GatherConditions( void )
|
||||
{
|
||||
if( GetGoalEntity() )
|
||||
{
|
||||
if( GetGoalEntity()->GetState() == OPERATOR_STATE_FINISHED )
|
||||
{
|
||||
if( IsCurSchedule(SCHED_OPERATOR_OPERATE) )
|
||||
{
|
||||
// Break us out of the operator schedule if the operation completes.
|
||||
SetCondition(COND_PROVOKED);
|
||||
}
|
||||
|
||||
m_hGoalEntity.Set(NULL);
|
||||
m_hPositionEnt.Set(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( CanSeePositionEntity() )
|
||||
{
|
||||
ClearCondition( COND_OPERATOR_LOST_SIGHT_OF_POSITION );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCondition( COND_OPERATOR_LOST_SIGHT_OF_POSITION );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::GatherConditions();
|
||||
|
||||
// Ignore player pushing.
|
||||
ClearCondition( COND_PLAYER_PUSHING );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorBehavior::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_OPERATOR_OPERATE:
|
||||
{
|
||||
// Fire the appropriate output!
|
||||
switch( GetGoalEntity()->GetState() )
|
||||
{
|
||||
case OPERATOR_STATE_NOT_READY:
|
||||
GetGoalEntity()->m_OnMakeReady.FireOutput(NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
case OPERATOR_STATE_READY:
|
||||
GetGoalEntity()->m_OnBeginOperating.FireOutput(NULL, NULL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
//!!!HACKHACK
|
||||
Assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
TaskComplete();
|
||||
break;
|
||||
|
||||
case TASK_OPERATOR_START_PATH:
|
||||
{
|
||||
ChainStartTask(TASK_WALK_PATH);
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_OPERATOR_GET_PATH_TO_POSITION:
|
||||
{
|
||||
CBaseEntity *pGoal = m_hPositionEnt;
|
||||
|
||||
if( !pGoal )
|
||||
{
|
||||
TaskFail("ai_goal_operator has no location entity\n");
|
||||
break;
|
||||
}
|
||||
|
||||
AI_NavGoal_t goal( pGoal->GetAbsOrigin() );
|
||||
goal.pTarget = pGoal;
|
||||
|
||||
if ( GetNavigator()->SetGoal( goal ) == false )
|
||||
{
|
||||
TaskFail( "Can't build path\n" );
|
||||
/*
|
||||
// Try and get as close as possible otherwise
|
||||
AI_NavGoal_t nearGoal( GOALTYPE_LOCATION_NEAREST_NODE, m_hTargetObject->GetAbsOrigin(), AIN_DEF_ACTIVITY, 256 );
|
||||
if ( GetNavigator()->SetGoal( nearGoal, AIN_CLEAR_PREVIOUS_STATE ) )
|
||||
{
|
||||
//FIXME: HACK! The internal pathfinding is setting this without our consent, so override it!
|
||||
ClearCondition( COND_TASK_FAILED );
|
||||
GetNavigator()->SetArrivalDirection( m_hTargetObject->GetAbsAngles() );
|
||||
TaskComplete();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
GetNavigator()->SetArrivalDirection( pGoal->GetAbsAngles() );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorBehavior::RunTask( const Task_t *pTask )
|
||||
{
|
||||
/*
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
default:
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
*/
|
||||
BaseClass::RunTask( pTask );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
CAI_OperatorGoal *CAI_OperatorBehavior::GetGoalEntity()
|
||||
{
|
||||
CAI_OperatorGoal *pGoal = dynamic_cast<CAI_OperatorGoal*>(m_hGoalEntity.Get());
|
||||
|
||||
// NULL is OK.
|
||||
return pGoal;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_OperatorBehavior::IsGoalReady()
|
||||
{
|
||||
if( GetGoalEntity()->GetState() == OPERATOR_STATE_READY )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorBehavior::SetParameters( CAI_OperatorGoal *pGoal, CBaseEntity *pPositionEnt, CBaseEntity *pContextTarget )
|
||||
{
|
||||
m_hGoalEntity.Set( pGoal );
|
||||
m_hPositionEnt.Set( pPositionEnt );
|
||||
m_hContextTarget.Set( pContextTarget );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_OperatorBehavior::CanSelectSchedule()
|
||||
{
|
||||
if ( m_hGoalEntity.Get() == NULL )
|
||||
return false;
|
||||
|
||||
if ( m_hPositionEnt.Get() == NULL )
|
||||
return false;
|
||||
|
||||
if( GetGoalEntity()->GetState() == OPERATOR_STATE_FINISHED )
|
||||
{
|
||||
m_hGoalEntity.Set(NULL);
|
||||
m_hPositionEnt.Set(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !GetOuter()->IsInterruptable() )
|
||||
return false;
|
||||
|
||||
if ( GetOuter()->m_NPCState == NPC_STATE_COMBAT || GetOuter()->m_NPCState == NPC_STATE_SCRIPT )
|
||||
return false;
|
||||
|
||||
// Don't grab NPCs who have been in combat recently
|
||||
if ( GetOuter()->GetLastEnemyTime() && (gpGlobals->curtime - GetOuter()->GetLastEnemyTime()) < 3.0 )
|
||||
return false;
|
||||
|
||||
if( !CanSeePositionEntity() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_OperatorBehavior::SelectSchedule()
|
||||
{
|
||||
if( !IsAtPositionEntity() )
|
||||
{
|
||||
GetGoalEntity()->m_OnBeginApproach.FireOutput( GetOuter(), GetOuter(), 0 );
|
||||
return SCHED_OPERATOR_APPROACH_POSITION;
|
||||
}
|
||||
|
||||
if( GetGoalEntity() && GetGoalEntity()->GetState() != OPERATOR_STATE_FINISHED)
|
||||
{
|
||||
if( GetOuter()->GetActiveWeapon() && !GetOuter()->IsWeaponHolstered() )
|
||||
{
|
||||
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED );
|
||||
return SCHED_OPERATOR_WAIT_FOR_HOLSTER;
|
||||
}
|
||||
|
||||
return SCHED_OPERATOR_OPERATE;
|
||||
}
|
||||
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// >AI_GOAL_OPERATOR
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
LINK_ENTITY_TO_CLASS( ai_goal_operator, CAI_OperatorGoal );
|
||||
|
||||
BEGIN_DATADESC( CAI_OperatorGoal )
|
||||
DEFINE_KEYFIELD( m_iState, FIELD_INTEGER, "state" ),
|
||||
DEFINE_KEYFIELD( m_iMoveTo, FIELD_INTEGER, "moveto" ),
|
||||
DEFINE_KEYFIELD( m_iszContextTarget, FIELD_STRING, "contexttarget" ),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "SetStateReady", InputSetStateReady ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "SetStateFinished", InputSetStateFinished ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
|
||||
|
||||
// Outputs
|
||||
DEFINE_OUTPUT( m_OnBeginApproach, "OnBeginApproach" ),
|
||||
DEFINE_OUTPUT( m_OnMakeReady, "OnMakeReady" ),
|
||||
DEFINE_OUTPUT( m_OnBeginOperating, "OnBeginOperating" ),
|
||||
DEFINE_OUTPUT( m_OnFinished, "OnFinished" ),
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorGoal::EnableGoal( CAI_BaseNPC *pAI )
|
||||
{
|
||||
CAI_OperatorBehavior *pBehavior;
|
||||
|
||||
if ( !pAI->GetBehavior( &pBehavior ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CBaseEntity *pPosition = gEntList.FindEntityByName(NULL, m_target);
|
||||
|
||||
if( !pPosition )
|
||||
{
|
||||
DevMsg("ai_goal_operator called %s with invalid position ent!\n", GetDebugName() );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CBaseEntity *pContextTarget = NULL;
|
||||
|
||||
if( m_iszContextTarget != NULL_STRING )
|
||||
{
|
||||
pContextTarget = gEntList.FindEntityByName( NULL, m_iszContextTarget );
|
||||
}
|
||||
|
||||
pBehavior->SetParameters(this, pPosition, pContextTarget);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorGoal::InputActivate( inputdata_t &inputdata )
|
||||
{
|
||||
BaseClass::InputActivate( inputdata );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorGoal::InputDeactivate( inputdata_t &inputdata )
|
||||
{
|
||||
BaseClass::InputDeactivate( inputdata );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorGoal::InputSetStateReady( inputdata_t &inputdata )
|
||||
{
|
||||
m_iState = OPERATOR_STATE_READY;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_OperatorGoal::InputSetStateFinished( inputdata_t &inputdata )
|
||||
{
|
||||
m_iState = OPERATOR_STATE_FINISHED;
|
||||
m_OnFinished.FireOutput( NULL, NULL, 0 );
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// >SCHEDULES
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_OperatorBehavior )
|
||||
|
||||
DECLARE_TASK( TASK_OPERATOR_GET_PATH_TO_POSITION )
|
||||
DECLARE_TASK( TASK_OPERATOR_START_PATH )
|
||||
DECLARE_TASK( TASK_OPERATOR_OPERATE )
|
||||
|
||||
DECLARE_CONDITION( COND_OPERATOR_LOST_SIGHT_OF_POSITION )
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_OPERATOR_APPROACH_POSITION,
|
||||
" Tasks"
|
||||
" TASK_OPERATOR_GET_PATH_TO_POSITION 0"
|
||||
" TASK_OPERATOR_START_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_STOP_MOVING 0"
|
||||
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_HEAR_DANGER"
|
||||
" COND_OPERATOR_LOST_SIGHT_OF_POSITION"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_OPERATOR_OPERATE,
|
||||
" Tasks"
|
||||
" TASK_WAIT 0.2" // Allow pending entity I/O to settle
|
||||
" TASK_OPERATOR_OPERATE 0"
|
||||
" TASK_WAIT_INDEFINITE 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_PROVOKED"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_OPERATOR_WAIT_FOR_HOLSTER,
|
||||
" Tasks"
|
||||
" TASK_WAIT 1.0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" "
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|
||||
@@ -0,0 +1,144 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Operate consoles/machinery in the world.
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#ifndef AI_BEHAVIOR_OPERATOR_H
|
||||
#define AI_BEHAVIOR_OPERATOR_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_behavior.h"
|
||||
#include "ai_goalentity.h"
|
||||
|
||||
enum
|
||||
{
|
||||
OPERATOR_STATE_NOT_READY = 0,
|
||||
OPERATOR_STATE_READY,
|
||||
OPERATOR_STATE_FINISHED,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
OPERATOR_MOVETO_RESERVED = 0,
|
||||
OPERATOR_MOVETO_WALK,
|
||||
OPERATOR_MOVETO_RUN,
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CAI_OperatorGoal : public CAI_GoalEntity
|
||||
{
|
||||
DECLARE_CLASS( CAI_OperatorGoal, CAI_GoalEntity );
|
||||
public:
|
||||
CAI_OperatorGoal()
|
||||
{
|
||||
}
|
||||
|
||||
void EnableGoal( CAI_BaseNPC *pAI );
|
||||
|
||||
int GetState() { return m_iState; }
|
||||
int GetMoveTo() { return m_iMoveTo; }
|
||||
|
||||
// Inputs
|
||||
virtual void InputActivate( inputdata_t &inputdata );
|
||||
virtual void InputDeactivate( inputdata_t &inputdata );
|
||||
|
||||
void InputSetStateReady( inputdata_t &inputdata );
|
||||
void InputSetStateFinished( inputdata_t &inputdata );
|
||||
|
||||
COutputEvent m_OnBeginApproach;
|
||||
COutputEvent m_OnMakeReady;
|
||||
COutputEvent m_OnBeginOperating;
|
||||
COutputEvent m_OnFinished;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
protected:
|
||||
int m_iState;
|
||||
int m_iMoveTo;
|
||||
string_t m_iszContextTarget;
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CAI_OperatorBehavior : public CAI_SimpleBehavior
|
||||
{
|
||||
DECLARE_CLASS( CAI_OperatorBehavior, CAI_SimpleBehavior );
|
||||
|
||||
public:
|
||||
CAI_OperatorBehavior();
|
||||
|
||||
virtual const char *GetName() { return "Operator"; }
|
||||
|
||||
virtual void SetParameters( CAI_OperatorGoal *pGoal, CBaseEntity *pPositionEnt, CBaseEntity *pContextTarget );
|
||||
|
||||
virtual bool CanSelectSchedule();
|
||||
//virtual void BeginScheduleSelection();
|
||||
//virtual void EndScheduleSelection();
|
||||
|
||||
bool CanSeePositionEntity();
|
||||
bool IsAtPositionEntity();
|
||||
|
||||
void GatherConditionsNotActive();
|
||||
void GatherConditions( void );
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
|
||||
CAI_OperatorGoal *GetGoalEntity();
|
||||
|
||||
bool IsGoalReady();
|
||||
|
||||
//void BuildScheduleTestBits();
|
||||
//int TranslateSchedule( int scheduleType );
|
||||
//void OnStartSchedule( int scheduleType );
|
||||
|
||||
//void InitializeBehavior();
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_OPERATOR_APPROACH_POSITION = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_OPERATOR_MAKE_READY,
|
||||
SCHED_OPERATOR_OPERATE,
|
||||
SCHED_OPERATOR_WAIT_FOR_HOLSTER,
|
||||
NEXT_SCHEDULE,
|
||||
|
||||
TASK_OPERATOR_GET_PATH_TO_POSITION = BaseClass::NEXT_TASK,
|
||||
TASK_OPERATOR_START_PATH,
|
||||
TASK_OPERATOR_OPERATE,
|
||||
NEXT_TASK,
|
||||
|
||||
COND_OPERATOR_LOST_SIGHT_OF_POSITION = BaseClass::NEXT_CONDITION,
|
||||
NEXT_CONDITION,
|
||||
};
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
|
||||
|
||||
public:
|
||||
EHANDLE m_hGoalEntity;
|
||||
EHANDLE m_hPositionEnt;
|
||||
EHANDLE m_hContextTarget;
|
||||
CRandStopwatch m_WatchSeeEntity;
|
||||
|
||||
private:
|
||||
virtual int SelectSchedule();
|
||||
|
||||
//---------------------------------
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif // AI_BEHAVIOR_OPERATOR_H
|
||||
|
||||
|
||||
@@ -0,0 +1,783 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "ai_behavior_police.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "ai_memory.h"
|
||||
#include "collisionutils.h"
|
||||
#include "npc_metropolice.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
BEGIN_DATADESC( CAI_PolicingBehavior )
|
||||
|
||||
DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bStartPolicing, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_hPoliceGoal, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_flNextHarassTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flAggressiveTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_nNumWarnings, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_bTargetIsHostile, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flTargetHostileTime,FIELD_TIME ),
|
||||
|
||||
END_DATADESC();
|
||||
|
||||
CAI_PolicingBehavior::CAI_PolicingBehavior( void )
|
||||
{
|
||||
m_bEnabled = false;
|
||||
m_nNumWarnings = 0;
|
||||
m_bTargetIsHostile = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PolicingBehavior::TargetIsHostile( void )
|
||||
{
|
||||
if ( ( m_flTargetHostileTime < gpGlobals->curtime ) && ( !m_bTargetIsHostile ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pGoal -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::Enable( CAI_PoliceGoal *pGoal )
|
||||
{
|
||||
m_hPoliceGoal = pGoal;
|
||||
m_bEnabled = true;
|
||||
|
||||
m_bStartPolicing = true;
|
||||
|
||||
// Update ourselves immediately
|
||||
GetOuter()->ClearSchedule( "Enable police behavior" );
|
||||
//NotifyChangeBehaviorStatus( GetOuter()->IsInAScript() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::Disable( void )
|
||||
{
|
||||
m_hPoliceGoal = NULL;
|
||||
m_bEnabled = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PolicingBehavior::CanSelectSchedule( void )
|
||||
{
|
||||
// Must be activated and valid
|
||||
if ( IsEnabled() == false || !m_hPoliceGoal || !m_hPoliceGoal->GetTarget() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : false -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::HostSetBatonState( bool state )
|
||||
{
|
||||
// If we're a cop, turn the baton on
|
||||
CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(GetOuter());
|
||||
|
||||
if ( pCop != NULL )
|
||||
{
|
||||
pCop->SetBatonState( state );
|
||||
pCop->SetTarget( m_hPoliceGoal->GetTarget() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : false -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PolicingBehavior::HostBatonIsOn( void )
|
||||
{
|
||||
// If we're a cop, turn the baton on
|
||||
CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(GetOuter());
|
||||
if ( pCop )
|
||||
return pCop->BatonActive();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria )
|
||||
{
|
||||
// If we're a cop, turn the baton on
|
||||
CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(GetOuter());
|
||||
|
||||
if ( pCop != NULL )
|
||||
{
|
||||
CAI_Sentence< CNPC_MetroPolice > *pSentences = pCop->GetSentences();
|
||||
|
||||
pSentences->Speak( pSentence, nSoundPriority, nCriteria );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::BuildScheduleTestBits( void )
|
||||
{
|
||||
if ( IsCurSchedule( SCHED_IDLE_STAND ) || IsCurSchedule( SCHED_ALERT_STAND ) )
|
||||
{
|
||||
if ( m_flNextHarassTime < gpGlobals->curtime )
|
||||
{
|
||||
GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_POLICE_TARGET_TOO_CLOSE_HARASS ) );
|
||||
}
|
||||
|
||||
GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::GatherConditions( void )
|
||||
{
|
||||
BaseClass::GatherConditions();
|
||||
|
||||
// Mapmaker may have removed our goal while we're running our schedule
|
||||
if ( !m_hPoliceGoal )
|
||||
{
|
||||
Disable();
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS );
|
||||
ClearCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
|
||||
|
||||
CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
|
||||
|
||||
if ( pTarget == NULL )
|
||||
{
|
||||
DevMsg( "ai_goal_police with NULL target entity!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we need to knock out our target immediately
|
||||
if ( ShouldKnockOutTarget( pTarget ) )
|
||||
{
|
||||
SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
|
||||
}
|
||||
|
||||
float flDistSqr = ( m_hPoliceGoal->WorldSpaceCenter() - pTarget->WorldSpaceCenter() ).Length2DSqr();
|
||||
float radius = ( m_hPoliceGoal->GetRadius() * PATROL_RADIUS_RATIO );
|
||||
float zDiff = fabs( m_hPoliceGoal->WorldSpaceCenter().z - pTarget->WorldSpaceCenter().z );
|
||||
|
||||
// If we're too far away, don't bother
|
||||
if ( flDistSqr < (radius*radius) && zDiff < 32.0f )
|
||||
{
|
||||
SetCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS );
|
||||
|
||||
if ( flDistSqr < (m_hPoliceGoal->GetRadius()*m_hPoliceGoal->GetRadius()) )
|
||||
{
|
||||
SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
|
||||
}
|
||||
}
|
||||
|
||||
// If we're supposed to stop chasing (aggression over), return
|
||||
if ( m_bTargetIsHostile && m_flAggressiveTime < gpGlobals->curtime && IsCurSchedule(SCHED_CHASE_ENEMY) )
|
||||
{
|
||||
// Force me to re-evaluate my schedule
|
||||
GetOuter()->ClearSchedule( "Stopped chasing, aggression over" );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// We're taking cover from danger
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::AnnouncePolicing( void )
|
||||
{
|
||||
// We're policing
|
||||
static const char *pWarnings[3] =
|
||||
{
|
||||
"METROPOLICE_MOVE_ALONG_A",
|
||||
"METROPOLICE_MOVE_ALONG_B",
|
||||
"METROPOLICE_MOVE_ALONG_C",
|
||||
};
|
||||
|
||||
if ( m_nNumWarnings <= 3 )
|
||||
{
|
||||
HostSpeakSentence( pWarnings[ m_nNumWarnings - 1 ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
|
||||
}
|
||||
else
|
||||
{
|
||||
// We loop at m_nNumWarnings == 4 for players who aren't moving
|
||||
// but still pissing us off, and we're not allowed to do anything about it. (i.e. can't leave post)
|
||||
// First two sentences sound pretty good, so randomly pick one of them.
|
||||
int iSentence = RandomInt( 0, 1 );
|
||||
HostSpeakSentence( pWarnings[ iSentence ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : scheduleType -
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_PolicingBehavior::TranslateSchedule( int scheduleType )
|
||||
{
|
||||
if ( scheduleType == SCHED_CHASE_ENEMY )
|
||||
{
|
||||
if ( m_hPoliceGoal->ShouldRemainAtPost() && !MaintainGoalPosition() )
|
||||
return BaseClass::TranslateSchedule( SCHED_COMBAT_FACE );
|
||||
}
|
||||
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : newActivity -
|
||||
// Output : Activity
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CAI_PolicingBehavior::NPC_TranslateActivity( Activity newActivity )
|
||||
{
|
||||
// See which harassment to play
|
||||
if ( newActivity == ACT_POLICE_HARASS1 )
|
||||
{
|
||||
switch( m_nNumWarnings )
|
||||
{
|
||||
case 1:
|
||||
return (Activity) ACT_POLICE_HARASS1;
|
||||
break;
|
||||
|
||||
default:
|
||||
case 2:
|
||||
return (Activity) ACT_POLICE_HARASS2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::NPC_TranslateActivity( newActivity );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : CBaseEntity
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity *CAI_PolicingBehavior::GetGoalTarget( void )
|
||||
{
|
||||
if ( m_hPoliceGoal == NULL )
|
||||
{
|
||||
//NOTENOTE: This has been called before the behavior is actually active, or the goal has gone invalid
|
||||
Assert(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_hPoliceGoal->GetTarget();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : time -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::SetTargetHostileDuration( float time )
|
||||
{
|
||||
m_flTargetHostileTime = gpGlobals->curtime + time;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch (pTask->iTask)
|
||||
{
|
||||
case TASK_POLICE_GET_PATH_TO_HARASS_GOAL:
|
||||
{
|
||||
Vector harassDir = ( m_hPoliceGoal->GetTarget()->WorldSpaceCenter() - WorldSpaceCenter() );
|
||||
float flDist = VectorNormalize( harassDir );
|
||||
|
||||
// See if we're already close enough
|
||||
if ( flDist < pTask->flTaskData )
|
||||
{
|
||||
TaskComplete();
|
||||
break;
|
||||
}
|
||||
|
||||
float flInter1, flInter2;
|
||||
Vector harassPos = GetAbsOrigin() + ( harassDir * ( flDist - pTask->flTaskData ) );
|
||||
|
||||
// Find a point on our policing radius to stand on
|
||||
if ( IntersectInfiniteRayWithSphere( GetAbsOrigin(), harassDir, m_hPoliceGoal->GetAbsOrigin(), m_hPoliceGoal->GetRadius(), &flInter1, &flInter2 ) )
|
||||
{
|
||||
Vector vPos = m_hPoliceGoal->GetAbsOrigin() + harassDir * ( MAX( flInter1, flInter2 ) );
|
||||
|
||||
// See how far away the default one is
|
||||
float testDist = UTIL_DistApprox2D( m_hPoliceGoal->GetAbsOrigin(), harassPos );
|
||||
|
||||
// If our other goal is closer, choose it
|
||||
if ( testDist > UTIL_DistApprox2D( m_hPoliceGoal->GetAbsOrigin(), vPos ) )
|
||||
{
|
||||
harassPos = vPos;
|
||||
}
|
||||
}
|
||||
|
||||
if ( GetNavigator()->SetGoal( harassPos, pTask->flTaskData ) )
|
||||
{
|
||||
GetNavigator()->SetMovementActivity( (Activity) ACT_WALK_ANGRY );
|
||||
GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetTarget() );
|
||||
TaskComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskFail( FAIL_NO_ROUTE );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_POLICE_GET_PATH_TO_POLICE_GOAL:
|
||||
{
|
||||
if ( GetNavigator()->SetGoal( m_hPoliceGoal->GetAbsOrigin(), pTask->flTaskData ) )
|
||||
{
|
||||
GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetAbsAngles() );
|
||||
TaskComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskFail( FAIL_NO_ROUTE );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_POLICE_ANNOUNCE_HARASS:
|
||||
{
|
||||
AnnouncePolicing();
|
||||
|
||||
// Randomly say this again in the future
|
||||
m_flNextHarassTime = gpGlobals->curtime + random->RandomInt( 4, 6 );
|
||||
|
||||
// Scatter rubber-neckers
|
||||
CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), 256.0f, 2.0f, GetOuter() );
|
||||
}
|
||||
TaskComplete();
|
||||
break;
|
||||
|
||||
case TASK_POLICE_FACE_ALONG_GOAL:
|
||||
{
|
||||
// We may have lost our police goal in the 2 seconds we wait before this task
|
||||
if ( m_hPoliceGoal )
|
||||
{
|
||||
GetMotor()->SetIdealYaw( m_hPoliceGoal->GetAbsAngles().y );
|
||||
GetOuter()->SetTurnActivity();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_POLICE_FACE_ALONG_GOAL:
|
||||
{
|
||||
GetMotor()->UpdateYaw();
|
||||
|
||||
if ( GetOuter()->FacingIdeal() )
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BaseClass::RunTask( pTask);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PolicingBehavior::MaintainGoalPosition( void )
|
||||
{
|
||||
Vector vecOrg = GetAbsOrigin();
|
||||
Vector vecTarget = m_hPoliceGoal->GetAbsOrigin();
|
||||
|
||||
// Allow some slop on Z
|
||||
if ( fabs(vecOrg.z - vecTarget.z) > 64 )
|
||||
return true;
|
||||
|
||||
// Need to be very close on X/Y
|
||||
if ( (vecOrg - vecTarget).Length2D() > 16 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PolicingBehavior::ShouldKnockOutTarget( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( m_hPoliceGoal == NULL )
|
||||
{
|
||||
//NOTENOTE: This has been called before the behavior is actually active, or the goal has gone invalid
|
||||
Assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bVisible = GetOuter()->FVisible( pTarget );
|
||||
return m_hPoliceGoal->ShouldKnockOutTarget( pTarget->WorldSpaceCenter(), bVisible );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTarget -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PolicingBehavior::KnockOutTarget( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( m_hPoliceGoal == NULL )
|
||||
{
|
||||
//NOTENOTE: This has been called before the behavior is actually active, or the goal has gone invalid
|
||||
Assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
m_hPoliceGoal->KnockOutTarget( pTarget );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_PolicingBehavior::SelectSuppressSchedule( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
|
||||
|
||||
m_flAggressiveTime = gpGlobals->curtime + 4.0f;
|
||||
|
||||
if ( m_bTargetIsHostile == false )
|
||||
{
|
||||
// Mark this as a valid target
|
||||
m_bTargetIsHostile = true;
|
||||
|
||||
// Attack the target
|
||||
GetOuter()->SetEnemy( pTarget );
|
||||
GetOuter()->SetState( NPC_STATE_COMBAT );
|
||||
GetOuter()->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin() );
|
||||
|
||||
HostSetBatonState( true );
|
||||
|
||||
// Remember that we're angry with the target
|
||||
m_nNumWarnings = POLICE_MAX_WARNINGS;
|
||||
|
||||
// We need to let the system pickup the new enemy and deal with it on the next frame
|
||||
return SCHED_COMBAT_FACE;
|
||||
}
|
||||
|
||||
// If we're supposed to stand still, then we need to show aggression
|
||||
if ( m_hPoliceGoal->ShouldRemainAtPost() )
|
||||
{
|
||||
// If we're off our mark, fight to it
|
||||
if ( MaintainGoalPosition() )
|
||||
{
|
||||
return SCHED_CHASE_ENEMY;
|
||||
}
|
||||
|
||||
//FIXME: This needs to be a more aggressive warning to the player
|
||||
if ( m_flNextHarassTime < gpGlobals->curtime )
|
||||
{
|
||||
return SCHED_POLICE_WARN_TARGET;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCHED_COMBAT_FACE;
|
||||
}
|
||||
}
|
||||
|
||||
return SCHED_CHASE_ENEMY;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_PolicingBehavior::SelectHarassSchedule( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
|
||||
|
||||
m_flAggressiveTime = gpGlobals->curtime + 4.0f;
|
||||
|
||||
// If we just started to police, make sure we're on our mark
|
||||
if ( MaintainGoalPosition() )
|
||||
return SCHED_POLICE_RETURN_FROM_HARASS;
|
||||
|
||||
// Look at the target if they're too close
|
||||
GetOuter()->AddLookTarget( pTarget, 0.5f, 5.0f );
|
||||
|
||||
// Say something if it's been long enough
|
||||
if ( m_flNextHarassTime < gpGlobals->curtime )
|
||||
{
|
||||
// Gesture the player away
|
||||
GetOuter()->SetTarget( pTarget );
|
||||
|
||||
// Send outputs for each level of warning
|
||||
if ( m_nNumWarnings == 0 )
|
||||
{
|
||||
m_hPoliceGoal->FireWarningLevelOutput( 1 );
|
||||
}
|
||||
else if ( m_nNumWarnings == 1 )
|
||||
{
|
||||
m_hPoliceGoal->FireWarningLevelOutput( 2 );
|
||||
}
|
||||
|
||||
if ( m_nNumWarnings < POLICE_MAX_WARNINGS )
|
||||
{
|
||||
m_nNumWarnings++;
|
||||
}
|
||||
|
||||
// If we're over our limit, just suppress the offender
|
||||
if ( m_nNumWarnings >= POLICE_MAX_WARNINGS )
|
||||
{
|
||||
if ( m_bTargetIsHostile == false )
|
||||
{
|
||||
// Mark the target as a valid target
|
||||
m_bTargetIsHostile = true;
|
||||
|
||||
GetOuter()->SetEnemy( pTarget );
|
||||
GetOuter()->SetState( NPC_STATE_COMBAT );
|
||||
GetOuter()->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin() );
|
||||
HostSetBatonState( true );
|
||||
|
||||
m_hPoliceGoal->FireWarningLevelOutput( 4 );
|
||||
|
||||
return SCHED_COMBAT_FACE;
|
||||
}
|
||||
|
||||
if ( m_hPoliceGoal->ShouldRemainAtPost() == false )
|
||||
return SCHED_CHASE_ENEMY;
|
||||
}
|
||||
|
||||
// On our last warning, approach the target
|
||||
if ( m_nNumWarnings == (POLICE_MAX_WARNINGS-1) )
|
||||
{
|
||||
m_hPoliceGoal->FireWarningLevelOutput( 3 );
|
||||
|
||||
GetOuter()->SetTarget( pTarget );
|
||||
|
||||
HostSetBatonState( true );
|
||||
|
||||
if ( m_hPoliceGoal->ShouldRemainAtPost() == false )
|
||||
return SCHED_POLICE_HARASS_TARGET;
|
||||
}
|
||||
|
||||
// Otherwise just verbally warn him
|
||||
return SCHED_POLICE_WARN_TARGET;
|
||||
}
|
||||
|
||||
return SCHED_NONE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_PolicingBehavior::SelectSchedule( void )
|
||||
{
|
||||
CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
|
||||
|
||||
// Validate our target
|
||||
if ( pTarget == NULL )
|
||||
{
|
||||
DevMsg( "ai_goal_police with NULL target entity!\n" );
|
||||
|
||||
// Turn us off
|
||||
Disable();
|
||||
return SCHED_NONE;
|
||||
}
|
||||
|
||||
// Attack if we're supposed to
|
||||
if ( ( m_flAggressiveTime >= gpGlobals->curtime ) && HasCondition( COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
return SCHED_MELEE_ATTACK1;
|
||||
}
|
||||
|
||||
// See if we should immediately begin to attack our target
|
||||
if ( HasCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ) )
|
||||
{
|
||||
return SelectSuppressSchedule();
|
||||
}
|
||||
|
||||
int newSchedule = SCHED_NONE;
|
||||
|
||||
// See if we're harassing
|
||||
if ( HasCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS ) )
|
||||
{
|
||||
newSchedule = SelectHarassSchedule();
|
||||
}
|
||||
|
||||
// Return that schedule if it was found
|
||||
if ( newSchedule != SCHED_NONE )
|
||||
return newSchedule;
|
||||
|
||||
// If our enemy is set, fogeda'bout it!
|
||||
if ( m_flAggressiveTime < gpGlobals->curtime )
|
||||
{
|
||||
// Return to your initial spot
|
||||
if ( GetEnemy() )
|
||||
{
|
||||
GetOuter()->SetEnemy( NULL );
|
||||
GetOuter()->SetState( NPC_STATE_ALERT );
|
||||
GetOuter()->GetEnemies()->RefreshMemories();
|
||||
}
|
||||
|
||||
HostSetBatonState( false );
|
||||
m_bTargetIsHostile = false;
|
||||
}
|
||||
|
||||
// If we just started to police, make sure we're on our mark
|
||||
if ( MaintainGoalPosition() )
|
||||
return SCHED_POLICE_RETURN_FROM_HARASS;
|
||||
|
||||
// If I've got my baton on, keep looking at the target
|
||||
if ( HostBatonIsOn() )
|
||||
return SCHED_POLICE_TRACK_TARGET;
|
||||
|
||||
// Re-align myself to the goal angles if I've strayed
|
||||
if ( fabs(UTIL_AngleDiff( GetAbsAngles().y, m_hPoliceGoal->GetAbsAngles().y )) > 15 )
|
||||
return SCHED_POLICE_FACE_ALONG_GOAL;
|
||||
|
||||
return SCHED_IDLE_STAND;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_PolicingBehavior::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
|
||||
{
|
||||
if ( failedSchedule == SCHED_CHASE_ENEMY )
|
||||
{
|
||||
// We've failed to chase our enemy, return to where we were came from
|
||||
if ( MaintainGoalPosition() )
|
||||
return SCHED_POLICE_RETURN_FROM_HARASS;
|
||||
|
||||
return SCHED_POLICE_WARN_TARGET;
|
||||
}
|
||||
|
||||
return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_PolicingBehavior )
|
||||
|
||||
DECLARE_CONDITION( COND_POLICE_TARGET_TOO_CLOSE_HARASS );
|
||||
DECLARE_CONDITION( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
|
||||
|
||||
DECLARE_TASK( TASK_POLICE_GET_PATH_TO_HARASS_GOAL );
|
||||
DECLARE_TASK( TASK_POLICE_GET_PATH_TO_POLICE_GOAL );
|
||||
DECLARE_TASK( TASK_POLICE_ANNOUNCE_HARASS );
|
||||
DECLARE_TASK( TASK_POLICE_FACE_ALONG_GOAL );
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_POLICE_WARN_TARGET,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_TARGET 0"
|
||||
" TASK_POLICE_ANNOUNCE_HARASS 0"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_POLICE_HARASS1"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
|
||||
);
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_POLICE_HARASS_TARGET,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_TARGET 0"
|
||||
" TASK_POLICE_GET_PATH_TO_HARASS_GOAL 64"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_POLICE_ANNOUNCE_HARASS 0"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_POLICE_HARASS1"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
|
||||
);
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_POLICE_SUPPRESS_TARGET,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_TARGET 0"
|
||||
" TASK_POLICE_ANNOUNCE_HARASS 0"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_POLICE_HARASS1"
|
||||
""
|
||||
" Interrupts"
|
||||
);
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_POLICE_RETURN_FROM_HARASS,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_POLICE_GET_PATH_TO_POLICE_GOAL 16"
|
||||
" TASK_WALK_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_STOP_MOVING 0"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
|
||||
);
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_POLICE_TRACK_TARGET,
|
||||
|
||||
" Tasks"
|
||||
" TASK_FACE_TARGET 0"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
|
||||
);
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_POLICE_FACE_ALONG_GOAL,
|
||||
|
||||
" Tasks"
|
||||
" TASK_WAIT_RANDOM 2"
|
||||
" TASK_POLICE_FACE_ALONG_GOAL 0"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
|
||||
);
|
||||
|
||||
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|
||||
@@ -0,0 +1,106 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_BEHAVIOR_POLICE_H
|
||||
#define AI_BEHAVIOR_POLICE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_behavior.h"
|
||||
#include "ai_goal_police.h"
|
||||
#include "ai_sentence.h"
|
||||
|
||||
#define PATROL_RADIUS_RATIO 2.0f
|
||||
#define POLICE_MAX_WARNINGS 4
|
||||
|
||||
class CAI_PolicingBehavior : public CAI_SimpleBehavior
|
||||
{
|
||||
DECLARE_CLASS( CAI_PolicingBehavior, CAI_SimpleBehavior );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
CAI_PolicingBehavior();
|
||||
|
||||
enum
|
||||
{
|
||||
// Schedules
|
||||
SCHED_POLICE_RETURN_FROM_HARASS = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_POLICE_WARN_TARGET,
|
||||
SCHED_POLICE_HARASS_TARGET,
|
||||
SCHED_POLICE_SUPPRESS_TARGET,
|
||||
SCHED_POLICE_FACE_ALONG_GOAL,
|
||||
SCHED_POLICE_TRACK_TARGET,
|
||||
NEXT_SCHEDULE,
|
||||
|
||||
// Tasks
|
||||
TASK_POLICE_GET_PATH_TO_HARASS_GOAL = BaseClass::NEXT_TASK,
|
||||
TASK_POLICE_GET_PATH_TO_POLICE_GOAL,
|
||||
TASK_POLICE_FACE_ALONG_GOAL,
|
||||
TASK_POLICE_ANNOUNCE_HARASS,
|
||||
NEXT_TASK,
|
||||
|
||||
// Conditions
|
||||
COND_POLICE_TARGET_TOO_CLOSE_HARASS = BaseClass::NEXT_CONDITION,
|
||||
COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS,
|
||||
NEXT_CONDITION,
|
||||
};
|
||||
|
||||
virtual const char *GetName() { return "Policing"; }
|
||||
|
||||
void Enable( CAI_PoliceGoal *pGoal );
|
||||
void Disable( void );
|
||||
bool CanSelectSchedule( void );
|
||||
void BuildScheduleTestBits( void );
|
||||
|
||||
bool IsEnabled( void ) { return m_bEnabled; }
|
||||
bool TargetIsHostile( void );
|
||||
|
||||
bool ShouldKnockOutTarget( CBaseEntity *pTarget );
|
||||
void KnockOutTarget( CBaseEntity *pTarget );
|
||||
|
||||
int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
|
||||
|
||||
CBaseEntity *GetGoalTarget( void );
|
||||
|
||||
private:
|
||||
|
||||
void HostSpeakSentence( const char *pSentence, SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria );
|
||||
|
||||
int TranslateSchedule( int scheduleType );
|
||||
|
||||
int SelectSchedule( void );
|
||||
int SelectSuppressSchedule( void );
|
||||
int SelectHarassSchedule( void );
|
||||
|
||||
Activity NPC_TranslateActivity( Activity newActivity );
|
||||
void GatherConditions( void );
|
||||
bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval );
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
|
||||
void AnnouncePolicing( void );
|
||||
void HostSetBatonState( bool state );
|
||||
bool HostBatonIsOn( void );
|
||||
|
||||
void SetTargetHostileDuration( float time );
|
||||
bool MaintainGoalPosition( void );
|
||||
|
||||
protected:
|
||||
|
||||
bool m_bEnabled;
|
||||
bool m_bStartPolicing;
|
||||
float m_flNextHarassTime;
|
||||
float m_flAggressiveTime;
|
||||
int m_nNumWarnings;
|
||||
bool m_bTargetIsHostile;
|
||||
float m_flTargetHostileTime;
|
||||
|
||||
CHandle<CAI_PoliceGoal> m_hPoliceGoal;
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
|
||||
};
|
||||
|
||||
#endif // AI_BEHAVIOR_POLICE_H
|
||||
@@ -0,0 +1,170 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_goal_police.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// ai_goal_police
|
||||
|
||||
// Used by police to define a region they should keep a target outside of
|
||||
|
||||
LINK_ENTITY_TO_CLASS( ai_goal_police, CAI_PoliceGoal );
|
||||
|
||||
BEGIN_DATADESC( CAI_PoliceGoal )
|
||||
|
||||
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "PoliceRadius" ),
|
||||
DEFINE_KEYFIELD( m_iszTarget, FIELD_STRING, "PoliceTarget" ),
|
||||
|
||||
DEFINE_FIELD( m_bOverrideKnockOut, FIELD_BOOLEAN ),
|
||||
// m_hTarget
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "EnableKnockOut", InputEnableKnockOut ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "DisableKnockOut", InputDisableKnockOut ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnKnockOut, "OnKnockOut" ),
|
||||
DEFINE_OUTPUT( m_OnFirstWarning, "OnFirstWarning" ),
|
||||
DEFINE_OUTPUT( m_OnSecondWarning, "OnSecondWarning" ),
|
||||
DEFINE_OUTPUT( m_OnLastWarning, "OnLastWarning" ),
|
||||
DEFINE_OUTPUT( m_OnSupressingTarget,"OnSupressingTarget" ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CAI_PoliceGoal::CAI_PoliceGoal( void )
|
||||
{
|
||||
m_hTarget = NULL;
|
||||
m_bOverrideKnockOut = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : float
|
||||
//-----------------------------------------------------------------------------
|
||||
float CAI_PoliceGoal::GetRadius( void )
|
||||
{
|
||||
return m_flRadius;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : CBaseEntity
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity *CAI_PoliceGoal::GetTarget( void )
|
||||
{
|
||||
if ( m_hTarget == NULL )
|
||||
{
|
||||
CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_iszTarget );
|
||||
|
||||
if ( pTarget == NULL )
|
||||
{
|
||||
DevMsg( "Unable to find ai_goal_police target: %s\n", STRING(m_iszTarget) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_hTarget = pTarget;
|
||||
}
|
||||
|
||||
return m_hTarget;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &targetPos -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PoliceGoal::ShouldKnockOutTarget( const Vector &targetPos, bool bTargetVisible )
|
||||
{
|
||||
if ( m_bOverrideKnockOut )
|
||||
return true;
|
||||
|
||||
// Must be flagged to do it
|
||||
if ( HasSpawnFlags( SF_POLICE_GOAL_KNOCKOUT_BEHIND ) == false )
|
||||
return false;
|
||||
|
||||
// If the target's not visible, we don't care about him
|
||||
if ( !bTargetVisible )
|
||||
return false;
|
||||
|
||||
Vector targetDir = targetPos - GetAbsOrigin();
|
||||
VectorNormalize( targetDir );
|
||||
|
||||
Vector facingDir;
|
||||
AngleVectors( GetAbsAngles(), &facingDir );
|
||||
|
||||
// See if it's behind us
|
||||
if ( DotProduct( facingDir, targetDir ) < 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTarget -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PoliceGoal::KnockOutTarget( CBaseEntity *pTarget )
|
||||
{
|
||||
m_OnKnockOut.FireOutput( pTarget, this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_PoliceGoal::ShouldRemainAtPost( void )
|
||||
{
|
||||
return HasSpawnFlags( SF_POLICE_GOAL_DO_NOT_LEAVE_POST );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : level -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PoliceGoal::FireWarningLevelOutput( int level )
|
||||
{
|
||||
switch( level )
|
||||
{
|
||||
case 1:
|
||||
m_OnFirstWarning.FireOutput( this, this );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_OnSecondWarning.FireOutput( this, this );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
m_OnLastWarning.FireOutput( this, this );
|
||||
break;
|
||||
|
||||
default:
|
||||
m_OnSupressingTarget.FireOutput( this, this );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PoliceGoal::InputEnableKnockOut( inputdata_t &data )
|
||||
{
|
||||
m_bOverrideKnockOut = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_PoliceGoal::InputDisableKnockOut( inputdata_t &data )
|
||||
{
|
||||
m_bOverrideKnockOut = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_GOAL_POLICE_H
|
||||
#define AI_GOAL_POLICE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CAI_PoliceGoal : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS( CAI_PoliceGoal, CBaseEntity );
|
||||
|
||||
CAI_PoliceGoal( void );
|
||||
|
||||
float GetRadius( void );
|
||||
CBaseEntity *GetTarget( void );
|
||||
|
||||
bool ShouldKnockOutTarget( const Vector &targetPos, bool bTargetVisible ); // If the target should be knocked out
|
||||
void KnockOutTarget( CBaseEntity *pTarget ); // Send an output that we've knocked out this target
|
||||
bool ShouldRemainAtPost( void );
|
||||
|
||||
void InputEnableKnockOut( inputdata_t &data );
|
||||
void InputDisableKnockOut( inputdata_t &data );
|
||||
|
||||
void FireWarningLevelOutput( int level );
|
||||
|
||||
float m_flRadius;
|
||||
EHANDLE m_hTarget;
|
||||
string_t m_iszTarget;
|
||||
bool m_bOverrideKnockOut;
|
||||
|
||||
COutputEvent m_OnKnockOut;
|
||||
COutputEvent m_OnFirstWarning;
|
||||
COutputEvent m_OnSecondWarning;
|
||||
COutputEvent m_OnLastWarning;
|
||||
COutputEvent m_OnSupressingTarget;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#define SF_POLICE_GOAL_KNOCKOUT_BEHIND (1<<1) // Knockout a target that's behind the plane that cuts perpendicularly through us
|
||||
#define SF_POLICE_GOAL_DO_NOT_LEAVE_POST (1<<2) // Cop will not come off his policing goal, even when angered
|
||||
|
||||
#endif // AI_GOAL_POLICE_H
|
||||
@@ -0,0 +1,80 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
//==================================================
|
||||
// Definition for all AI interactions
|
||||
//==================================================
|
||||
|
||||
#ifndef AI_INTERACTIONS_H
|
||||
#define AI_INTERACTIONS_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//Antlion
|
||||
extern int g_interactionAntlionKilled;
|
||||
|
||||
//Barnacle
|
||||
extern int g_interactionBarnacleVictimDangle;
|
||||
extern int g_interactionBarnacleVictimReleased;
|
||||
extern int g_interactionBarnacleVictimGrab;
|
||||
|
||||
//Bullsquid
|
||||
//extern int g_interactionBullsquidPlay;
|
||||
//extern int g_interactionBullsquidThrow;
|
||||
|
||||
//Combine
|
||||
extern int g_interactionCombineBash;
|
||||
extern int g_interactionCombineRequestCover;
|
||||
|
||||
//Houndeye
|
||||
//extern int g_interactionHoundeyeGroupAttack;
|
||||
//extern int g_interactionHoundeyeGroupRetreat;
|
||||
//extern int g_interactionHoundeyeGroupRalley;
|
||||
|
||||
//Scanner
|
||||
extern int g_interactionScannerInspect;
|
||||
extern int g_interactionScannerInspectBegin;
|
||||
extern int g_interactionScannerInspectDone;
|
||||
extern int g_interactionScannerInspectHandsUp;
|
||||
extern int g_interactionScannerInspectShowArmband;
|
||||
extern int g_interactionScannerSupportEntity;
|
||||
extern int g_interactionScannerSupportPosition;
|
||||
|
||||
//Metrocop
|
||||
extern int g_interactionMetrocopPointed;
|
||||
extern int g_interactionMetrocopStartedStitch;
|
||||
|
||||
//ScriptedTarget
|
||||
extern int g_interactionScriptedTarget;
|
||||
|
||||
//Stalker
|
||||
extern int g_interactionStalkerBurn;
|
||||
|
||||
//Vortigaunt
|
||||
extern int g_interactionVortigauntStomp;
|
||||
extern int g_interactionVortigauntStompFail;
|
||||
extern int g_interactionVortigauntStompHit;
|
||||
extern int g_interactionVortigauntKick;
|
||||
extern int g_interactionVortigauntClaw;
|
||||
|
||||
//Floor turret
|
||||
extern int g_interactionTurretStillStanding;
|
||||
|
||||
// AI Interaction for being hit by a physics object
|
||||
extern int g_interactionHitByPlayerThrownPhysObj;
|
||||
|
||||
// Alerts vital allies when the player punts a large object (car)
|
||||
extern int g_interactionPlayerPuntedHeavyObject;
|
||||
|
||||
// Zombie
|
||||
// Melee attack will land in one second or so.
|
||||
extern int g_interactionZombieMeleeWarning;
|
||||
|
||||
#endif //AI_INTERACTIONS_H
|
||||
@@ -0,0 +1,411 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_spotlight.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "spotlightend.h"
|
||||
#include "beam_shared.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parameters for how the scanner relates to citizens.
|
||||
//-----------------------------------------------------------------------------
|
||||
#define SPOTLIGHT_WIDTH 96
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Save/load
|
||||
//-----------------------------------------------------------------------------
|
||||
BEGIN_SIMPLE_DATADESC( CAI_Spotlight )
|
||||
|
||||
// Robin: Don't save, recreated after restore/transition.
|
||||
//DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ),
|
||||
//DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flSpotlightMaxLength, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flConstraintAngle, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_nHaloSprite, FIELD_MODELINDEX ),
|
||||
DEFINE_FIELD( m_nSpotlightAttachment, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_nFlags, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_vAngularVelocity, FIELD_QUATERNION ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CAI_Spotlight::CAI_Spotlight()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
m_vSpotlightTargetPos.Init();
|
||||
m_vSpotlightDir.Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CAI_Spotlight::~CAI_Spotlight()
|
||||
{
|
||||
SpotlightDestroy();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_Spotlight::Precache(void)
|
||||
{
|
||||
// Sprites
|
||||
m_nHaloSprite = GetOuter()->PrecacheModel("sprites/light_glow03.vmt");
|
||||
|
||||
GetOuter()->PrecacheModel( "sprites/glow_test02.vmt" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_Spotlight::Init( CAI_BaseNPC *pOuter, int nFlags, float flConstraintAngle, float flMaxLength )
|
||||
{
|
||||
SetOuter( pOuter );
|
||||
m_nFlags = nFlags;
|
||||
m_flConstraintAngle = flConstraintAngle;
|
||||
m_flSpotlightMaxLength = flMaxLength;
|
||||
|
||||
// Check for user error
|
||||
if (m_flSpotlightMaxLength <= 0)
|
||||
{
|
||||
DevMsg("ERROR: Invalid spotlight length <= 0, setting to 500\n");
|
||||
m_flSpotlightMaxLength = 500;
|
||||
}
|
||||
|
||||
Precache();
|
||||
|
||||
m_vSpotlightTargetPos = vec3_origin;
|
||||
m_hSpotlight = NULL;
|
||||
m_hSpotlightTarget = NULL;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &m_vSpotlightDir );
|
||||
m_vAngularVelocity.Init( 0, 0, 0, 1 );
|
||||
m_flSpotlightCurLength = m_flSpotlightMaxLength;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Computes the spotlight endpoint
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::ComputeEndpoint( const Vector &vecStartPoint, Vector *pEndPoint )
|
||||
{
|
||||
// Create the endpoint
|
||||
trace_t tr;
|
||||
AI_TraceLine( vecStartPoint, vecStartPoint + m_vSpotlightDir * 2 * m_flSpotlightMaxLength, MASK_OPAQUE, GetOuter(), COLLISION_GROUP_NONE, &tr );
|
||||
*pEndPoint = tr.endpos;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::SpotlightCreate( int nAttachment, const Vector &vecInitialDir )
|
||||
{
|
||||
// Make sure we don't already have one
|
||||
if ( m_hSpotlight != NULL )
|
||||
return;
|
||||
|
||||
m_vSpotlightDir = vecInitialDir;
|
||||
VectorNormalize( m_vSpotlightDir );
|
||||
m_nSpotlightAttachment = nAttachment;
|
||||
|
||||
CreateSpotlightEntities();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create the beam & spotlight end for this spotlight.
|
||||
// Will be re-called after restore/transition
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_Spotlight::CreateSpotlightEntities( void )
|
||||
{
|
||||
m_vAngularVelocity.Init( 0, 0, 0, 1 );
|
||||
|
||||
// Create the endpoint
|
||||
// Get the initial position...
|
||||
Vector vecStartPoint;
|
||||
if ( m_nSpotlightAttachment == 0 )
|
||||
{
|
||||
vecStartPoint = GetOuter()->GetAbsOrigin();
|
||||
}
|
||||
else
|
||||
{
|
||||
GetOuter()->GetAttachment( m_nSpotlightAttachment, vecStartPoint );
|
||||
}
|
||||
|
||||
Vector vecEndPoint;
|
||||
ComputeEndpoint( vecStartPoint, &vecEndPoint );
|
||||
|
||||
m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" );
|
||||
m_hSpotlightTarget->Spawn();
|
||||
m_hSpotlightTarget->SetAbsOrigin( vecEndPoint );
|
||||
m_hSpotlightTarget->SetOwnerEntity( GetOuter() );
|
||||
m_hSpotlightTarget->SetRenderColor( 255, 255, 255 );
|
||||
m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength;
|
||||
if ( FBitSet (m_nFlags, AI_SPOTLIGHT_NO_DLIGHTS) )
|
||||
{
|
||||
m_hSpotlightTarget->m_flLightScale = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hSpotlightTarget->m_flLightScale = SPOTLIGHT_WIDTH;
|
||||
}
|
||||
|
||||
// Create the beam
|
||||
m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", SPOTLIGHT_WIDTH );
|
||||
// Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore)
|
||||
m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY );
|
||||
m_hSpotlight->SetColor( 255, 255, 255 );
|
||||
m_hSpotlight->SetHaloTexture( m_nHaloSprite );
|
||||
m_hSpotlight->SetHaloScale( 32 );
|
||||
m_hSpotlight->SetEndWidth( m_hSpotlight->GetWidth() );
|
||||
m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) );
|
||||
m_hSpotlight->SetBrightness( 32 );
|
||||
m_hSpotlight->SetNoise( 0 );
|
||||
m_hSpotlight->EntsInit( GetOuter(), m_hSpotlightTarget );
|
||||
m_hSpotlight->SetStartAttachment( m_nSpotlightAttachment );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::SpotlightDestroy(void)
|
||||
{
|
||||
if ( m_hSpotlight )
|
||||
{
|
||||
UTIL_Remove(m_hSpotlight);
|
||||
m_hSpotlight = NULL;
|
||||
|
||||
UTIL_Remove(m_hSpotlightTarget);
|
||||
m_hSpotlightTarget = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::SetSpotlightTargetPos( const Vector &vSpotlightTargetPos )
|
||||
{
|
||||
m_vSpotlightTargetPos = vSpotlightTargetPos;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::SetSpotlightTargetDirection( const Vector &vSpotlightTargetDir )
|
||||
{
|
||||
if ( !m_hSpotlight )
|
||||
{
|
||||
CreateSpotlightEntities();
|
||||
}
|
||||
|
||||
VectorMA( m_hSpotlight->GetAbsStartPos(), 1000.0f, vSpotlightTargetDir, m_vSpotlightTargetPos );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constrain to cone
|
||||
//------------------------------------------------------------------------------
|
||||
bool CAI_Spotlight::ConstrainToCone( Vector *pDirection )
|
||||
{
|
||||
Vector vecOrigin, vecForward;
|
||||
if ( m_nSpotlightAttachment == 0 )
|
||||
{
|
||||
QAngle vecAngles;
|
||||
vecAngles = GetOuter()->GetAbsAngles();
|
||||
AngleVectors( vecAngles, &vecForward );
|
||||
}
|
||||
else
|
||||
{
|
||||
GetOuter()->GetAttachment( m_nSpotlightAttachment, vecOrigin, &vecForward );
|
||||
}
|
||||
|
||||
|
||||
if ( m_flConstraintAngle == 0.0f )
|
||||
{
|
||||
*pDirection = vecForward;
|
||||
return true;
|
||||
}
|
||||
|
||||
float flDot = DotProduct( vecForward, *pDirection );
|
||||
if ( flDot >= cos( DEG2RAD( m_flConstraintAngle ) ) )
|
||||
return false;
|
||||
|
||||
Vector vecAxis;
|
||||
CrossProduct( *pDirection, vecForward, vecAxis );
|
||||
VectorNormalize( vecAxis );
|
||||
|
||||
Quaternion q;
|
||||
AxisAngleQuaternion( vecAxis, -m_flConstraintAngle, q );
|
||||
|
||||
Vector vecResult;
|
||||
matrix3x4_t rot;
|
||||
QuaternionMatrix( q, rot );
|
||||
VectorRotate( vecForward, rot, vecResult );
|
||||
VectorNormalize( vecResult );
|
||||
|
||||
*pDirection = vecResult;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
#define QUAT_BLEND_FACTOR 0.4f
|
||||
|
||||
void CAI_Spotlight::UpdateSpotlightDirection( void )
|
||||
{
|
||||
if ( !m_hSpotlight )
|
||||
{
|
||||
CreateSpotlightEntities();
|
||||
}
|
||||
|
||||
// Compute the current beam direction
|
||||
Vector vTargetDir;
|
||||
VectorSubtract( m_vSpotlightTargetPos, m_hSpotlight->GetAbsStartPos(), vTargetDir );
|
||||
VectorNormalize(vTargetDir);
|
||||
ConstrainToCone( &vTargetDir );
|
||||
|
||||
// Compute the amount to rotate
|
||||
float flDot = DotProduct( vTargetDir, m_vSpotlightDir );
|
||||
flDot = clamp( flDot, -1.0f, 1.0f );
|
||||
float flAngle = AngleNormalize( RAD2DEG( acos( flDot ) ) );
|
||||
float flClampedAngle = clamp( flAngle, 0.0f, 45.0f );
|
||||
float flBeamTurnRate = SimpleSplineRemapVal( flClampedAngle, 0.0f, 45.0f, 10.0f, 45.0f );
|
||||
if ( fabs(flAngle) > flBeamTurnRate * gpGlobals->frametime )
|
||||
{
|
||||
flAngle = flBeamTurnRate * gpGlobals->frametime;
|
||||
}
|
||||
|
||||
// Compute the rotation axis
|
||||
Vector vecRotationAxis;
|
||||
CrossProduct( m_vSpotlightDir, vTargetDir, vecRotationAxis );
|
||||
if ( VectorNormalize( vecRotationAxis ) < 1e-3 )
|
||||
{
|
||||
vecRotationAxis.Init( 0, 0, 1 );
|
||||
}
|
||||
|
||||
// Compute the actual rotation amount, using quat slerp blending
|
||||
Quaternion desiredQuat, resultQuat;
|
||||
AxisAngleQuaternion( vecRotationAxis, flAngle, desiredQuat );
|
||||
QuaternionSlerp( m_vAngularVelocity, desiredQuat, QUAT_BLEND_FACTOR, resultQuat );
|
||||
m_vAngularVelocity = resultQuat;
|
||||
|
||||
// If we're really close, and we're not moving very quickly, slam.
|
||||
float flActualRotation = AngleNormalize( RAD2DEG(2 * acos(m_vAngularVelocity.w)) );
|
||||
if (( flActualRotation < 1e-3 ) && (flAngle < 1e-3 ))
|
||||
{
|
||||
m_vSpotlightDir = vTargetDir;
|
||||
m_vAngularVelocity.Init( 0, 0, 0, 1 );
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the desired direction
|
||||
matrix3x4_t rot;
|
||||
Vector vecNewDir;
|
||||
QuaternionMatrix( m_vAngularVelocity, rot );
|
||||
VectorRotate( m_vSpotlightDir, rot, vecNewDir );
|
||||
m_vSpotlightDir = vecNewDir;
|
||||
VectorNormalize(m_vSpotlightDir);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::UpdateSpotlightEndpoint( void )
|
||||
{
|
||||
if ( !m_hSpotlight )
|
||||
{
|
||||
CreateSpotlightEntities();
|
||||
}
|
||||
|
||||
Vector vecStartPoint, vecEndPoint;
|
||||
vecStartPoint = m_hSpotlight->GetAbsStartPos();
|
||||
ComputeEndpoint( vecStartPoint, &vecEndPoint );
|
||||
|
||||
// If I'm not facing the spotlight turn it off
|
||||
Vector vecSpotDir;
|
||||
VectorSubtract( vecEndPoint, vecStartPoint, vecSpotDir );
|
||||
float flBeamLength = VectorNormalize(vecSpotDir);
|
||||
|
||||
m_hSpotlightTarget->SetAbsOrigin( vecEndPoint );
|
||||
m_hSpotlightTarget->SetAbsVelocity( vec3_origin );
|
||||
m_hSpotlightTarget->m_vSpotlightOrg = vecStartPoint;
|
||||
m_hSpotlightTarget->m_vSpotlightDir = vecSpotDir;
|
||||
|
||||
// Avoid sudden change in where beam fades out when cross disconinuities
|
||||
m_flSpotlightCurLength = Lerp( 0.20f, m_flSpotlightCurLength, flBeamLength );
|
||||
|
||||
// Fade out spotlight end if past max length.
|
||||
if (m_flSpotlightCurLength > 2*m_flSpotlightMaxLength)
|
||||
{
|
||||
m_hSpotlightTarget->SetRenderColorA( 0 );
|
||||
m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
|
||||
}
|
||||
else if (m_flSpotlightCurLength > m_flSpotlightMaxLength)
|
||||
{
|
||||
m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) );
|
||||
m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hSpotlightTarget->SetRenderColorA( 1.0 );
|
||||
m_hSpotlight->SetFadeLength(m_flSpotlightCurLength);
|
||||
}
|
||||
|
||||
// Adjust end width to keep beam width constant
|
||||
float flNewWidth = SPOTLIGHT_WIDTH * ( flBeamLength / m_flSpotlightMaxLength );
|
||||
|
||||
flNewWidth = MIN( 100, flNewWidth );
|
||||
|
||||
m_hSpotlight->SetWidth(flNewWidth);
|
||||
m_hSpotlight->SetEndWidth(flNewWidth);
|
||||
|
||||
// Adjust width of light on the end.
|
||||
if ( FBitSet (m_nFlags, AI_SPOTLIGHT_NO_DLIGHTS) )
|
||||
{
|
||||
m_hSpotlightTarget->m_flLightScale = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hSpotlightTarget->m_flLightScale = flNewWidth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose: Update the direction and position of my spotlight (if it's active)
|
||||
//------------------------------------------------------------------------------
|
||||
void CAI_Spotlight::Update(void)
|
||||
{
|
||||
if ( !m_hSpotlight )
|
||||
{
|
||||
CreateSpotlightEntities();
|
||||
}
|
||||
|
||||
// Update the beam direction
|
||||
UpdateSpotlightDirection();
|
||||
UpdateSpotlightEndpoint();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AI_SPOTLIGHT_H
|
||||
#define AI_SPOTLIGHT_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_component.h"
|
||||
|
||||
class CBeam;
|
||||
class CSprite;
|
||||
class SmokeTrail;
|
||||
class CSpotlightEnd;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parameters for how the scanner relates to citizens.
|
||||
//-----------------------------------------------------------------------------
|
||||
enum
|
||||
{
|
||||
AI_SPOTLIGHT_NO_DLIGHTS = 0x1,
|
||||
};
|
||||
|
||||
|
||||
class CAI_Spotlight : public CAI_Component
|
||||
{
|
||||
DECLARE_SIMPLE_DATADESC();
|
||||
DECLARE_CLASS_NOBASE( CAI_Spotlight );
|
||||
|
||||
public:
|
||||
CAI_Spotlight();
|
||||
~CAI_Spotlight();
|
||||
|
||||
void Init( CAI_BaseNPC *pOuter, int nFlags, float flConstraintAngle, float flMaxLength );
|
||||
|
||||
// Create, destroy the spotlight
|
||||
void SpotlightCreate( int nAttachment, const Vector &vecInitialDir );
|
||||
void SpotlightDestroy(void);
|
||||
|
||||
// Controls the spotlight target position + direction
|
||||
void SetSpotlightTargetPos( const Vector &vSpotlightTargetPos );
|
||||
void SetSpotlightTargetDirection( const Vector &vSpotlightTargetDir );
|
||||
|
||||
// Updates the spotlight. Call every frame!
|
||||
void Update(void);
|
||||
|
||||
private:
|
||||
void Precache(void);
|
||||
void CreateSpotlightEntities( void );
|
||||
void UpdateSpotlightDirection( void );
|
||||
void UpdateSpotlightEndpoint( void );
|
||||
|
||||
// Constrain to cone, returns true if it was constrained
|
||||
bool ConstrainToCone( Vector *pDirection );
|
||||
|
||||
// Computes the spotlight endpoint
|
||||
void ComputeEndpoint( const Vector &vecStartPoint, Vector *pEndPoint );
|
||||
|
||||
private:
|
||||
CHandle<CBeam> m_hSpotlight;
|
||||
CHandle<CSpotlightEnd> m_hSpotlightTarget;
|
||||
|
||||
Vector m_vSpotlightTargetPos;
|
||||
Vector m_vSpotlightDir;
|
||||
float m_flSpotlightCurLength;
|
||||
float m_flSpotlightMaxLength;
|
||||
float m_flConstraintAngle;
|
||||
int m_nHaloSprite;
|
||||
int m_nSpotlightAttachment;
|
||||
int m_nFlags;
|
||||
Quaternion m_vAngularVelocity;
|
||||
};
|
||||
|
||||
|
||||
#endif // AI_SPOTLIGHT_H
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "antlion_dust.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CTEAntlionDust, DT_TEAntlionDust )
|
||||
SendPropVector( SENDINFO( m_vecOrigin ) ),
|
||||
SendPropVector( SENDINFO( m_vecAngles ) ),
|
||||
SendPropBool( SENDINFO( m_bBlockedSpawner ) ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
CTEAntlionDust::CTEAntlionDust( const char *name ) : BaseClass( name )
|
||||
{
|
||||
}
|
||||
|
||||
CTEAntlionDust::~CTEAntlionDust( void )
|
||||
{
|
||||
}
|
||||
|
||||
static CTEAntlionDust g_TEAntlionDust( "AntlionDust" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates antlion dust effect
|
||||
// Input : &origin - position
|
||||
// &angles - angles
|
||||
//-----------------------------------------------------------------------------
|
||||
void UTIL_CreateAntlionDust( const Vector &origin, const QAngle &angles, bool bBlockedSpawner )
|
||||
{
|
||||
g_TEAntlionDust.m_vecOrigin = origin;
|
||||
g_TEAntlionDust.m_vecAngles = angles;
|
||||
g_TEAntlionDust.m_bBlockedSpawner = bBlockedSpawner;
|
||||
|
||||
//Send it
|
||||
CPVSFilter filter( origin );
|
||||
g_TEAntlionDust.Create( filter, 0.0f );
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef ANTLION_DUST_H
|
||||
#define ANTLION_DUST_H
|
||||
|
||||
#include "te_particlesystem.h"
|
||||
|
||||
class CTEAntlionDust : public CTEParticleSystem
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS( CTEAntlionDust, CTEParticleSystem );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
CTEAntlionDust( const char *name );
|
||||
virtual ~CTEAntlionDust( void );
|
||||
|
||||
virtual void Test( const Vector& current_origin, const QAngle& current_angles ) { };
|
||||
|
||||
CNetworkVector( m_vecOrigin );
|
||||
CNetworkVar( QAngle, m_vecAngles );
|
||||
CNetworkVar( bool, m_bBlockedSpawner );
|
||||
};
|
||||
|
||||
void UTIL_CreateAntlionDust( const Vector &origin, const QAngle &angles, bool bBlockedSpawner = false );
|
||||
|
||||
#endif //ANTLION_DUST_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,186 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ANTLION_MAKER_H
|
||||
#define ANTLION_MAKER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "npc_antlion.h"
|
||||
#include "monstermaker.h"
|
||||
#include "igamesystem.h"
|
||||
#include "ai_hint.h"
|
||||
|
||||
//
|
||||
// Antlion maker class
|
||||
//
|
||||
|
||||
#define SF_ANTLIONMAKER_RANDOM_SPAWN_NODE 0x00000400
|
||||
#define SF_ANTLIONMAKER_SPAWN_CLOSE_TO_TARGET 0x00000800
|
||||
#define SF_ANTLIONMAKER_RANDOM_FIGHT_TARGET 0x00001000
|
||||
#define SF_ANTLIONMAKER_DO_BLOCKEDEFFECTS 0x00002000
|
||||
|
||||
class CAntlionTemplateMaker : public CTemplateNPCMaker
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS( CAntlionTemplateMaker, CTemplateNPCMaker );
|
||||
|
||||
CAntlionTemplateMaker( void );
|
||||
~CAntlionTemplateMaker( void );
|
||||
|
||||
virtual int DrawDebugTextOverlays( void );
|
||||
virtual void DrawDebugGeometryOverlays( void );
|
||||
|
||||
void MakeNPC( void );
|
||||
void ChildPreSpawn( CAI_BaseNPC *pChild );
|
||||
void ChildPostSpawn( CAI_BaseNPC *pChild );
|
||||
|
||||
void InputSetFightTarget( inputdata_t &inputdata );
|
||||
void InputSetFollowTarget( inputdata_t &inputdata );
|
||||
void InputClearFightTarget( inputdata_t &inputdata );
|
||||
void InputClearFollowTarget( inputdata_t &inputdata );
|
||||
void InputSetSpawnRadius( inputdata_t &inputdata );
|
||||
void InputAddToPool( inputdata_t &inputdata );
|
||||
void InputSetMaxPool( inputdata_t &inputdata );
|
||||
void InputSetPoolRegenAmount( inputdata_t &inputdata );
|
||||
void InputSetPoolRegenTime( inputdata_t &inputdata );
|
||||
void InputChangeDestinationGroup( inputdata_t &inputdata );
|
||||
|
||||
void Activate( void );
|
||||
|
||||
// Do not transition
|
||||
int ObjectCaps( void ) { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
|
||||
|
||||
bool CanMakeNPC( bool bIgnoreSolidEntities = false );
|
||||
bool ShouldAlwaysThink( void ) { return true; }
|
||||
|
||||
void AddChild( CNPC_Antlion *pAnt );
|
||||
void RemoveChild( CNPC_Antlion *pAnt );
|
||||
|
||||
void FixupOrphans( void );
|
||||
void UpdateChildren( void );
|
||||
|
||||
void CreateProxyTarget( const Vector &position );
|
||||
void DestroyProxyTarget( void );
|
||||
|
||||
void SetFightTarget( string_t strTarget, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL );
|
||||
void SetFightTarget( CBaseEntity *pEntity );
|
||||
void SetFightTarget( const Vector &position );
|
||||
|
||||
void SetFollowTarget( string_t strTarget, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL );
|
||||
void SetFollowTarget( CBaseEntity *pEntity );
|
||||
|
||||
void SetChildMoveState( AntlionMoveState_e state );
|
||||
|
||||
void DeathNotice( CBaseEntity *pVictim );
|
||||
bool IsDepleted( void );
|
||||
|
||||
bool ShouldHearBugbait( void ) { return (m_bIgnoreBugbait==false); }
|
||||
|
||||
CBaseEntity *GetFightTarget( void );
|
||||
CBaseEntity *GetFollowTarget( void );
|
||||
|
||||
virtual void Enable( void );
|
||||
virtual void Disable( void );
|
||||
|
||||
|
||||
void BlockedCheckFunc( void );
|
||||
void FindNodesCloseToPlayer( void );
|
||||
void DoBlockedEffects( CBaseEntity *pBlocker, Vector vOrigin );
|
||||
|
||||
CBaseEntity *AllHintsFromClusterBlocked( CAI_Hint *pNode, bool &bChosenHintBlocked );
|
||||
|
||||
void ActivateAllSpores( void );
|
||||
void ActivateSpore( const char* sporename, Vector vOrigin );
|
||||
void DisableSpore( const char* sporename );
|
||||
void DisableAllSpores( void );
|
||||
|
||||
protected:
|
||||
|
||||
void PrecacheTemplateEntity( CBaseEntity *pEntity );
|
||||
|
||||
bool FindHintSpawnPosition( const Vector &origin, float radius, string_t hintGroupName, CAI_Hint **pHint, bool bRandom = false );
|
||||
bool FindNearTargetSpawnPosition( Vector &origin, float radius, CBaseEntity *pTarget );
|
||||
|
||||
//These are used by FindNearTargetSpawnPosition
|
||||
bool FindPositionOnFoot( Vector &origin, float radius, CBaseEntity *pTarget );
|
||||
bool FindPositionOnVehicle( Vector &origin, float radius, CBaseEntity *pTarget );
|
||||
bool ValidateSpawnPosition( Vector &vOrigin, CBaseEntity *pTarget = NULL );
|
||||
|
||||
// Pool behavior for coast
|
||||
void PoolAdd( int iNumToAdd );
|
||||
void PoolRegenThink( void );
|
||||
|
||||
protected:
|
||||
// FIXME: The m_strSpawnGroup is redundant to the m_iszDestinationGroup in the base class NPC template maker
|
||||
string_t m_strSpawnGroup; // if present, spawn children on the nearest node of this group (to the player)
|
||||
string_t m_strSpawnTarget; // name of target to spawn near
|
||||
float m_flSpawnRadius; // radius around target to attempt to spawn in
|
||||
float m_flWorkerSpawnRate; // Percentage chance of spawning a worker when we spawn an antlion [0..1].
|
||||
|
||||
string_t m_strFightTarget; // target entity name that all children will be told to fight to
|
||||
string_t m_strFollowTarget; // entity name that all children will follow
|
||||
|
||||
bool m_bIgnoreBugbait; // Whether or not to ignore bugbait
|
||||
|
||||
AntlionMoveState_e m_nChildMoveState;
|
||||
|
||||
EHANDLE m_hFightTarget; // A normal entity pointer for fight position
|
||||
EHANDLE m_hProxyTarget; // This is a self-held target that is created and used when a vector is passed in as a fight
|
||||
// goal, instead of an entity
|
||||
EHANDLE m_hFollowTarget; // Target to follow
|
||||
|
||||
CUtlVector< CHandle< CNPC_Antlion > > m_Children;
|
||||
|
||||
// Pool behavior for coast
|
||||
int m_iPool;
|
||||
int m_iMaxPool;
|
||||
int m_iPoolRegenAmount;
|
||||
float m_flPoolRegenTime;
|
||||
|
||||
float m_flVehicleSpawnDistance;
|
||||
|
||||
int m_iSkinCount;
|
||||
|
||||
float m_flBlockedBumpTime;
|
||||
|
||||
bool m_bBlocked;
|
||||
COutputEvent m_OnAllBlocked;
|
||||
|
||||
bool m_bCreateSpores;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
// ========================================================
|
||||
// Antlion maker manager
|
||||
// ========================================================
|
||||
|
||||
class CAntlionMakerManager : public CAutoGameSystem
|
||||
{
|
||||
public:
|
||||
CAntlionMakerManager( char const *name ) : CAutoGameSystem( name )
|
||||
{
|
||||
}
|
||||
|
||||
void LevelInitPostEntity( void );
|
||||
|
||||
void BroadcastFightGoal( const Vector &vFightGoal );
|
||||
void BroadcastFightGoal( CBaseEntity *pFightGoal );
|
||||
void BroadcastFollowGoal( CBaseEntity *pFollowGoal );
|
||||
|
||||
protected:
|
||||
|
||||
void GatherMakers( void );
|
||||
|
||||
CUtlVector< CHandle< CAntlionTemplateMaker > > m_Makers;
|
||||
};
|
||||
|
||||
extern CAntlionMakerManager g_AntlionMakerManager;
|
||||
|
||||
#endif // ANTLION_MAKER_H
|
||||
@@ -0,0 +1,112 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ar2_explosion.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define AR2EXPLOSION_ENTITYNAME "ar2explosion"
|
||||
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST(AR2Explosion, DT_AR2Explosion)
|
||||
SendPropString( SENDINFO( m_szMaterialName ) ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS(ar2explosion, AR2Explosion);
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Save/Restore
|
||||
//---------------------------------------------------------
|
||||
BEGIN_DATADESC( AR2Explosion )
|
||||
|
||||
DEFINE_AUTO_ARRAY( m_szMaterialName, FIELD_CHARACTER ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
AR2Explosion* AR2Explosion::CreateAR2Explosion(const Vector &pos)
|
||||
{
|
||||
CBaseEntity *pEnt = CreateEntityByName(AR2EXPLOSION_ENTITYNAME);
|
||||
if(pEnt)
|
||||
{
|
||||
AR2Explosion *pEffect = dynamic_cast<AR2Explosion*>(pEnt);
|
||||
if(pEffect && pEffect->edict())
|
||||
{
|
||||
pEffect->SetLocalOrigin( pos );
|
||||
pEffect->Activate();
|
||||
return pEffect;
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(pEnt);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A lightweight entity for level-designer placed AR2 explosions.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEnvAR2Explosion : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CEnvAR2Explosion, CPointEntity );
|
||||
|
||||
void Spawn( void );
|
||||
|
||||
// Input handlers
|
||||
void InputExplode( inputdata_t &inputdata );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
|
||||
string_t m_iszMaterialName;
|
||||
};
|
||||
|
||||
|
||||
BEGIN_DATADESC( CEnvAR2Explosion )
|
||||
DEFINE_INPUTFUNC(FIELD_VOID, "Explode", InputExplode),
|
||||
DEFINE_KEYFIELD(m_iszMaterialName, FIELD_STRING, "material"),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( env_ar2explosion, CEnvAR2Explosion );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: So you can see where this function begins and the last one ends.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEnvAR2Explosion::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetSolid( SOLID_NONE );
|
||||
AddEffects( EF_NODRAW );
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates the explosion effect.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEnvAR2Explosion::InputExplode( inputdata_t &inputdata )
|
||||
{
|
||||
AR2Explosion *pExplosion = AR2Explosion::CreateAR2Explosion(GetAbsOrigin());
|
||||
if (pExplosion)
|
||||
{
|
||||
pExplosion->SetLifetime( 10 );
|
||||
if (m_iszMaterialName != NULL_STRING)
|
||||
{
|
||||
pExplosion->SetMaterialName(STRING(m_iszMaterialName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef AR2_EXPLOSION_H
|
||||
#define AR2_EXPLOSION_H
|
||||
|
||||
|
||||
#include "baseparticleentity.h"
|
||||
|
||||
|
||||
class AR2Explosion : public CBaseParticleEntity
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
DECLARE_CLASS( AR2Explosion, CBaseParticleEntity );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
static AR2Explosion* CreateAR2Explosion(const Vector &pos);
|
||||
|
||||
inline void SetMaterialName(const char *szMaterialName);
|
||||
|
||||
private:
|
||||
|
||||
CNetworkString( m_szMaterialName, 255 );
|
||||
};
|
||||
|
||||
|
||||
void AR2Explosion::SetMaterialName(const char *szMaterialName)
|
||||
{
|
||||
if (szMaterialName)
|
||||
{
|
||||
Q_strncpy(m_szMaterialName.GetForModify(), szMaterialName, sizeof(m_szMaterialName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "assassin_smoke.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define ASSASSINSMOKE_ENTITYNAME "env_assassinsmoke"
|
||||
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST(CAssassinSmoke, DT_AssassinSmoke)
|
||||
END_SEND_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS(env_assassinsmoke, CAssassinSmoke);
|
||||
|
||||
|
||||
CAssassinSmoke* CAssassinSmoke::CreateAssassinSmoke(const Vector &pos)
|
||||
{
|
||||
CBaseEntity *pEnt = CreateEntityByName(ASSASSINSMOKE_ENTITYNAME);
|
||||
if(pEnt)
|
||||
{
|
||||
CAssassinSmoke *pEffect = dynamic_cast<CAssassinSmoke*>(pEnt);
|
||||
if (pEffect && pEffect->edict())
|
||||
{
|
||||
pEffect->SetLocalOrigin( pos );
|
||||
pEffect->Activate();
|
||||
return pEffect;
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(pEnt);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ASSASSIN_SMOKE_H
|
||||
#define ASSASSIN_SMOKE_H
|
||||
|
||||
|
||||
#include "baseparticleentity.h"
|
||||
|
||||
|
||||
class CAssassinSmoke : public CBaseParticleEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CAssassinSmoke, CBaseParticleEntity );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
static CAssassinSmoke* CreateAssassinSmoke (const Vector &pos);
|
||||
};
|
||||
|
||||
|
||||
#endif//ASSASSIN_SMOKE_H
|
||||
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "basehlcombatweapon.h"
|
||||
#include "soundent.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "game.h"
|
||||
#include "in_buttons.h"
|
||||
#include "gamestats.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CHLMachineGun, DT_HLMachineGun )
|
||||
END_SEND_TABLE()
|
||||
|
||||
//=========================================================
|
||||
// >> CHLSelectFireMachineGun
|
||||
//=========================================================
|
||||
BEGIN_DATADESC( CHLMachineGun )
|
||||
|
||||
DEFINE_FIELD( m_nShotsFired, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flNextSoundTime, FIELD_TIME ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CHLMachineGun::CHLMachineGun( void )
|
||||
{
|
||||
}
|
||||
|
||||
const Vector &CHLMachineGun::GetBulletSpread( void )
|
||||
{
|
||||
static Vector cone = VECTOR_CONE_3DEGREES;
|
||||
return cone;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLMachineGun::PrimaryAttack( void )
|
||||
{
|
||||
// Only the player fires this way so we can cast
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
if (!pPlayer)
|
||||
return;
|
||||
|
||||
// Abort here to handle burst and auto fire modes
|
||||
if ( (UsesClipsForAmmo1() && m_iClip1 == 0) || ( !UsesClipsForAmmo1() && !pPlayer->GetAmmoCount(m_iPrimaryAmmoType) ) )
|
||||
return;
|
||||
|
||||
m_nShotsFired++;
|
||||
|
||||
pPlayer->DoMuzzleFlash();
|
||||
|
||||
// To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems,
|
||||
// especially if the weapon we're firing has a really fast rate of fire.
|
||||
int iBulletsToFire = 0;
|
||||
float fireRate = GetFireRate();
|
||||
|
||||
// MUST call sound before removing a round from the clip of a CHLMachineGun
|
||||
while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
|
||||
{
|
||||
WeaponSound(SINGLE, m_flNextPrimaryAttack);
|
||||
m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
|
||||
iBulletsToFire++;
|
||||
}
|
||||
|
||||
// Make sure we don't fire more than the amount in the clip, if this weapon uses clips
|
||||
if ( UsesClipsForAmmo1() )
|
||||
{
|
||||
if ( iBulletsToFire > m_iClip1 )
|
||||
iBulletsToFire = m_iClip1;
|
||||
m_iClip1 -= iBulletsToFire;
|
||||
}
|
||||
|
||||
m_iPrimaryAttacks++;
|
||||
gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
|
||||
|
||||
// Fire the bullets
|
||||
FireBulletsInfo_t info;
|
||||
info.m_iShots = iBulletsToFire;
|
||||
info.m_vecSrc = pPlayer->Weapon_ShootPosition( );
|
||||
info.m_vecDirShooting = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
|
||||
info.m_vecSpread = pPlayer->GetAttackSpread( this );
|
||||
info.m_flDistance = MAX_TRACE_LENGTH;
|
||||
info.m_iAmmoType = m_iPrimaryAmmoType;
|
||||
info.m_iTracerFreq = 2;
|
||||
FireBullets( info );
|
||||
|
||||
//Factor in the view kick
|
||||
AddViewKick();
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pPlayer );
|
||||
|
||||
if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
|
||||
{
|
||||
// HEV suit - indicate out of ammo condition
|
||||
pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
|
||||
}
|
||||
|
||||
SendWeaponAnim( GetPrimaryAttackActivity() );
|
||||
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
// Register a muzzleflash for the AI
|
||||
pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &info -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLMachineGun::FireBullets( const FireBulletsInfo_t &info )
|
||||
{
|
||||
if(CBasePlayer *pPlayer = ToBasePlayer ( GetOwner() ) )
|
||||
{
|
||||
pPlayer->FireBullets(info);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Weapon firing conditions
|
||||
//-----------------------------------------------------------------------------
|
||||
int CHLMachineGun::WeaponRangeAttack1Condition( float flDot, float flDist )
|
||||
{
|
||||
if ( m_iClip1 <=0 )
|
||||
{
|
||||
return COND_NO_PRIMARY_AMMO;
|
||||
}
|
||||
else if ( flDist < m_fMinRange1 )
|
||||
{
|
||||
return COND_TOO_CLOSE_TO_ATTACK;
|
||||
}
|
||||
else if ( flDist > m_fMaxRange1 )
|
||||
{
|
||||
return COND_TOO_FAR_TO_ATTACK;
|
||||
}
|
||||
else if ( flDot < 0.5f ) // UNDONE: Why check this here? Isn't the AI checking this already?
|
||||
{
|
||||
return COND_NOT_FACING_ATTACK;
|
||||
}
|
||||
|
||||
return COND_CAN_RANGE_ATTACK1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLMachineGun::DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime )
|
||||
{
|
||||
#define KICK_MIN_X 0.2f //Degrees
|
||||
#define KICK_MIN_Y 0.2f //Degrees
|
||||
#define KICK_MIN_Z 0.1f //Degrees
|
||||
|
||||
QAngle vecScratch;
|
||||
|
||||
//Find how far into our accuracy degradation we are
|
||||
float duration = ( fireDurationTime > slideLimitTime ) ? slideLimitTime : fireDurationTime;
|
||||
float kickPerc = duration / slideLimitTime;
|
||||
|
||||
// do this to get a hard discontinuity, clear out anything under 10 degrees punch
|
||||
pPlayer->ViewPunchReset( 10 );
|
||||
|
||||
//Apply this to the view angles as well
|
||||
vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPerc ) );
|
||||
vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPerc ) ) / 3;
|
||||
vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPerc ) / 8;
|
||||
|
||||
//Wibble left and right
|
||||
if ( random->RandomInt( -1, 1 ) >= 0 )
|
||||
vecScratch.y *= -1;
|
||||
|
||||
//Wobble up and down
|
||||
if ( random->RandomInt( -1, 1 ) >= 0 )
|
||||
vecScratch.z *= -1;
|
||||
|
||||
//If we're in easy, dampen the effect a bit
|
||||
if ( g_pGameRules->IsSkillLevel( SKILL_EASY ) )
|
||||
{
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
vecScratch[i] *= dampEasy;
|
||||
}
|
||||
}
|
||||
|
||||
//Clip this to our desired min/max
|
||||
UTIL_ClipPunchAngleOffset( vecScratch, pPlayer->m_Local.m_vecPunchAngle, QAngle( 24.0f, 3.0f, 1.0f ) );
|
||||
|
||||
//Add it to the view punch
|
||||
// NOTE: 0.5 is just tuned to match the old effect before the punch became simulated
|
||||
pPlayer->ViewPunch( vecScratch * 0.5 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reset our shots fired
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHLMachineGun::Deploy( void )
|
||||
{
|
||||
m_nShotsFired = 0;
|
||||
|
||||
return BaseClass::Deploy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Make enough sound events to fill the estimated think interval
|
||||
// returns: number of shots needed
|
||||
//-----------------------------------------------------------------------------
|
||||
int CHLMachineGun::WeaponSoundRealtime( WeaponSound_t shoot_type )
|
||||
{
|
||||
int numBullets = 0;
|
||||
|
||||
// ran out of time, clamp to current
|
||||
if (m_flNextSoundTime < gpGlobals->curtime)
|
||||
{
|
||||
m_flNextSoundTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
// make enough sound events to fill up the next estimated think interval
|
||||
float dt = clamp( m_flAnimTime - m_flPrevAnimTime, 0, 0.2 );
|
||||
if (m_flNextSoundTime < gpGlobals->curtime + dt)
|
||||
{
|
||||
WeaponSound( SINGLE_NPC, m_flNextSoundTime );
|
||||
m_flNextSoundTime += GetFireRate();
|
||||
numBullets++;
|
||||
}
|
||||
if (m_flNextSoundTime < gpGlobals->curtime + dt)
|
||||
{
|
||||
WeaponSound( SINGLE_NPC, m_flNextSoundTime );
|
||||
m_flNextSoundTime += GetFireRate();
|
||||
numBullets++;
|
||||
}
|
||||
|
||||
return numBullets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLMachineGun::ItemPostFrame( void )
|
||||
{
|
||||
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( pOwner == NULL )
|
||||
return;
|
||||
|
||||
// Debounce the recoiling counter
|
||||
if ( ( pOwner->m_nButtons & IN_ATTACK ) == false )
|
||||
{
|
||||
m_nShotsFired = 0;
|
||||
}
|
||||
|
||||
BaseClass::ItemPostFrame();
|
||||
}
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CHLSelectFireMachineGun, DT_HLSelectFireMachineGun )
|
||||
END_SEND_TABLE()
|
||||
|
||||
//=========================================================
|
||||
// >> CHLSelectFireMachineGun
|
||||
//=========================================================
|
||||
BEGIN_DATADESC( CHLSelectFireMachineGun )
|
||||
|
||||
DEFINE_FIELD( m_iBurstSize, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iFireMode, FIELD_INTEGER ),
|
||||
|
||||
// Function pinters
|
||||
DEFINE_FUNCTION( BurstThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
|
||||
float CHLSelectFireMachineGun::GetBurstCycleRate( void )
|
||||
{
|
||||
// this is the time it takes to fire an entire
|
||||
// burst, plus whatever amount of delay we want
|
||||
// to have between bursts.
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
float CHLSelectFireMachineGun::GetFireRate( void )
|
||||
{
|
||||
switch( m_iFireMode )
|
||||
{
|
||||
case FIREMODE_FULLAUTO:
|
||||
// the time between rounds fired on full auto
|
||||
return 0.1f; // 600 rounds per minute = 0.1 seconds per bullet
|
||||
break;
|
||||
|
||||
case FIREMODE_3RNDBURST:
|
||||
// the time between rounds fired within a single burst
|
||||
return 0.1f; // 600 rounds per minute = 0.1 seconds per bullet
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0.1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CHLSelectFireMachineGun::Deploy( void )
|
||||
{
|
||||
// Forget about any bursts this weapon was firing when holstered
|
||||
m_iBurstSize = 0;
|
||||
return BaseClass::Deploy();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLSelectFireMachineGun::PrimaryAttack( void )
|
||||
{
|
||||
if (m_bFireOnEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch( m_iFireMode )
|
||||
{
|
||||
case FIREMODE_FULLAUTO:
|
||||
BaseClass::PrimaryAttack();
|
||||
// Msg("%.3f\n", m_flNextPrimaryAttack.Get() );
|
||||
SetWeaponIdleTime( gpGlobals->curtime + 3.0f );
|
||||
break;
|
||||
|
||||
case FIREMODE_3RNDBURST:
|
||||
m_iBurstSize = GetBurstSize();
|
||||
|
||||
// Call the think function directly so that the first round gets fired immediately.
|
||||
BurstThink();
|
||||
SetThink( &CHLSelectFireMachineGun::BurstThink );
|
||||
m_flNextPrimaryAttack = gpGlobals->curtime + GetBurstCycleRate();
|
||||
m_flNextSecondaryAttack = gpGlobals->curtime + GetBurstCycleRate();
|
||||
|
||||
// Pick up the rest of the burst through the think function.
|
||||
SetNextThink( gpGlobals->curtime + GetFireRate() );
|
||||
break;
|
||||
}
|
||||
|
||||
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
if ( pOwner )
|
||||
{
|
||||
m_iPrimaryAttacks++;
|
||||
gamestats->Event_WeaponFired( pOwner, true, GetClassname() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLSelectFireMachineGun::SecondaryAttack( void )
|
||||
{
|
||||
// change fire modes.
|
||||
|
||||
switch( m_iFireMode )
|
||||
{
|
||||
case FIREMODE_FULLAUTO:
|
||||
//Msg( "Burst\n" );
|
||||
m_iFireMode = FIREMODE_3RNDBURST;
|
||||
WeaponSound(SPECIAL2);
|
||||
break;
|
||||
|
||||
case FIREMODE_3RNDBURST:
|
||||
//Msg( "Auto\n" );
|
||||
m_iFireMode = FIREMODE_FULLAUTO;
|
||||
WeaponSound(SPECIAL1);
|
||||
break;
|
||||
}
|
||||
|
||||
SendWeaponAnim( GetSecondaryAttackActivity() );
|
||||
|
||||
m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
|
||||
|
||||
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
if ( pOwner )
|
||||
{
|
||||
m_iSecondaryAttacks++;
|
||||
gamestats->Event_WeaponFired( pOwner, false, GetClassname() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLSelectFireMachineGun::BurstThink( void )
|
||||
{
|
||||
CHLMachineGun::PrimaryAttack();
|
||||
|
||||
m_iBurstSize--;
|
||||
|
||||
if( m_iBurstSize == 0 )
|
||||
{
|
||||
// The burst is over!
|
||||
SetThink(NULL);
|
||||
|
||||
// idle immediately to stop the firing animation
|
||||
SetWeaponIdleTime( gpGlobals->curtime );
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + GetFireRate() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHLSelectFireMachineGun::WeaponSound( WeaponSound_t shoot_type, float soundtime /*= 0.0f*/ )
|
||||
{
|
||||
if (shoot_type == SINGLE)
|
||||
{
|
||||
switch( m_iFireMode )
|
||||
{
|
||||
case FIREMODE_FULLAUTO:
|
||||
BaseClass::WeaponSound( SINGLE, soundtime );
|
||||
break;
|
||||
|
||||
case FIREMODE_3RNDBURST:
|
||||
if( m_iBurstSize == GetBurstSize() && m_iClip1 >= m_iBurstSize )
|
||||
{
|
||||
// First round of a burst, and enough bullets remaining in the clip to fire the whole burst
|
||||
BaseClass::WeaponSound( BURST, soundtime );
|
||||
}
|
||||
else if( m_iClip1 < m_iBurstSize )
|
||||
{
|
||||
// Not enough rounds remaining in the magazine to fire a burst, so play the gunshot
|
||||
// sounds individually as each round is fired.
|
||||
BaseClass::WeaponSound( SINGLE, soundtime );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::WeaponSound( shoot_type, soundtime );
|
||||
}
|
||||
|
||||
// BUGBUG: These need to be rethought
|
||||
//-----------------------------------------------------------------------------
|
||||
int CHLSelectFireMachineGun::WeaponRangeAttack1Condition( float flDot, float flDist )
|
||||
{
|
||||
if (m_iClip1 <=0)
|
||||
{
|
||||
return COND_NO_PRIMARY_AMMO;
|
||||
}
|
||||
else if ( flDist < m_fMinRange1)
|
||||
{
|
||||
return COND_TOO_CLOSE_TO_ATTACK;
|
||||
}
|
||||
else if (flDist > m_fMaxRange1)
|
||||
{
|
||||
return COND_TOO_FAR_TO_ATTACK;
|
||||
}
|
||||
else if (flDot < 0.5) // UNDONE: Why check this here? Isn't the AI checking this already?
|
||||
{
|
||||
return COND_NOT_FACING_ATTACK;
|
||||
}
|
||||
|
||||
return COND_CAN_RANGE_ATTACK1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int CHLSelectFireMachineGun::WeaponRangeAttack2Condition( float flDot, float flDist )
|
||||
{
|
||||
return COND_NONE; // FIXME: disabled for now
|
||||
|
||||
// m_iClip2 == -1 when no secondary clip is used
|
||||
if ( m_iClip2 == 0 && UsesSecondaryAmmo() )
|
||||
{
|
||||
return COND_NO_SECONDARY_AMMO;
|
||||
}
|
||||
else if ( flDist < m_fMinRange2 )
|
||||
{
|
||||
// Don't return COND_TOO_CLOSE_TO_ATTACK only for primary attack
|
||||
return COND_NONE;
|
||||
}
|
||||
else if (flDist > m_fMaxRange2 )
|
||||
{
|
||||
// Don't return COND_TOO_FAR_TO_ATTACK only for primary attack
|
||||
return COND_NONE;
|
||||
}
|
||||
else if ( flDot < 0.5 ) // UNDONE: Why check this here? Isn't the AI checking this already?
|
||||
{
|
||||
return COND_NOT_FACING_ATTACK;
|
||||
}
|
||||
|
||||
return COND_CAN_RANGE_ATTACK2;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CHLSelectFireMachineGun::CHLSelectFireMachineGun( void )
|
||||
{
|
||||
m_fMinRange1 = 65;
|
||||
m_fMinRange2 = 65;
|
||||
m_fMaxRange1 = 1024;
|
||||
m_fMaxRange2 = 1024;
|
||||
m_iFireMode = FIREMODE_FULLAUTO;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "basehlcombatweapon_shared.h"
|
||||
|
||||
#ifndef BASEHLCOMBATWEAPON_H
|
||||
#define BASEHLCOMBATWEAPON_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//=========================================================
|
||||
// Machine gun base class
|
||||
//=========================================================
|
||||
abstract_class CHLMachineGun : public CBaseHLCombatWeapon
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHLMachineGun, CBaseHLCombatWeapon );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CHLMachineGun();
|
||||
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
void PrimaryAttack( void );
|
||||
|
||||
// Default calls through to m_hOwner, but plasma weapons can override and shoot projectiles here.
|
||||
virtual void ItemPostFrame( void );
|
||||
virtual void FireBullets( const FireBulletsInfo_t &info );
|
||||
virtual float GetFireRate( void ) = 0;
|
||||
virtual int WeaponRangeAttack1Condition( float flDot, float flDist );
|
||||
virtual bool Deploy( void );
|
||||
|
||||
virtual const Vector &GetBulletSpread( void );
|
||||
|
||||
int WeaponSoundRealtime( WeaponSound_t shoot_type );
|
||||
|
||||
// utility function
|
||||
static void DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime );
|
||||
|
||||
protected:
|
||||
|
||||
int m_nShotsFired; // Number of consecutive shots fired
|
||||
|
||||
float m_flNextSoundTime; // real-time clock of when to make next sound
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Machine guns capable of switching between full auto and
|
||||
// burst fire modes.
|
||||
//=========================================================
|
||||
// Mode settings for select fire weapons
|
||||
enum
|
||||
{
|
||||
FIREMODE_FULLAUTO = 1,
|
||||
FIREMODE_SEMI,
|
||||
FIREMODE_3RNDBURST,
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// >> CHLSelectFireMachineGun
|
||||
//=========================================================
|
||||
class CHLSelectFireMachineGun : public CHLMachineGun
|
||||
{
|
||||
DECLARE_CLASS( CHLSelectFireMachineGun, CHLMachineGun );
|
||||
public:
|
||||
|
||||
CHLSelectFireMachineGun( void );
|
||||
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
virtual float GetBurstCycleRate( void );
|
||||
virtual float GetFireRate( void );
|
||||
|
||||
virtual bool Deploy( void );
|
||||
virtual void WeaponSound( WeaponSound_t shoot_type, float soundtime = 0.0f );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual int GetBurstSize( void ) { return 3; };
|
||||
|
||||
void BurstThink( void );
|
||||
|
||||
virtual void PrimaryAttack( void );
|
||||
virtual void SecondaryAttack( void );
|
||||
|
||||
virtual int WeaponRangeAttack1Condition( float flDot, float flDist );
|
||||
virtual int WeaponRangeAttack2Condition( float flDot, float flDist );
|
||||
|
||||
protected:
|
||||
int m_iBurstSize;
|
||||
int m_iFireMode;
|
||||
};
|
||||
#endif // BASEHLCOMBATWEAPON_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,294 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Base class for helicopters & helicopter-type vehicles
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef CBASEHELICOPTER_H
|
||||
#define CBASEHELICOPTER_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "ai_basenpc.h"
|
||||
#include "ai_trackpather.h"
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Helicopter flags
|
||||
//---------------------------------------------------------
|
||||
enum HelicopterFlags_t
|
||||
{
|
||||
BITS_HELICOPTER_GUN_ON = 0x00000001, // Gun is on and aiming
|
||||
BITS_HELICOPTER_MISSILE_ON = 0x00000002, // Missile turrets are on and aiming
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
#define SF_NOWRECKAGE 0x08
|
||||
#define SF_NOROTORWASH 0x20
|
||||
#define SF_AWAITINPUT 0x40
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
// Pathing data
|
||||
#define BASECHOPPER_LEAD_DISTANCE 800.0f
|
||||
#define BASECHOPPER_MIN_CHASE_DIST_DIFF 128.0f // Distance threshold used to determine when a target has moved enough to update our navigation to it
|
||||
#define BASECHOPPER_AVOID_DIST 256.0f
|
||||
|
||||
#define BASECHOPPER_MAX_SPEED 400.0f
|
||||
#define BASECHOPPER_MAX_FIRING_SPEED 250.0f
|
||||
#define BASECHOPPER_MIN_ROCKET_DIST 1000.0f
|
||||
#define BASECHOPPER_MAX_GUN_DIST 2000.0f
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Physics rotor pushing
|
||||
#define BASECHOPPER_WASH_RADIUS 256
|
||||
#define BASECHOPPER_WASH_PUSH_MIN 30 // Initial force * their mass applied to objects in the wash
|
||||
#define BASECHOPPER_WASH_PUSH_MAX 40 // Maximum force * their mass applied to objects in the wash
|
||||
#define BASECHOPPER_WASH_RAMP_TIME 1.0 // Time it takes to ramp from the initial to the max force on an object in the wash (at the center of the wash)
|
||||
#define BASECHOPPER_WASH_MAX_MASS 300 // Don't attempt to push anything over this mass
|
||||
#define BASECHOPPER_WASH_MAX_OBJECTS 6 // Maximum number of objects the wash will push at once
|
||||
|
||||
// Wash physics pushing
|
||||
struct washentity_t
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
|
||||
EHANDLE hEntity;
|
||||
float flWashStartTime;
|
||||
};
|
||||
|
||||
#define BASECHOPPER_WASH_ALTITUDE 1024.0f
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
|
||||
class CBaseHelicopter : public CAI_TrackPather
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CBaseHelicopter, CAI_TrackPather );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
CBaseHelicopter( void );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
virtual void UpdateOnRemove();
|
||||
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void StopLoopingSounds();
|
||||
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
void GibMonster( void );
|
||||
|
||||
Class_T Classify ( void ) { return CLASS_COMBINE; }
|
||||
|
||||
void CallDyingThink( void ) { DyingThink(); }
|
||||
|
||||
bool HasEnemy( void ) { return GetEnemy() != NULL; }
|
||||
virtual void GatherEnemyConditions( CBaseEntity *pEnemy );
|
||||
virtual bool ChooseEnemy( void );
|
||||
virtual void HelicopterPostThink( void ) { };
|
||||
virtual void FlyTouch( CBaseEntity *pOther );
|
||||
virtual void CrashTouch( CBaseEntity *pOther );
|
||||
virtual void HelicopterThink( void );
|
||||
virtual void DyingThink( void );
|
||||
virtual void NullThink( void );
|
||||
virtual void Startup ( void );
|
||||
|
||||
virtual void Flight( void );
|
||||
|
||||
virtual void ShowDamage( void ) {};
|
||||
|
||||
void UpdatePlayerDopplerShift( void );
|
||||
|
||||
virtual void Hunt( void );
|
||||
|
||||
virtual bool IsCrashing( void ) { return m_lifeState != LIFE_ALIVE; }
|
||||
virtual float GetAcceleration( void ) { return 5; }
|
||||
|
||||
virtual void ApplySidewaysDrag( const Vector &vecRight );
|
||||
virtual void ApplyGeneralDrag( void );
|
||||
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
|
||||
virtual bool FireGun( void );
|
||||
|
||||
virtual float GetRotorVolume( void );
|
||||
virtual void InitializeRotorSound( void );
|
||||
virtual void UpdateRotorSoundPitch( int iPitch );
|
||||
|
||||
virtual void AimRocketGun(void) {};
|
||||
virtual void FireRocket( Vector vLaunchPos, Vector vLaunchDir ) {};
|
||||
|
||||
virtual bool GetTrackPatherTarget( Vector *pPos );
|
||||
virtual CBaseEntity *GetTrackPatherTargetEnt();
|
||||
|
||||
void DrawDebugGeometryOverlays(void);
|
||||
|
||||
// Rotor washes
|
||||
virtual void DrawRotorWash( float flAltitude, const Vector &vecRotorOrigin );
|
||||
void DoRotorPhysicsPush( const Vector &vecRotorOrigin, float flAltitude );
|
||||
bool DoWashPush( washentity_t *pWash, const Vector &vecWashOrigin );
|
||||
void StopRotorWash( void );
|
||||
|
||||
// Purpose: Marks the entity for deletion
|
||||
void InputKill( inputdata_t &inputdata );
|
||||
void DelayedKillThink( );
|
||||
|
||||
virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
|
||||
|
||||
// Helicopters never burn
|
||||
virtual void Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner ) { return; }
|
||||
|
||||
|
||||
protected:
|
||||
void HelicopterMove( );
|
||||
|
||||
// Updates the enemy
|
||||
void UpdateEnemy();
|
||||
|
||||
// Override the desired position if your derived helicopter is doing something special
|
||||
virtual void UpdateDesiredPosition( void );
|
||||
|
||||
// Updates the facing direction
|
||||
virtual void UpdateFacingDirection();
|
||||
|
||||
// Fire weapons
|
||||
void FireWeapons();
|
||||
|
||||
// Computes the actual position to fly to
|
||||
void ComputeActualTargetPosition( float flSpeed, float flTime, float flPerpDist, Vector *pDest, bool bApplyNoise = true );
|
||||
|
||||
// Gets the max speed of the helicopter
|
||||
virtual float GetMaxSpeed();
|
||||
virtual float GetMaxSpeedFiring();
|
||||
|
||||
// Updates the enemy
|
||||
virtual float EnemySearchDistance( );
|
||||
|
||||
// Rotor wash think
|
||||
void RotorWashThink( void );
|
||||
|
||||
// Purpose: Push an airboat in our wash
|
||||
void DoWashPushOnAirboat( CBaseEntity *pAirboat, const Vector &vecWashToAirboat, float flWashAmount );
|
||||
|
||||
// Updates the rotor wash volume
|
||||
virtual void UpdateRotorWashVolume();
|
||||
|
||||
// Rotor sound
|
||||
void InputEnableRotorSound( inputdata_t &inputdata );
|
||||
void InputDisableRotorSound( inputdata_t &inputdata );
|
||||
|
||||
protected:
|
||||
CSoundPatch *m_pRotorSound; // Rotor loop played when the player can see the helicopter
|
||||
CSoundPatch *m_pRotorBlast; // Sound played when the helicopter's pushing around physics objects
|
||||
|
||||
float m_flForce;
|
||||
int m_fHelicopterFlags;
|
||||
|
||||
Vector m_vecDesiredFaceDir;
|
||||
|
||||
float m_flLastSeen;
|
||||
float m_flPrevSeen;
|
||||
|
||||
int m_iSoundState; // don't save this
|
||||
|
||||
Vector m_vecTargetPosition;
|
||||
|
||||
float m_flMaxSpeed; // Maximum speed of the helicopter.
|
||||
float m_flMaxSpeedFiring; // Maximum speed of the helicopter whilst firing guns.
|
||||
|
||||
float m_flGoalSpeed; // Goal speed
|
||||
float m_flInitialSpeed;
|
||||
|
||||
float m_flRandomOffsetTime;
|
||||
Vector m_vecRandomOffset;
|
||||
float m_flRotorWashEntitySearchTime;
|
||||
bool m_bSuppressSound;
|
||||
|
||||
EHANDLE m_hRotorWash; // Attached rotorwash entity
|
||||
|
||||
// Inputs
|
||||
void InputActivate( inputdata_t &inputdata );
|
||||
|
||||
// Inputs
|
||||
void InputGunOn( inputdata_t &inputdata );
|
||||
void InputGunOff( inputdata_t &inputdata );
|
||||
void InputMissileOn( inputdata_t &inputdata );
|
||||
void InputMissileOff( inputdata_t &inputdata );
|
||||
void InputEnableRotorWash( inputdata_t &inputdata );
|
||||
void InputDisableRotorWash( inputdata_t &inputdata );
|
||||
void InputMoveTopSpeed( inputdata_t &inputdata ); // Causes the helicopter to immediately accelerate to its desired velocity
|
||||
void InputMoveSpecifiedSpeed( inputdata_t &inputdata );
|
||||
void InputSetAngles( inputdata_t &inputdata ); // Sets the angles of the helicopter
|
||||
|
||||
protected:
|
||||
// Custom conservative collision volumes
|
||||
Vector m_cullBoxMins;
|
||||
Vector m_cullBoxMaxs;
|
||||
|
||||
// Wash physics pushing
|
||||
CUtlVector< washentity_t > m_hEntitiesPushedByWash;
|
||||
|
||||
void SetStartupTime( float time ) { m_flStartupTime = time; }
|
||||
private:
|
||||
CNetworkVar( float, m_flStartupTime );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This entity is used to create little force spheres that the helicopter
|
||||
// should avoid.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAvoidSphere : public CBaseEntity
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
DECLARE_CLASS( CAvoidSphere, CBaseEntity );
|
||||
|
||||
void Init( float flRadius );
|
||||
virtual void Activate();
|
||||
virtual void UpdateOnRemove();
|
||||
|
||||
static void ComputeAvoidanceForces( CBaseEntity *pEntity, float flEntityRadius, float flAvoidTime, Vector *pVecAvoidForce );
|
||||
|
||||
private:
|
||||
typedef CHandle<CAvoidSphere> AvoidSphereHandle_t;
|
||||
|
||||
float m_flRadius;
|
||||
|
||||
static CUtlVector< AvoidSphereHandle_t > s_AvoidSpheres;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This entity is used to create little force boxes that the helicopter
|
||||
// should avoid.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAvoidBox : public CBaseEntity
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
DECLARE_CLASS( CAvoidBox, CBaseEntity );
|
||||
|
||||
virtual void Spawn( );
|
||||
virtual void Activate();
|
||||
virtual void UpdateOnRemove();
|
||||
|
||||
static void ComputeAvoidanceForces( CBaseEntity *pEntity, float flEntityRadius, float flAvoidTime, Vector *pVecAvoidForce );
|
||||
|
||||
private:
|
||||
typedef CHandle<CAvoidBox> AvoidBoxHandle_t;
|
||||
static CUtlVector< AvoidBoxHandle_t > s_AvoidBoxes;
|
||||
};
|
||||
|
||||
|
||||
#endif // CBASEHELICOPTER_H
|
||||
@@ -0,0 +1,106 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Base class for simple projectiles
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "cbasespriteprojectile.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( baseprojectile, CBaseSpriteProjectile );
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Save/Restore
|
||||
//---------------------------------------------------------
|
||||
BEGIN_DATADESC( CBaseSpriteProjectile )
|
||||
|
||||
DEFINE_FIELD( m_iDmg, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iDmgType, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_hIntendedTarget, FIELD_EHANDLE ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
void CBaseSpriteProjectile::Spawn( char *pszModel,
|
||||
const Vector &vecOrigin,
|
||||
const Vector &vecVelocity,
|
||||
edict_t *pOwner,
|
||||
MoveType_t iMovetype,
|
||||
MoveCollide_t nMoveCollide,
|
||||
int iDamage,
|
||||
int iDamageType,
|
||||
CBaseEntity *pIntendedTarget )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetModel( pszModel );
|
||||
|
||||
UTIL_SetSize( this, vec3_origin, vec3_origin );
|
||||
|
||||
m_iDmg = iDamage;
|
||||
m_iDmgType = iDamageType;
|
||||
|
||||
SetMoveType( iMovetype, nMoveCollide );
|
||||
|
||||
UTIL_SetOrigin( this, vecOrigin );
|
||||
SetAbsVelocity( vecVelocity );
|
||||
|
||||
SetOwnerEntity( Instance( pOwner ) );
|
||||
|
||||
m_hIntendedTarget.Set( pIntendedTarget );
|
||||
|
||||
// Call think for free the first time. It's up to derived classes to rethink.
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
void CBaseSpriteProjectile::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
HandleTouch( pOther );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
void CBaseSpriteProjectile::HandleTouch( CBaseEntity *pOther )
|
||||
{
|
||||
CBaseEntity *pOwner;
|
||||
|
||||
pOwner = GetOwnerEntity();
|
||||
|
||||
if( !pOwner )
|
||||
{
|
||||
pOwner = this;
|
||||
}
|
||||
|
||||
trace_t tr;
|
||||
tr = BaseClass::GetTouchTrace( );
|
||||
|
||||
CTakeDamageInfo info( this, pOwner, m_iDmg, m_iDmgType );
|
||||
GuessDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos );
|
||||
pOther->TakeDamage( info );
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
void CBaseSpriteProjectile::Think()
|
||||
{
|
||||
HandleThink();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
void CBaseSpriteProjectile::HandleThink()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Base class for simple projectiles
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef CBASESPRITEPROJECTILE_H
|
||||
#define CBASESPRITEPROJECTILE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "Sprite.h"
|
||||
|
||||
enum MoveType_t;
|
||||
enum MoveCollide_t;
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
class CBaseSpriteProjectile : public CSprite
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_CLASS( CBaseSpriteProjectile, CSprite );
|
||||
|
||||
public:
|
||||
void Touch( CBaseEntity *pOther );
|
||||
virtual void HandleTouch( CBaseEntity *pOther );
|
||||
|
||||
void Think();
|
||||
virtual void HandleThink();
|
||||
|
||||
void Spawn( char *pszModel,
|
||||
const Vector &vecOrigin,
|
||||
const Vector &vecVelocity,
|
||||
edict_t *pOwner,
|
||||
MoveType_t iMovetype,
|
||||
MoveCollide_t nMoveCollide,
|
||||
int iDamage,
|
||||
int iDamageType,
|
||||
CBaseEntity *pIntendedTarget = NULL );
|
||||
|
||||
virtual void Precache( void ) {};
|
||||
|
||||
int m_iDmg;
|
||||
int m_iDmgType;
|
||||
EHANDLE m_hIntendedTarget;
|
||||
};
|
||||
|
||||
#endif // CBASESPRITEPROJECTILE_H
|
||||
@@ -0,0 +1,158 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "citadel_effects_shared.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( env_citadel_energy_core, CCitadelEnergyCore );
|
||||
|
||||
BEGIN_DATADESC( CCitadelEnergyCore )
|
||||
DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "scale" ),
|
||||
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flDuration, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "StartCharge", InputStartCharge ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "StartDischarge", InputStartDischarge ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "Stop", InputStop ),
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CCitadelEnergyCore, DT_CitadelEnergyCore )
|
||||
SendPropFloat( SENDINFO(m_flScale), 0, SPROP_NOSCALE),
|
||||
SendPropInt( SENDINFO(m_nState), 8, SPROP_UNSIGNED),
|
||||
SendPropFloat( SENDINFO(m_flDuration), 0, SPROP_NOSCALE),
|
||||
SendPropFloat( SENDINFO(m_flStartTime), 0, SPROP_NOSCALE),
|
||||
SendPropInt( SENDINFO(m_spawnflags), 0, SPROP_UNSIGNED),
|
||||
END_SEND_TABLE()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Precache:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
PrecacheMaterial( "effects/combinemuzzle2_dark" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
UTIL_SetSize( this, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ) );
|
||||
|
||||
// See if we start active
|
||||
if ( HasSpawnFlags( SF_ENERGYCORE_START_ON ) )
|
||||
{
|
||||
m_nState = (int)ENERGYCORE_STATE_DISCHARGING;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
// No model but we still need to force this!
|
||||
AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : flWarmUpTime -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::StartCharge( float flWarmUpTime )
|
||||
{
|
||||
m_nState = (int)ENERGYCORE_STATE_CHARGING;
|
||||
m_flDuration = flWarmUpTime;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::StartDischarge( void )
|
||||
{
|
||||
m_nState = (int)ENERGYCORE_STATE_DISCHARGING;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : flCoolDownTime -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::StopDischarge( float flCoolDownTime )
|
||||
{
|
||||
m_nState = (int)ENERGYCORE_STATE_OFF;
|
||||
m_flDuration = flCoolDownTime;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::InputStartCharge( inputdata_t &inputdata )
|
||||
{
|
||||
StartCharge( inputdata.value.Float() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::InputStartDischarge( inputdata_t &inputdata )
|
||||
{
|
||||
StartDischarge();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCitadelEnergyCore::InputStop( inputdata_t &inputdata )
|
||||
{
|
||||
StopDischarge( inputdata.value.Float() );
|
||||
}
|
||||
|
||||
CBaseViewModel *IsViewModelMoveParent( CBaseEntity *pEffect )
|
||||
{
|
||||
if ( pEffect->GetMoveParent() )
|
||||
{
|
||||
CBaseViewModel *pViewModel = dynamic_cast<CBaseViewModel *>( pEffect->GetMoveParent() );
|
||||
|
||||
if ( pViewModel )
|
||||
{
|
||||
return pViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int CCitadelEnergyCore::UpdateTransmitState( void )
|
||||
{
|
||||
if ( IsViewModelMoveParent( this ) )
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_FULLCHECK );
|
||||
}
|
||||
|
||||
return BaseClass::UpdateTransmitState();
|
||||
}
|
||||
|
||||
int CCitadelEnergyCore::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
||||
{
|
||||
CBaseViewModel *pViewModel = IsViewModelMoveParent( this );
|
||||
|
||||
if ( pViewModel )
|
||||
{
|
||||
return pViewModel->ShouldTransmit( pInfo );
|
||||
}
|
||||
|
||||
return BaseClass::ShouldTransmit( pInfo );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
#ifndef COMBINE_MINE_H
|
||||
#define COMBINE_MINE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CSoundPatch;
|
||||
|
||||
//---------------------------------------------------------
|
||||
//---------------------------------------------------------
|
||||
#define BOUNCEBOMB_HOOK_RANGE 64
|
||||
#define BOUNCEBOMB_WARN_RADIUS 245.0 // Must be slightly less than physcannon!
|
||||
#define BOUNCEBOMB_DETONATE_RADIUS 100.0
|
||||
|
||||
#define BOUNCEBOMB_EXPLODE_RADIUS 125.0
|
||||
#define BOUNCEBOMB_EXPLODE_DAMAGE 150.0
|
||||
#include "player_pickup.h"
|
||||
|
||||
class CBounceBomb : public CBaseAnimating, public CDefaultPlayerPickupVPhysics
|
||||
{
|
||||
DECLARE_CLASS( CBounceBomb, CBaseAnimating );
|
||||
|
||||
public:
|
||||
CBounceBomb() { m_pWarnSound = NULL; m_bPlacedByPlayer = false; }
|
||||
void Precache();
|
||||
void Spawn();
|
||||
void OnRestore();
|
||||
int DrawDebugTextOverlays(void);
|
||||
void SetMineState( int iState );
|
||||
int GetMineState() { return m_iMineState; }
|
||||
bool IsValidLocation();
|
||||
void Flip( const Vector &vecForce, const AngularImpulse &torque );
|
||||
void SearchThink();
|
||||
void BounceThink();
|
||||
void SettleThink();
|
||||
void CaptiveThink();
|
||||
void ExplodeThink();
|
||||
void ExplodeTouch( CBaseEntity *pOther );
|
||||
void CavernBounceThink(); ///< an alternative style of bouncing used for the citizen modded bouncers
|
||||
bool IsAwake() { return m_bAwake; }
|
||||
void Wake( bool bWake );
|
||||
float FindNearestNPC();
|
||||
void SetNearestNPC( CBaseEntity *pNearest ) { m_hNearestNPC.Set( pNearest ); }
|
||||
int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
bool IsFriend( CBaseEntity *pEntity );
|
||||
|
||||
void UpdateLight( bool bTurnOn, unsigned int r, unsigned int g, unsigned int b, unsigned int a );
|
||||
bool IsLightOn() { return m_hSprite.Get() != NULL; }
|
||||
|
||||
void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason = PICKED_UP_BY_CANNON );
|
||||
void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason );
|
||||
bool ForcePhysgunOpen( CBasePlayer *pPlayer ) { return true; }
|
||||
bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer ) { return true; }
|
||||
virtual QAngle PreferredCarryAngles( void ) { return vec3_angle; }
|
||||
CBasePlayer *HasPhysicsAttacker( float dt );
|
||||
|
||||
bool IsPlayerPlaced() { return m_bPlacedByPlayer; }
|
||||
|
||||
bool CreateVPhysics()
|
||||
{
|
||||
VPhysicsInitNormal( SOLID_VPHYSICS, 0, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
void Pickup();
|
||||
|
||||
void OpenHooks( bool bSilent = false );
|
||||
void CloseHooks();
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
static string_t gm_iszFloorTurretClassname;
|
||||
static string_t gm_iszGroundTurretClassname;
|
||||
|
||||
private:
|
||||
float m_flExplosionDelay;
|
||||
|
||||
bool m_bAwake;
|
||||
bool m_bBounce;
|
||||
EHANDLE m_hNearestNPC;
|
||||
EHANDLE m_hSprite;
|
||||
Color m_LastSpriteColor;
|
||||
|
||||
float m_flHookPositions;
|
||||
int m_iHookN;
|
||||
int m_iHookE;
|
||||
int m_iHookS;
|
||||
int m_iAllHooks;
|
||||
|
||||
CSoundPatch *m_pWarnSound;
|
||||
|
||||
bool m_bLockSilently;
|
||||
bool m_bFoeNearest;
|
||||
|
||||
float m_flIgnoreWorldTime;
|
||||
|
||||
bool m_bDisarmed;
|
||||
|
||||
bool m_bPlacedByPlayer;
|
||||
|
||||
bool m_bHeldByPhysgun;
|
||||
|
||||
int m_iFlipAttempts;
|
||||
int m_iModification;
|
||||
|
||||
CHandle<CBasePlayer> m_hPhysicsAttacker;
|
||||
float m_flLastPhysicsInfluenceTime;
|
||||
|
||||
float m_flTimeGrabbed;
|
||||
IPhysicsConstraint *m_pConstraint;
|
||||
int m_iMineState;
|
||||
|
||||
COutputEvent m_OnPulledUp;
|
||||
void InputDisarm( inputdata_t &inputdata );
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // COMBINE_MINE_H
|
||||
@@ -0,0 +1,37 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ENERGYWAVE_H
|
||||
#define ENERGYWAVE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "basecombatweapon.h"
|
||||
#include "energy_wave.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Shield
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEnergyWave : public CBaseEntity
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
public:
|
||||
DECLARE_CLASS( CEnergyWave, CBaseEntity );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
public:
|
||||
static CEnergyWave* Create( CBaseEntity *pentOwner );
|
||||
};
|
||||
|
||||
|
||||
#endif //ENERGYWAVE_H
|
||||
@@ -0,0 +1,272 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Alyx's EMP effect
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "env_alyxemp_shared.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define EMP_BEAM_SPRITE "effects/laser1.vmt"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( env_alyxemp, CAlyxEmpEffect );
|
||||
|
||||
BEGIN_DATADESC( CAlyxEmpEffect )
|
||||
|
||||
DEFINE_KEYFIELD( m_nType, FIELD_INTEGER, "Type" ),
|
||||
DEFINE_KEYFIELD( m_strTargetName, FIELD_STRING, "EndTargetName" ),
|
||||
|
||||
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flDuration, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
|
||||
|
||||
DEFINE_FIELD( m_iState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_bAutomated, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_THINKFUNC( AutomaticThink ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "StartCharge", InputStartCharge ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "StartDischarge", InputStartDischarge ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "Stop", InputStop ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetTargetEnt", InputSetTargetEnt ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CAlyxEmpEffect, DT_AlyxEmpEffect )
|
||||
SendPropInt( SENDINFO(m_nState), 8, SPROP_UNSIGNED),
|
||||
SendPropFloat( SENDINFO(m_flDuration), 0, SPROP_NOSCALE),
|
||||
SendPropFloat( SENDINFO(m_flStartTime), 0, SPROP_NOSCALE),
|
||||
END_SEND_TABLE()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
// No model but we still need to force this!
|
||||
AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
|
||||
|
||||
// No shadows
|
||||
AddEffects( EF_NOSHADOW | EF_NORECEIVESHADOW );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::Activate( void )
|
||||
{
|
||||
// Start out with a target entity
|
||||
SetTargetEntity( STRING(m_strTargetName) );
|
||||
|
||||
BaseClass::Activate();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *szEntityName -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::SetTargetEntity( const char *szEntityName )
|
||||
{
|
||||
// Find and store off our target entity
|
||||
CBaseEntity *pTargetEnt = NULL;
|
||||
if ( szEntityName && szEntityName[0] )
|
||||
{
|
||||
pTargetEnt = gEntList.FindEntityByName( NULL, szEntityName );
|
||||
|
||||
if ( pTargetEnt == NULL )
|
||||
{
|
||||
Assert(0);
|
||||
DevMsg( "Unable to find env_alyxemp (%s) target %s!\n", GetEntityName().ToCStr(), szEntityName );
|
||||
}
|
||||
}
|
||||
|
||||
SetTargetEntity( pTargetEnt );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Passing NULL is ok!
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::SetTargetEntity( CBaseEntity *pTarget )
|
||||
{
|
||||
m_hTargetEnt.Set( pTarget );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::ActivateAutomatic( CBaseEntity *pAlyx, CBaseEntity *pTarget )
|
||||
{
|
||||
Assert( pAlyx->GetBaseAnimating() != NULL );
|
||||
|
||||
SetParent( pAlyx, pAlyx->GetBaseAnimating()->LookupAttachment("LeftHand") );
|
||||
SetLocalOrigin( vec3_origin );
|
||||
|
||||
m_iState = ALYXEMP_STATE_OFF;
|
||||
SetTargetEntity( pTarget );
|
||||
SetThink( &CAlyxEmpEffect::AutomaticThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
|
||||
m_bAutomated = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::AutomaticThink()
|
||||
{
|
||||
bool bSetNextThink = true;
|
||||
|
||||
switch( m_iState )
|
||||
{
|
||||
case ALYXEMP_STATE_OFF:
|
||||
StartCharge( 0.05f );
|
||||
break;
|
||||
|
||||
case ALYXEMP_STATE_CHARGING:
|
||||
StartDischarge();
|
||||
break;
|
||||
|
||||
case ALYXEMP_STATE_DISCHARGING:
|
||||
Stop( 1.0f );
|
||||
bSetNextThink = false;
|
||||
break;
|
||||
}
|
||||
|
||||
m_iState++;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::Precache( void )
|
||||
{
|
||||
PrecacheModel( EMP_BEAM_SPRITE );
|
||||
|
||||
PrecacheScriptSound( "AlyxEmp.Charge" );
|
||||
PrecacheScriptSound( "AlyxEmp.Discharge" );
|
||||
PrecacheScriptSound( "AlyxEmp.Stop" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::InputStartCharge( inputdata_t &inputdata )
|
||||
{
|
||||
StartCharge( inputdata.value.Float() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::StartCharge( float flDuration )
|
||||
{
|
||||
EmitSound( "AlyxEmp.Charge" );
|
||||
|
||||
m_nState = (int)ALYXEMP_STATE_CHARGING;
|
||||
m_flDuration = flDuration;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
|
||||
if( m_bAutomated )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_flDuration );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::InputStartDischarge( inputdata_t &inputdata )
|
||||
{
|
||||
StartDischarge();
|
||||
}
|
||||
|
||||
void CAlyxEmpEffect::StartDischarge()
|
||||
{
|
||||
EmitSound( "AlyxEmp.Discharge" );
|
||||
|
||||
m_nState = (int)ALYXEMP_STATE_DISCHARGING;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
|
||||
// Beam effects on the target entity!
|
||||
if ( !m_hBeam && m_hTargetEnt )
|
||||
{
|
||||
// Check to store off our view model index
|
||||
m_hBeam = CBeam::BeamCreate( EMP_BEAM_SPRITE, 8 );
|
||||
|
||||
if ( m_hBeam != NULL )
|
||||
{
|
||||
m_hBeam->PointEntInit( m_hTargetEnt->GetAbsOrigin(), this );
|
||||
m_hBeam->SetStartEntity( m_hTargetEnt );
|
||||
m_hBeam->SetWidth( 4 );
|
||||
m_hBeam->SetEndWidth( 8 );
|
||||
m_hBeam->SetBrightness( 255 );
|
||||
m_hBeam->SetColor( 255, 255, 255 );
|
||||
m_hBeam->LiveForTime( 999.0f );
|
||||
m_hBeam->RelinkBeam();
|
||||
m_hBeam->SetNoise( 16 );
|
||||
}
|
||||
|
||||
// End hit
|
||||
Vector shotDir = ( GetAbsOrigin() - m_hTargetEnt->GetAbsOrigin() );
|
||||
VectorNormalize( shotDir );
|
||||
|
||||
CPVSFilter filter( m_hTargetEnt->GetAbsOrigin() );
|
||||
te->GaussExplosion( filter, 0.0f, m_hTargetEnt->GetAbsOrigin() - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 );
|
||||
}
|
||||
|
||||
if( m_bAutomated )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.5f );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::InputStop( inputdata_t &inputdata )
|
||||
{
|
||||
float flDuration = inputdata.value.Float();
|
||||
|
||||
Stop( flDuration );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::Stop( float flDuration )
|
||||
{
|
||||
EmitSound( "AlyxEmp.Stop" );
|
||||
|
||||
m_nState = (int)ALYXEMP_STATE_OFF;
|
||||
m_flDuration = flDuration;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
|
||||
if ( m_hBeam != NULL )
|
||||
{
|
||||
UTIL_Remove( m_hBeam );
|
||||
m_hBeam = NULL;
|
||||
}
|
||||
|
||||
if( m_bAutomated )
|
||||
{
|
||||
SetThink( &CAlyxEmpEffect::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + flDuration + 1.0f );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAlyxEmpEffect::InputSetTargetEnt( inputdata_t &inputdata )
|
||||
{
|
||||
SetTargetEntity( inputdata.value.String() );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "env_speaker.h"
|
||||
#include "ai_speech.h"
|
||||
#include "stringregistry.h"
|
||||
#include "gamerules.h"
|
||||
#include "game.h"
|
||||
#include <ctype.h>
|
||||
#include "entitylist.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "soundscape.h"
|
||||
#include "AI_ResponseSystem.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define SF_SPEAKER_START_SILENT 1
|
||||
#define SF_SPEAKER_EVERYWHERE 2
|
||||
|
||||
extern ISaveRestoreOps *responseSystemSaveRestoreOps;
|
||||
#include "saverestore.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( env_speaker, CSpeaker );
|
||||
|
||||
BEGIN_DATADESC( CSpeaker )
|
||||
|
||||
DEFINE_KEYFIELD( m_delayMin, FIELD_FLOAT, "delaymin" ),
|
||||
DEFINE_KEYFIELD( m_delayMax, FIELD_FLOAT, "delaymax" ),
|
||||
DEFINE_KEYFIELD( m_iszRuleScriptFile, FIELD_STRING, "rulescript" ),
|
||||
DEFINE_KEYFIELD( m_iszConcept, FIELD_STRING, "concept" ),
|
||||
|
||||
// Needs to be set up in the Activate methods of derived classes
|
||||
//DEFINE_CUSTOM_FIELD( m_pInstancedResponseSystem, responseSystemSaveRestoreOps ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( SpeakerThink ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
void CSpeaker::Spawn( void )
|
||||
{
|
||||
const char *soundfile = (const char *)STRING( m_iszRuleScriptFile );
|
||||
|
||||
if ( Q_strlen( soundfile ) < 1 )
|
||||
{
|
||||
Warning( "'speaker' entity with no Level/Sentence! at: %f, %f, %f\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
SetThink( &CSpeaker::SUB_Remove );
|
||||
return;
|
||||
}
|
||||
|
||||
// const char *concept = (const char *)STRING( m_iszConcept );
|
||||
// if ( Q_strlen( concept ) < 1 )
|
||||
// {
|
||||
// Warning( "'speaker' entity using rule set %s with empty concept string\n", soundfile );
|
||||
// }
|
||||
|
||||
SetSolid( SOLID_NONE );
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
|
||||
SetThink(&CSpeaker::SpeakerThink);
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
|
||||
// allow on/off switching via 'use' function.
|
||||
|
||||
Precache( );
|
||||
}
|
||||
|
||||
|
||||
void CSpeaker::Precache( void )
|
||||
{
|
||||
if ( !FBitSet (m_spawnflags, SF_SPEAKER_START_SILENT ) )
|
||||
{
|
||||
// set first announcement time for random n second
|
||||
SetNextThink( gpGlobals->curtime + random->RandomFloat(5.0, 15.0) );
|
||||
}
|
||||
|
||||
if ( !m_pInstancedResponseSystem && Q_strlen( STRING(m_iszRuleScriptFile) ) > 0 )
|
||||
{
|
||||
m_pInstancedResponseSystem = PrecacheCustomResponseSystem( STRING( m_iszRuleScriptFile ) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Need a custom save restore so we can restore the instanced response system by name
|
||||
// after we've loaded the filename from disk...
|
||||
// Input : &save -
|
||||
//-----------------------------------------------------------------------------
|
||||
int CSpeaker::Save( ISave &save )
|
||||
{
|
||||
int iret = BaseClass::Save( save );
|
||||
if ( iret )
|
||||
{
|
||||
bool doSave = ( m_pInstancedResponseSystem && ( m_iszRuleScriptFile != NULL_STRING ) ) ? true : false;
|
||||
save.WriteBool( &doSave );
|
||||
if ( doSave )
|
||||
{
|
||||
save.StartBlock( "InstancedResponseSystem" );
|
||||
{
|
||||
SaveRestoreFieldInfo_t fieldInfo = { &m_pInstancedResponseSystem, 0, NULL };
|
||||
responseSystemSaveRestoreOps->Save( fieldInfo, &save );
|
||||
}
|
||||
save.EndBlock();
|
||||
}
|
||||
}
|
||||
return iret;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &restore -
|
||||
//-----------------------------------------------------------------------------
|
||||
int CSpeaker::Restore( IRestore &restore )
|
||||
{
|
||||
int iret = BaseClass::Restore( restore );
|
||||
if ( iret )
|
||||
{
|
||||
bool doRead = false;
|
||||
restore.ReadBool( &doRead );
|
||||
if ( doRead )
|
||||
{
|
||||
char szResponseSystemBlockName[SIZE_BLOCK_NAME_BUF];
|
||||
restore.StartBlock( szResponseSystemBlockName );
|
||||
if ( !Q_stricmp( szResponseSystemBlockName, "InstancedResponseSystem" ) )
|
||||
{
|
||||
if ( !m_pInstancedResponseSystem && Q_strlen( STRING(m_iszRuleScriptFile) ) > 0 )
|
||||
{
|
||||
m_pInstancedResponseSystem = PrecacheCustomResponseSystem( STRING( m_iszRuleScriptFile ) );
|
||||
if ( m_pInstancedResponseSystem )
|
||||
{
|
||||
SaveRestoreFieldInfo_t fieldInfo =
|
||||
{
|
||||
&m_pInstancedResponseSystem,
|
||||
0,
|
||||
NULL
|
||||
};
|
||||
responseSystemSaveRestoreOps->Restore( fieldInfo, &restore );
|
||||
}
|
||||
}
|
||||
}
|
||||
restore.EndBlock();
|
||||
}
|
||||
}
|
||||
return iret;
|
||||
}
|
||||
|
||||
void CSpeaker::SpeakerThink( void )
|
||||
{
|
||||
// Wait for the talking characters to finish first.
|
||||
if ( !g_AIFriendliesTalkSemaphore.IsAvailable( this ) || !g_AIFoesTalkSemaphore.IsAvailable( this ) )
|
||||
{
|
||||
float releaseTime = MAX( g_AIFriendliesTalkSemaphore.GetReleaseTime(), g_AIFoesTalkSemaphore.GetReleaseTime() );
|
||||
// Add some slop (only up to one second)
|
||||
releaseTime += random->RandomFloat( 0, 1 );
|
||||
SetNextThink( releaseTime );
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchResponse( m_iszConcept.ToCStr() );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + random->RandomFloat(m_delayMin, m_delayMax) );
|
||||
|
||||
// time delay until it's ok to speak: used so that two NPCs don't talk at once
|
||||
g_AIFriendliesTalkSemaphore.Acquire( 5, this );
|
||||
g_AIFoesTalkSemaphore.Acquire( 5, this );
|
||||
}
|
||||
|
||||
|
||||
void CSpeaker::InputTurnOn( inputdata_t &inputdata )
|
||||
{
|
||||
// turn on announcements
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
|
||||
|
||||
void CSpeaker::InputTurnOff( inputdata_t &inputdata )
|
||||
{
|
||||
// turn off announcements
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// If an announcement is pending, cancel it. If no announcement is pending, start one.
|
||||
//
|
||||
void CSpeaker::InputToggle( inputdata_t &inputdata )
|
||||
{
|
||||
int fActive = (GetNextThink() > 0.0 );
|
||||
|
||||
// fActive is true only if an announcement is pending
|
||||
if ( fActive )
|
||||
{
|
||||
// turn off announcements
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
}
|
||||
else
|
||||
{
|
||||
// turn on announcements
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ENV_SPEAKER_H
|
||||
#define ENV_SPEAKER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// ===================================================================================
|
||||
//
|
||||
// Speaker class. Used for announcements per level, for door lock/unlock spoken voice.
|
||||
//
|
||||
|
||||
class CSpeaker : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CSpeaker, CPointEntity );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
|
||||
|
||||
virtual IResponseSystem *GetResponseSystem() { return m_pInstancedResponseSystem; }
|
||||
|
||||
virtual int Save( ISave &save );
|
||||
virtual int Restore( IRestore &restore );
|
||||
|
||||
protected:
|
||||
|
||||
void SpeakerThink( void );
|
||||
|
||||
void InputToggle( inputdata_t &inputdata );
|
||||
|
||||
float m_delayMin;
|
||||
float m_delayMax;
|
||||
|
||||
string_t m_iszRuleScriptFile;
|
||||
string_t m_iszConcept;
|
||||
IResponseSystem *m_pInstancedResponseSystem;
|
||||
|
||||
public:
|
||||
|
||||
void InputTurnOff( inputdata_t &inputdata );
|
||||
void InputTurnOn( inputdata_t &inputdata );
|
||||
};
|
||||
|
||||
#endif // ENV_SPEAKER_H
|
||||
@@ -0,0 +1,107 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "baseparticleentity.h"
|
||||
#include "sendproxy.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEnvStarfield : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CEnvStarfield, CBaseEntity );
|
||||
public:
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void Precache();
|
||||
virtual void Spawn( void );
|
||||
virtual int UpdateTransmitState(void);
|
||||
|
||||
// Inputs
|
||||
void InputTurnOn( inputdata_t &inputdata );
|
||||
void InputTurnOff( inputdata_t &inputdata );
|
||||
void InputSetDensity( inputdata_t &inputdata );
|
||||
|
||||
private:
|
||||
CNetworkVar( bool, m_bOn );
|
||||
CNetworkVar( float, m_flDensity );
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CEnvStarfield )
|
||||
DEFINE_FIELD( m_bOn, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flDensity, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDensity", InputSetDensity ),
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CEnvStarfield, DT_EnvStarfield )
|
||||
SendPropInt( SENDINFO(m_bOn), 1, SPROP_UNSIGNED ),
|
||||
SendPropFloat( SENDINFO(m_flDensity), 0, SPROP_NOSCALE),
|
||||
END_SEND_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( env_starfield, CEnvStarfield );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEnvStarfield::Spawn()
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
m_flDensity = 1.0;
|
||||
m_bOn = false;
|
||||
|
||||
Precache();
|
||||
}
|
||||
|
||||
void CEnvStarfield::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheMaterial( "effects/spark_noz" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CEnvStarfield::UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEnvStarfield::InputTurnOn( inputdata_t &inputdata )
|
||||
{
|
||||
m_bOn = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEnvStarfield::InputTurnOff( inputdata_t &inputdata )
|
||||
{
|
||||
m_bOn = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEnvStarfield::InputSetDensity( inputdata_t &inputdata )
|
||||
{
|
||||
m_flDensity = inputdata.value.Float();
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "extinguisherjet.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "fire.h"
|
||||
#include "ndebugoverlay.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern ConVar fire_extinguisher_debug;
|
||||
|
||||
//Networking
|
||||
IMPLEMENT_SERVERCLASS_ST( CExtinguisherJet, DT_ExtinguisherJet )
|
||||
SendPropInt(SENDINFO(m_bEmit), 1, SPROP_UNSIGNED),
|
||||
SendPropInt(SENDINFO(m_bUseMuzzlePoint), 1, SPROP_UNSIGNED),
|
||||
SendPropInt(SENDINFO(m_nLength), 32, SPROP_UNSIGNED),
|
||||
SendPropInt(SENDINFO(m_nSize), 32, SPROP_UNSIGNED),
|
||||
END_SEND_TABLE()
|
||||
|
||||
//Save/restore
|
||||
BEGIN_DATADESC( CExtinguisherJet )
|
||||
|
||||
//Regular fields
|
||||
DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ),
|
||||
DEFINE_KEYFIELD( m_nLength, FIELD_INTEGER, "length" ),
|
||||
DEFINE_KEYFIELD( m_nSize, FIELD_INTEGER, "size" ),
|
||||
DEFINE_KEYFIELD( m_nRadius, FIELD_INTEGER, "radius" ),
|
||||
DEFINE_KEYFIELD( m_flStrength,FIELD_FLOAT, "strength" ),
|
||||
|
||||
DEFINE_FIELD( m_bAutoExtinguish, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bUseMuzzlePoint, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
||||
|
||||
DEFINE_FUNCTION( ExtinguishThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( env_extinguisherjet, CExtinguisherJet );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CExtinguisherJet::CExtinguisherJet( void )
|
||||
{
|
||||
m_bEmit = false;
|
||||
m_bEnabled = false;
|
||||
m_bAutoExtinguish = true;
|
||||
|
||||
m_nLength = 128;
|
||||
m_nSize = 8;
|
||||
m_flStrength = 0.97f; //FIXME: Stub numbers
|
||||
m_nRadius = 32;
|
||||
|
||||
// Send to the client even though we don't have a model
|
||||
AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
if ( m_bEnabled )
|
||||
{
|
||||
TurnOn();
|
||||
}
|
||||
}
|
||||
|
||||
void CExtinguisherJet::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "ExtinguisherJet.TurnOn" );
|
||||
PrecacheScriptSound( "ExtinguisherJet.TurnOff" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::TurnOn( void )
|
||||
{
|
||||
//Turn on sound
|
||||
if ( m_bEmit == false )
|
||||
{
|
||||
EmitSound( "ExtinguisherJet.TurnOn" );
|
||||
m_bEnabled = m_bEmit = true;
|
||||
}
|
||||
|
||||
SetThink( ExtinguishThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::TurnOff( void )
|
||||
{
|
||||
//Turn off sound
|
||||
if ( m_bEmit )
|
||||
{
|
||||
EmitSound( "ExtinguisherJet.TurnOff" );
|
||||
m_bEnabled = m_bEmit = false;
|
||||
}
|
||||
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
TurnOn();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
TurnOff();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::InputToggle( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_bEnabled )
|
||||
{
|
||||
TurnOff();
|
||||
}
|
||||
else
|
||||
{
|
||||
TurnOn();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::Think( void )
|
||||
{
|
||||
CBaseEntity::Think();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CExtinguisherJet::ExtinguishThink( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if ( m_bEnabled == false )
|
||||
return;
|
||||
|
||||
if ( m_bAutoExtinguish == false )
|
||||
return;
|
||||
|
||||
Vector vTestPos;
|
||||
Vector vForward, vRight, vUp;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vForward );
|
||||
|
||||
vTestPos = GetAbsOrigin() + ( vForward * m_nLength );
|
||||
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), vTestPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
//Extinguish the fire where we hit
|
||||
FireSystem_ExtinguishInRadius( tr.endpos, m_nRadius, m_flStrength );
|
||||
|
||||
//Debug visualization
|
||||
if ( fire_extinguisher_debug.GetInt() )
|
||||
{
|
||||
int radius = m_nRadius;
|
||||
|
||||
NDebugOverlay::Line( GetAbsOrigin(), tr.endpos, 0, 0, 128, false, 0.1f );
|
||||
|
||||
NDebugOverlay::Box( GetAbsOrigin(), Vector(-1, -1, -1), Vector(1, 1, 1), 0, 0, 128, false, 0.1f );
|
||||
NDebugOverlay::Box( tr.endpos, Vector(-2, -2, -2), Vector(2, 2, 2), 0, 0, 128, false, 0.1f );
|
||||
NDebugOverlay::Box( tr.endpos, Vector(-radius, -radius, -radius), Vector(radius, radius, radius), 0, 0, 255, false, 0.1f );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef EXTINGUISHERJET_H
|
||||
#define EXTINGUISHERJET_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "baseparticleentity.h"
|
||||
|
||||
class CExtinguisherJet : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CExtinguisherJet, CBaseEntity );
|
||||
|
||||
CExtinguisherJet( void );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Precache();
|
||||
|
||||
void TurnOn( void );
|
||||
void TurnOff( void );
|
||||
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
void InputToggle( inputdata_t &inputdata );
|
||||
|
||||
virtual void Think( void );
|
||||
|
||||
void ExtinguishThink( void );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
// Stuff from the datatable.
|
||||
public:
|
||||
CNetworkVar( bool, m_bEmit ); // Emit particles?
|
||||
CNetworkVar( int, m_nLength ); // Length of jet
|
||||
CNetworkVar( int, m_nSize ); // Size of jet (as in width and noise of particle movement)
|
||||
int m_nRadius; // Radius area to extinguish where jet hits
|
||||
float m_flStrength; // Strength of the extinguisher
|
||||
|
||||
bool m_bEnabled;
|
||||
|
||||
//Used for viewmodel
|
||||
CNetworkVar( bool, m_bUseMuzzlePoint );
|
||||
bool m_bAutoExtinguish; //Whether extinguisher should put out fires in its think, or let owner do it
|
||||
};
|
||||
|
||||
#endif // EXTINGUISHERJET_H
|
||||
@@ -0,0 +1,101 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "entityoutput.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "func_bulletshield.h"
|
||||
#include "collisionutils.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern ConVar ent_debugkeys;
|
||||
extern ConVar showtriggers;
|
||||
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_bulletshield, CFuncBulletShield );
|
||||
|
||||
BEGIN_DATADESC( CFuncBulletShield )
|
||||
|
||||
/*
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
||||
DEFINE_KEYFIELD( m_iDisabled, FIELD_INTEGER, "StartDisabled" ),
|
||||
DEFINE_KEYFIELD( m_iSolidity, FIELD_INTEGER, "Solidity" ),
|
||||
DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ),
|
||||
DEFINE_KEYFIELD( m_iszExcludedClass, FIELD_STRING, "excludednpc" ),
|
||||
DEFINE_KEYFIELD( m_bInvertExclusion, FIELD_BOOLEAN, "invert_exclusion" ),
|
||||
*/
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
|
||||
void CFuncBulletShield::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
AddSolidFlags( FSOLID_CUSTOMRAYTEST );
|
||||
AddSolidFlags( FSOLID_CUSTOMBOXTEST );
|
||||
// SetSolid(SOLID_CUSTOM);
|
||||
|
||||
VPhysicsDestroyObject();
|
||||
}
|
||||
|
||||
/*
|
||||
bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta,
|
||||
const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs,
|
||||
float flTolerance, CBaseTrace *pTrace );
|
||||
|
||||
bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta,
|
||||
const Vector &vecBoxOrigin, const QAngle &angBoxRotation,
|
||||
const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace );
|
||||
|
||||
bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation,
|
||||
const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace );
|
||||
|
||||
bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld,
|
||||
const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace );
|
||||
|
||||
bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta,
|
||||
const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs,
|
||||
float flTolerance, BoxTraceInfo_t *pTrace );
|
||||
*/
|
||||
|
||||
bool CFuncBulletShield::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
|
||||
{
|
||||
// ignore unless a shot
|
||||
if ((mask & MASK_SHOT) == MASK_SHOT)
|
||||
{
|
||||
// use obb collision
|
||||
ICollideable *pCol = GetCollideable();
|
||||
Assert(pCol);
|
||||
|
||||
return IntersectRayWithOBB(ray,pCol->GetCollisionOrigin(),pCol->GetCollisionAngles(),
|
||||
pCol->OBBMins(),pCol->OBBMaxs(),1.0f,&trace);
|
||||
|
||||
/*
|
||||
const model_t *pModel = this->GetCollisionModel();
|
||||
if ( pModel && pModel->type == mod_brush )
|
||||
{
|
||||
int nModelIndex = this->GetCollisionModelIndex();
|
||||
cmodel_t *pCModel = CM_InlineModelNumber( nModelIndex - 1 );
|
||||
int nHeadNode = pCModel->headnode;
|
||||
|
||||
CM_TransformedBoxTrace( ray, nHeadNode, fMask, this->GetCollisionOrigin(), this->GetCollisionAngles(), *pTrace );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
*/
|
||||
|
||||
// return BaseClass::TestCollision( ray, mask, trace );
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef FUNC_BULLETSHIELD_H
|
||||
#define FUNC_BULLETSHIELD_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//!! replace this with generic start enabled/disabled
|
||||
#define SF_WALL_START_OFF 0x0001
|
||||
#define SF_IGNORE_PLAYERUSE 0x0002
|
||||
|
||||
#include "modelentities.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: shield that stops bullets, but not other objects
|
||||
// enabled state: brush is visible
|
||||
// disabled staute: brush not visible
|
||||
//-----------------------------------------------------------------------------
|
||||
class CFuncBulletShield : public CFuncBrush
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CFuncBulletShield, CFuncBrush );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void Spawn( void );
|
||||
|
||||
bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
|
||||
/*
|
||||
bool CreateVPhysics( void );
|
||||
|
||||
virtual int ObjectCaps( void ) { return HasSpawnFlags(SF_IGNORE_PLAYERUSE) ? BaseClass::ObjectCaps() : BaseClass::ObjectCaps() | FCAP_IMPULSE_USE; }
|
||||
|
||||
virtual int DrawDebugTextOverlays( void );
|
||||
|
||||
void TurnOff( void );
|
||||
void TurnOn( void );
|
||||
|
||||
// Input handlers
|
||||
void InputTurnOff( inputdata_t &inputdata );
|
||||
void InputTurnOn( inputdata_t &inputdata );
|
||||
void InputToggle( inputdata_t &inputdata );
|
||||
|
||||
enum BrushSolidities_e {
|
||||
BRUSHSOLID_TOGGLE = 0,
|
||||
BRUSHSOLID_NEVER = 1,
|
||||
BRUSHSOLID_ALWAYS = 2,
|
||||
};
|
||||
|
||||
BrushSolidities_e m_iSolidity;
|
||||
int m_iDisabled;
|
||||
bool m_bSolidBsp;
|
||||
string_t m_iszExcludedClass;
|
||||
bool m_bInvertExclusion;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual bool IsOn( void );
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
#endif // MODELENTITIES_H
|
||||
@@ -0,0 +1,777 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== h_battery.cpp ========================================================
|
||||
|
||||
battery-related code
|
||||
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gamerules.h"
|
||||
#include "player.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "in_buttons.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static ConVar sk_suitcharger( "sk_suitcharger","0" );
|
||||
static ConVar sk_suitcharger_citadel( "sk_suitcharger_citadel","0" );
|
||||
static ConVar sk_suitcharger_citadel_maxarmor( "sk_suitcharger_citadel_maxarmor","0" );
|
||||
|
||||
#define SF_CITADEL_RECHARGER 0x2000
|
||||
#define SF_KLEINER_RECHARGER 0x4000 // Gives only 25 health
|
||||
|
||||
class CRecharge : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CRecharge, CBaseToggle );
|
||||
|
||||
void Spawn( );
|
||||
bool CreateVPhysics();
|
||||
int DrawDebugTextOverlays(void);
|
||||
void Off(void);
|
||||
void Recharge(void);
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_CONTINUOUS_USE); }
|
||||
|
||||
private:
|
||||
void InputRecharge( inputdata_t &inputdata );
|
||||
|
||||
float MaxJuice() const;
|
||||
void UpdateJuice( int newJuice );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
|
||||
int m_nState;
|
||||
|
||||
COutputFloat m_OutRemainingCharge;
|
||||
COutputEvent m_OnHalfEmpty;
|
||||
COutputEvent m_OnEmpty;
|
||||
COutputEvent m_OnFull;
|
||||
COutputEvent m_OnPlayerUse;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CRecharge )
|
||||
|
||||
DEFINE_FIELD( m_flNextCharge, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_flSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Off ),
|
||||
DEFINE_FUNCTION( Recharge ),
|
||||
|
||||
DEFINE_OUTPUT(m_OutRemainingCharge, "OutRemainingCharge"),
|
||||
DEFINE_OUTPUT(m_OnHalfEmpty, "OnHalfEmpty" ),
|
||||
DEFINE_OUTPUT(m_OnEmpty, "OnEmpty" ),
|
||||
DEFINE_OUTPUT(m_OnFull, "OnFull" ),
|
||||
DEFINE_OUTPUT(m_OnPlayerUse, "OnPlayerUse" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Recharge", InputRecharge ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS(func_recharge, CRecharge);
|
||||
|
||||
|
||||
bool CRecharge::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if ( FStrEq(szKeyName, "style") ||
|
||||
FStrEq(szKeyName, "height") ||
|
||||
FStrEq(szKeyName, "value1") ||
|
||||
FStrEq(szKeyName, "value2") ||
|
||||
FStrEq(szKeyName, "value3"))
|
||||
{
|
||||
}
|
||||
else if (FStrEq(szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(szValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BaseClass::KeyValue( szKeyName, szValue );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRecharge::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetSolid( SOLID_BSP );
|
||||
SetMoveType( MOVETYPE_PUSH );
|
||||
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
|
||||
UpdateJuice( MaxJuice() );
|
||||
|
||||
m_nState = 0;
|
||||
|
||||
CreateVPhysics();
|
||||
}
|
||||
|
||||
bool CRecharge::CreateVPhysics()
|
||||
{
|
||||
VPhysicsInitStatic();
|
||||
return true;
|
||||
}
|
||||
|
||||
int CRecharge::DrawDebugTextOverlays(void)
|
||||
{
|
||||
int text_offset = BaseClass::DrawDebugTextOverlays();
|
||||
|
||||
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
||||
{
|
||||
char tempstr[512];
|
||||
Q_snprintf(tempstr,sizeof(tempstr),"Charge left: %i", m_iJuice );
|
||||
EntityText(text_offset,tempstr,0);
|
||||
text_offset++;
|
||||
}
|
||||
return text_offset;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Max juice for recharger
|
||||
//-----------------------------------------------------------------------------
|
||||
float CRecharge::MaxJuice() const
|
||||
{
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
{
|
||||
return sk_suitcharger_citadel.GetFloat();
|
||||
}
|
||||
|
||||
return sk_suitcharger.GetFloat();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : newJuice -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CRecharge::UpdateJuice( int newJuice )
|
||||
{
|
||||
bool reduced = newJuice < m_iJuice;
|
||||
if ( reduced )
|
||||
{
|
||||
// Fire 1/2 way output and/or empyt output
|
||||
int oneHalfJuice = (int)(MaxJuice() * 0.5f);
|
||||
if ( newJuice <= oneHalfJuice && m_iJuice > oneHalfJuice )
|
||||
{
|
||||
m_OnHalfEmpty.FireOutput( this, this );
|
||||
}
|
||||
|
||||
if ( newJuice <= 0 )
|
||||
{
|
||||
m_OnEmpty.FireOutput( this, this );
|
||||
}
|
||||
}
|
||||
else if ( newJuice != m_iJuice &&
|
||||
newJuice == (int)MaxJuice() )
|
||||
{
|
||||
m_OnFull.FireOutput( this, this );
|
||||
}
|
||||
m_iJuice = newJuice;
|
||||
}
|
||||
|
||||
void CRecharge::InputRecharge( inputdata_t &inputdata )
|
||||
{
|
||||
Recharge();
|
||||
}
|
||||
|
||||
void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator || !pActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
// Only usable if you have the HEV suit on
|
||||
if ( !((CBasePlayer *)pActivator)->IsSuitEquipped() )
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
m_nState = 1;
|
||||
Off();
|
||||
}
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise
|
||||
if ( m_iJuice <= 0 )
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.25 );
|
||||
SetThink(&CRecharge::Off);
|
||||
|
||||
// Time to recharge yet?
|
||||
if (m_flNextCharge >= gpGlobals->curtime)
|
||||
return;
|
||||
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
|
||||
m_hActivator = pActivator;
|
||||
|
||||
//only recharge the player
|
||||
|
||||
if (!m_hActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EmitSound( "SuitRecharge.Start" );
|
||||
m_flSoundTime = 0.56 + gpGlobals->curtime;
|
||||
|
||||
m_OnPlayerUse.FireOutput( pActivator, this );
|
||||
}
|
||||
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->curtime))
|
||||
{
|
||||
m_iOn++;
|
||||
CPASAttenuationFilter filter( this, "SuitRecharge.ChargingLoop" );
|
||||
filter.MakeReliable();
|
||||
EmitSound( filter, entindex(), "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
CBasePlayer *pl = (CBasePlayer *) m_hActivator.Get();
|
||||
|
||||
// charge the player
|
||||
int nMaxArmor = 100;
|
||||
int nIncrementArmor = 1;
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
{
|
||||
nMaxArmor = sk_suitcharger_citadel_maxarmor.GetInt();
|
||||
nIncrementArmor = 10;
|
||||
|
||||
// Also give health for the citadel version.
|
||||
if( pActivator->GetHealth() < pActivator->GetMaxHealth() )
|
||||
{
|
||||
pActivator->TakeHealth( 5, DMG_GENERIC );
|
||||
}
|
||||
}
|
||||
|
||||
if (pl->ArmorValue() < nMaxArmor)
|
||||
{
|
||||
UpdateJuice( m_iJuice - nIncrementArmor );
|
||||
pl->IncrementArmorValue( nIncrementArmor, nMaxArmor );
|
||||
}
|
||||
|
||||
// Send the output.
|
||||
float flRemaining = m_iJuice / MaxJuice();
|
||||
m_OutRemainingCharge.Set(flRemaining, pActivator, this);
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
|
||||
void CRecharge::Recharge(void)
|
||||
{
|
||||
UpdateJuice( MaxJuice() );
|
||||
m_nState = 0;
|
||||
SetThink( &CRecharge::SUB_DoNothing );
|
||||
}
|
||||
|
||||
void CRecharge::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
{
|
||||
StopSound( "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
m_iOn = 0;
|
||||
|
||||
if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() ) > 0) )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_iReactivate );
|
||||
SetThink(&CRecharge::Recharge);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//NEW
|
||||
class CNewRecharge : public CBaseAnimating
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNewRecharge, CBaseAnimating );
|
||||
|
||||
void Spawn( );
|
||||
bool CreateVPhysics();
|
||||
int DrawDebugTextOverlays(void);
|
||||
void Off(void);
|
||||
void Recharge(void);
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | m_iCaps ); }
|
||||
|
||||
void SetInitialCharge( void );
|
||||
|
||||
private:
|
||||
void InputRecharge( inputdata_t &inputdata );
|
||||
void InputSetCharge( inputdata_t &inputdata );
|
||||
float MaxJuice() const;
|
||||
void UpdateJuice( int newJuice );
|
||||
void Precache( void );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
|
||||
int m_nState;
|
||||
int m_iCaps;
|
||||
int m_iMaxJuice;
|
||||
|
||||
COutputFloat m_OutRemainingCharge;
|
||||
COutputEvent m_OnHalfEmpty;
|
||||
COutputEvent m_OnEmpty;
|
||||
COutputEvent m_OnFull;
|
||||
COutputEvent m_OnPlayerUse;
|
||||
|
||||
virtual void StudioFrameAdvance ( void );
|
||||
float m_flJuice;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CNewRecharge )
|
||||
|
||||
DEFINE_FIELD( m_flNextCharge, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_flSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iCaps, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iMaxJuice, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Off ),
|
||||
DEFINE_FUNCTION( Recharge ),
|
||||
|
||||
DEFINE_OUTPUT(m_OutRemainingCharge, "OutRemainingCharge"),
|
||||
DEFINE_OUTPUT(m_OnHalfEmpty, "OnHalfEmpty" ),
|
||||
DEFINE_OUTPUT(m_OnEmpty, "OnEmpty" ),
|
||||
DEFINE_OUTPUT(m_OnFull, "OnFull" ),
|
||||
DEFINE_OUTPUT(m_OnPlayerUse, "OnPlayerUse" ),
|
||||
DEFINE_FIELD( m_flJuice, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Recharge", InputRecharge ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCharge", InputSetCharge ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_suitcharger, CNewRecharge);
|
||||
|
||||
#define HEALTH_CHARGER_MODEL_NAME "models/props_combine/suit_charger001.mdl"
|
||||
#define CHARGE_RATE 0.25f
|
||||
#define CHARGES_PER_SECOND 1 / CHARGE_RATE
|
||||
#define CITADEL_CHARGES_PER_SECOND 10 / CHARGE_RATE
|
||||
#define CALLS_PER_SECOND 7.0f * CHARGES_PER_SECOND
|
||||
|
||||
|
||||
bool CNewRecharge::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if ( FStrEq(szKeyName, "style") ||
|
||||
FStrEq(szKeyName, "height") ||
|
||||
FStrEq(szKeyName, "value1") ||
|
||||
FStrEq(szKeyName, "value2") ||
|
||||
FStrEq(szKeyName, "value3"))
|
||||
{
|
||||
}
|
||||
else if (FStrEq(szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(szValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BaseClass::KeyValue( szKeyName, szValue );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNewRecharge::Precache( void )
|
||||
{
|
||||
PrecacheModel( HEALTH_CHARGER_MODEL_NAME );
|
||||
|
||||
PrecacheScriptSound( "SuitRecharge.Deny" );
|
||||
PrecacheScriptSound( "SuitRecharge.Start" );
|
||||
PrecacheScriptSound( "SuitRecharge.ChargingLoop" );
|
||||
|
||||
}
|
||||
|
||||
void CNewRecharge::SetInitialCharge( void )
|
||||
{
|
||||
if ( HasSpawnFlags( SF_KLEINER_RECHARGER ) )
|
||||
{
|
||||
// The charger in Kleiner's lab.
|
||||
m_iMaxJuice = 25.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
{
|
||||
m_iMaxJuice = sk_suitcharger_citadel.GetFloat();
|
||||
return;
|
||||
}
|
||||
|
||||
m_iMaxJuice = sk_suitcharger.GetFloat();
|
||||
}
|
||||
|
||||
void CNewRecharge::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetSolid( SOLID_VPHYSICS );
|
||||
CreateVPhysics();
|
||||
|
||||
SetModel( HEALTH_CHARGER_MODEL_NAME );
|
||||
AddEffects( EF_NOSHADOW );
|
||||
|
||||
ResetSequence( LookupSequence( "idle" ) );
|
||||
|
||||
SetInitialCharge();
|
||||
|
||||
UpdateJuice( MaxJuice() );
|
||||
|
||||
m_nState = 0;
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
CreateVPhysics();
|
||||
|
||||
m_flJuice = m_iJuice;
|
||||
|
||||
m_iReactivate = 0;
|
||||
|
||||
SetCycle( 1.0f - ( m_flJuice / MaxJuice() ) );
|
||||
}
|
||||
|
||||
bool CNewRecharge::CreateVPhysics()
|
||||
{
|
||||
VPhysicsInitStatic();
|
||||
return true;
|
||||
}
|
||||
|
||||
int CNewRecharge::DrawDebugTextOverlays(void)
|
||||
{
|
||||
int text_offset = BaseClass::DrawDebugTextOverlays();
|
||||
|
||||
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
||||
{
|
||||
char tempstr[512];
|
||||
Q_snprintf(tempstr,sizeof(tempstr),"Charge left: %i", m_iJuice );
|
||||
EntityText(text_offset,tempstr,0);
|
||||
text_offset++;
|
||||
}
|
||||
return text_offset;
|
||||
}
|
||||
|
||||
void CNewRecharge::StudioFrameAdvance( void )
|
||||
{
|
||||
m_flPlaybackRate = 0;
|
||||
|
||||
float flMaxJuice = MaxJuice() + 0.1f;
|
||||
float flNewJuice = 1.0f - (float)( m_flJuice / flMaxJuice );
|
||||
|
||||
SetCycle( flNewJuice );
|
||||
// Msg( "Cycle: %f - Juice: %d - m_flJuice :%f - Interval: %f\n", (float)GetCycle(), (int)m_iJuice, (float)m_flJuice, GetAnimTimeInterval() );
|
||||
|
||||
if ( !m_flPrevAnimTime )
|
||||
{
|
||||
m_flPrevAnimTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
// Latch prev
|
||||
m_flPrevAnimTime = m_flAnimTime;
|
||||
// Set current
|
||||
m_flAnimTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Max juice for recharger
|
||||
//-----------------------------------------------------------------------------
|
||||
float CNewRecharge::MaxJuice() const
|
||||
{
|
||||
return m_iMaxJuice;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : newJuice -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewRecharge::UpdateJuice( int newJuice )
|
||||
{
|
||||
bool reduced = newJuice < m_iJuice;
|
||||
if ( reduced )
|
||||
{
|
||||
// Fire 1/2 way output and/or empyt output
|
||||
int oneHalfJuice = (int)(MaxJuice() * 0.5f);
|
||||
if ( newJuice <= oneHalfJuice && m_iJuice > oneHalfJuice )
|
||||
{
|
||||
m_OnHalfEmpty.FireOutput( this, this );
|
||||
}
|
||||
|
||||
if ( newJuice <= 0 )
|
||||
{
|
||||
m_OnEmpty.FireOutput( this, this );
|
||||
}
|
||||
}
|
||||
else if ( newJuice != m_iJuice &&
|
||||
newJuice == (int)MaxJuice() )
|
||||
{
|
||||
m_OnFull.FireOutput( this, this );
|
||||
}
|
||||
m_iJuice = newJuice;
|
||||
}
|
||||
|
||||
void CNewRecharge::InputRecharge( inputdata_t &inputdata )
|
||||
{
|
||||
Recharge();
|
||||
}
|
||||
|
||||
void CNewRecharge::InputSetCharge( inputdata_t &inputdata )
|
||||
{
|
||||
ResetSequence( LookupSequence( "idle" ) );
|
||||
|
||||
int iJuice = inputdata.value.Int();
|
||||
|
||||
m_flJuice = m_iMaxJuice = m_iJuice = iJuice;
|
||||
StudioFrameAdvance();
|
||||
}
|
||||
|
||||
void CNewRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator || !pActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
CBasePlayer *pPlayer = static_cast<CBasePlayer *>(pActivator);
|
||||
|
||||
// Reset to a state of continuous use.
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
if ( m_iOn )
|
||||
{
|
||||
float flCharges = CHARGES_PER_SECOND;
|
||||
float flCalls = CALLS_PER_SECOND;
|
||||
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
flCharges = CITADEL_CHARGES_PER_SECOND;
|
||||
|
||||
m_flJuice -= flCharges / flCalls;
|
||||
StudioFrameAdvance();
|
||||
}
|
||||
|
||||
// Only usable if you have the HEV suit on
|
||||
if ( !pPlayer->IsSuitEquipped() )
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if ( m_iJuice <= 0 )
|
||||
{
|
||||
// Start our deny animation over again
|
||||
ResetSequence( LookupSequence( "emptyclick" ) );
|
||||
|
||||
m_nState = 1;
|
||||
|
||||
// Shut off
|
||||
Off();
|
||||
|
||||
// Play a deny sound
|
||||
if ( m_flSoundTime <= gpGlobals->curtime )
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get our maximum armor value
|
||||
int nMaxArmor = 100;
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
{
|
||||
nMaxArmor = sk_suitcharger_citadel_maxarmor.GetInt();
|
||||
}
|
||||
|
||||
int nIncrementArmor = 1;
|
||||
|
||||
// The citadel charger gives more per charge and also gives health
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
{
|
||||
nIncrementArmor = 10;
|
||||
|
||||
#ifdef HL2MP
|
||||
nIncrementArmor = 2;
|
||||
#endif
|
||||
|
||||
// Also give health for the citadel version.
|
||||
if ( pActivator->GetHealth() < pActivator->GetMaxHealth() && m_flNextCharge < gpGlobals->curtime )
|
||||
{
|
||||
pActivator->TakeHealth( 5, DMG_GENERIC );
|
||||
}
|
||||
}
|
||||
|
||||
// If we're over our limit, debounce our keys
|
||||
if ( pPlayer->ArmorValue() >= nMaxArmor)
|
||||
{
|
||||
// Citadel charger must also be at max health
|
||||
if ( !HasSpawnFlags(SF_CITADEL_RECHARGER) || ( HasSpawnFlags( SF_CITADEL_RECHARGER ) && pActivator->GetHealth() >= pActivator->GetMaxHealth() ) )
|
||||
{
|
||||
// Make the user re-use me to get started drawing health.
|
||||
pPlayer->m_afButtonPressed &= ~IN_USE;
|
||||
m_iCaps = FCAP_IMPULSE_USE;
|
||||
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This is bumped out if used within the time period
|
||||
SetNextThink( gpGlobals->curtime + CHARGE_RATE );
|
||||
SetThink( &CNewRecharge::Off );
|
||||
|
||||
// Time to recharge yet?
|
||||
if ( m_flNextCharge >= gpGlobals->curtime )
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if ( !m_iOn )
|
||||
{
|
||||
m_iOn++;
|
||||
EmitSound( "SuitRecharge.Start" );
|
||||
m_flSoundTime = 0.56 + gpGlobals->curtime;
|
||||
|
||||
m_OnPlayerUse.FireOutput( pActivator, this );
|
||||
}
|
||||
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->curtime))
|
||||
{
|
||||
m_iOn++;
|
||||
CPASAttenuationFilter filter( this, "SuitRecharge.ChargingLoop" );
|
||||
filter.MakeReliable();
|
||||
EmitSound( filter, entindex(), "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
// Give armor if we need it
|
||||
if ( pPlayer->ArmorValue() < nMaxArmor )
|
||||
{
|
||||
UpdateJuice( m_iJuice - nIncrementArmor );
|
||||
pPlayer->IncrementArmorValue( nIncrementArmor, nMaxArmor );
|
||||
}
|
||||
|
||||
// Send the output.
|
||||
float flRemaining = m_iJuice / MaxJuice();
|
||||
m_OutRemainingCharge.Set(flRemaining, pActivator, this);
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
|
||||
void CNewRecharge::Recharge(void)
|
||||
{
|
||||
EmitSound( "SuitRecharge.Start" );
|
||||
ResetSequence( LookupSequence( "idle" ) );
|
||||
|
||||
UpdateJuice( MaxJuice() );
|
||||
|
||||
m_nState = 0;
|
||||
m_flJuice = m_iJuice;
|
||||
m_iReactivate = 0;
|
||||
StudioFrameAdvance();
|
||||
|
||||
SetThink( &CNewRecharge::SUB_DoNothing );
|
||||
}
|
||||
|
||||
void CNewRecharge::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
{
|
||||
StopSound( "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
if ( m_nState == 1 )
|
||||
{
|
||||
SetCycle( 1.0f );
|
||||
}
|
||||
|
||||
m_iOn = 0;
|
||||
m_flJuice = m_iJuice;
|
||||
|
||||
if ( m_iReactivate == 0 )
|
||||
{
|
||||
if ((!m_iJuice) && g_pGameRules->FlHEVChargerRechargeTime() > 0 )
|
||||
{
|
||||
if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) )
|
||||
{
|
||||
m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime();
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime + m_iReactivate );
|
||||
SetThink(&CNewRecharge::Recharge);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,353 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef FUNC_TANK_H
|
||||
#define FUNC_TANK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "triggers.h"
|
||||
|
||||
#define SF_TANK_ACTIVE 0x0001
|
||||
#define SF_TANK_PLAYER 0x0002
|
||||
#define SF_TANK_HUMANS 0x0004
|
||||
#define SF_TANK_ALIENS 0x0008
|
||||
#define SF_TANK_LINEOFSIGHT 0x0010
|
||||
#define SF_TANK_CANCONTROL 0x0020
|
||||
#define SF_TANK_DAMAGE_KICK 0x0040 // Kick when take damage
|
||||
#define SF_TANK_AIM_AT_POS 0x0080 // Aim at a particular position
|
||||
#define SF_TANK_AIM_ASSISTANCE 0x0100
|
||||
#define SF_TANK_NPC 0x0200
|
||||
#define SF_TANK_NPC_CONTROLLABLE 0x0400 // 1024
|
||||
#define SF_TANK_NPC_SET_CONTROLLER 0x0800 // 2048
|
||||
#define SF_TANK_ALLOW_PLAYER_HITS 0x1000 // 4096 Allow friendly NPCs to fire upon enemies near the player
|
||||
#define SF_TANK_IGNORE_RANGE_IN_VIEWCONE 0x2000 // 8192 Don't use range as a factor in determining if something is in view cone
|
||||
#define SF_TANK_NOTSOLID 0x8000 // 32768
|
||||
#define SF_TANK_SOUNDON 0x10000 // FIXME: This is not really a spawnflag! It holds transient state!!!
|
||||
#define SF_TANK_HACKPLAYERHIT 0x20000 // 131072 Make this func_tank cheat and hit the player regularly
|
||||
|
||||
#define FUNCTANK_DISTANCE_MAX 1200 // 100 ft.
|
||||
#define FUNCTANK_DISTANCE_MIN_TO_ENEMY 180
|
||||
#define FUNCTANK_FIREVOLUME 1000
|
||||
#define FUNCTANK_NPC_ROUTE_TIME 5.0f
|
||||
|
||||
// Effect handling
|
||||
// If the func_tank has a chosen method of handling effects, use that
|
||||
// instead of the individual effect settings. (muzzleflash, sound, tracer, etc)
|
||||
enum FUNCTANK_EFFECT_HANDLING
|
||||
{
|
||||
EH_NONE, // Use the effect settings
|
||||
EH_AR2, // Use AR2 effects
|
||||
EH_COMBINE_CANNON // Large Combine cannon
|
||||
};
|
||||
|
||||
enum TANKBULLET
|
||||
{
|
||||
TANK_BULLET_NONE = 0,
|
||||
TANK_BULLET_SMALL = 1,
|
||||
TANK_BULLET_MEDIUM = 2,
|
||||
TANK_BULLET_LARGE = 3,
|
||||
};
|
||||
|
||||
#define MORTAR_BLAST_RADIUS 350
|
||||
|
||||
|
||||
// Custom damage
|
||||
// env_laser (duration is 0.5 rate of fire)
|
||||
// rockets
|
||||
// explosion?
|
||||
|
||||
class CFuncTank : public CBaseEntity
|
||||
{
|
||||
|
||||
DECLARE_CLASS( CFuncTank, CBaseEntity );
|
||||
|
||||
public:
|
||||
|
||||
CFuncTank();
|
||||
~CFuncTank();
|
||||
void Spawn( void );
|
||||
void Activate( void );
|
||||
void Precache( void );
|
||||
bool CreateVPhysics( void );
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void UpdateOnRemove();
|
||||
|
||||
void SetYawRate( float flYawRate ) { m_yawRate = flYawRate; }
|
||||
void SetPitchRate( float flPitchRate ) { m_pitchRate = flPitchRate; }
|
||||
|
||||
int ObjectCaps( void )
|
||||
{
|
||||
return ( BaseClass::ObjectCaps() | FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS );
|
||||
}
|
||||
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
virtual void FuncTankPreThink() { return; }
|
||||
void Think( void );
|
||||
virtual void FuncTankPostThink() { return; }
|
||||
|
||||
int GetAmmoCount( void ) { return m_iAmmoCount; }
|
||||
|
||||
// NPC
|
||||
bool NPC_FindManPoint( Vector &vecPos );
|
||||
bool NPC_HasEnemy( void );
|
||||
void NPC_Fire( void );
|
||||
void NPC_InterruptRoute( void );
|
||||
void NPC_JustSawPlayer( CBaseEntity *pTarget );
|
||||
void NPC_SetInRoute( bool bInRoute ) { m_bNPCInRoute = bInRoute; }
|
||||
void NPC_SetIdleAngle( Vector vecIdle ) { m_vecNPCIdleTarget = vecIdle; }
|
||||
|
||||
// LOS
|
||||
bool IsEntityInViewCone( CBaseEntity *pEntity );
|
||||
bool HasLOSTo( CBaseEntity *pEntity );
|
||||
|
||||
// Controller
|
||||
CBaseCombatCharacter *GetController( void );
|
||||
bool StartControl( CBaseCombatCharacter *pController );
|
||||
void StopControl( void );
|
||||
Vector GetTargetPosition() { return m_vTargetPosition; }
|
||||
void SetTargetPosition( const Vector &vecPos ) { m_vTargetPosition = vecPos; }
|
||||
|
||||
const float YawCenter() const { return m_yawCenter; }
|
||||
const float YawCenterWorld() const { return m_yawCenterWorld; }
|
||||
const float YawRange() const { return m_yawRange; }
|
||||
const float PitchCenter() const { return m_pitchCenter; }
|
||||
const float PitchCenterWorld() const { return m_pitchCenterWorld; }
|
||||
const float PitchRange() const { return m_pitchRange; }
|
||||
|
||||
virtual void PhysicsSimulate( void );
|
||||
|
||||
virtual void OnStartControlled() {}
|
||||
virtual void OnStopControlled() {}
|
||||
|
||||
// SF Tests.
|
||||
inline bool IsControllable( void ) { return ( m_spawnflags & SF_TANK_CANCONTROL ) ? true : false; }
|
||||
inline bool IsActive( void ) { return ( m_spawnflags & SF_TANK_ACTIVE ) ? true : false; }
|
||||
inline bool IsNPCControllable( void ) { return ( m_spawnflags & SF_TANK_NPC_CONTROLLABLE ) ? true : false; }
|
||||
inline bool IsNPCSetController( void ) { return ( m_spawnflags & SF_TANK_NPC_SET_CONTROLLER ) ? true : false; }
|
||||
|
||||
virtual void DoMuzzleFlash( void );
|
||||
virtual const char *GetTracerType( void );
|
||||
|
||||
protected:
|
||||
virtual float GetShotSpeed() { return 0; }
|
||||
|
||||
virtual Vector WorldBarrelPosition( void );
|
||||
void UpdateMatrix( void );
|
||||
|
||||
float GetNextAttack() const { return m_flNextAttack; }
|
||||
virtual void SetNextAttack( float flWait ) { m_flNextAttack = flWait; }
|
||||
|
||||
virtual void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread );
|
||||
void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, trace_t &tr );
|
||||
int GetRandomBurst( void );
|
||||
float GetRandomFireTime( void );
|
||||
|
||||
void CalcPlayerCrosshairTarget( Vector *pVecTarget );
|
||||
void CalcNPCEnemyTarget( Vector *pVecTarget );
|
||||
|
||||
inline bool IsPlayerManned( void ) { return m_hController && m_hController->IsPlayer() && ( m_spawnflags & SF_TANK_PLAYER ); }
|
||||
inline bool IsNPCManned( void ) { return m_hController && m_hController->MyNPCPointer() && ( m_spawnflags & SF_TANK_NPC ); }
|
||||
|
||||
private:
|
||||
void TrackTarget( void );
|
||||
int DrawDebugTextOverlays(void);
|
||||
void DrawDebugGeometryOverlays(void);
|
||||
|
||||
virtual void FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker );
|
||||
|
||||
void StartRotSound( void );
|
||||
void StopRotSound( void );
|
||||
|
||||
// Input handlers.
|
||||
void InputActivate( inputdata_t &inputdata );
|
||||
void InputDeactivate( inputdata_t &inputdata );
|
||||
void InputSetFireRate( inputdata_t &inputdata );
|
||||
void InputSetDamage( inputdata_t &inputdata );
|
||||
void InputSetTargetDir( inputdata_t &inputdata );
|
||||
void InputSetTargetPosition( inputdata_t &inputdata );
|
||||
void InputSetTargetEntityName( inputdata_t &inputdata );
|
||||
|
||||
protected:
|
||||
virtual void InputSetTargetEntity( inputdata_t &inputdata );
|
||||
virtual void InputClearTargetEntity( inputdata_t &inputdata );
|
||||
|
||||
private:
|
||||
void InputFindNPCToManTank( inputdata_t &inputdata );
|
||||
void InputStopFindingNPCs( inputdata_t &inputdata );
|
||||
void InputStartFindingNPCs( inputdata_t &inputdata );
|
||||
void InputForceNPCOff( inputdata_t &inputdata );
|
||||
void InputSetMaxRange( inputdata_t &inputdata );
|
||||
|
||||
inline bool CanFire( void );
|
||||
bool InRange( float range );
|
||||
bool InRange2( float flRange2 );
|
||||
|
||||
void TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType);
|
||||
|
||||
QAngle AimBarrelAt( const Vector &parentTarget );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
bool OnControls( CBaseEntity *pTest );
|
||||
bool HasController( void );
|
||||
|
||||
CBaseEntity *FindTarget( string_t targetName, CBaseEntity *pActivator );
|
||||
|
||||
// NPC
|
||||
void NPC_FindController( void );
|
||||
bool NPC_InRoute( void ) { return m_bNPCInRoute; }
|
||||
bool NPC_InterruptController( void );
|
||||
|
||||
// Aim the tank at the player crosshair
|
||||
void AimBarrelAtPlayerCrosshair( QAngle *pAngles );
|
||||
|
||||
// Aim the tank at the NPC's enemy
|
||||
void AimBarrelAtNPCEnemy( QAngle *pAngles );
|
||||
|
||||
// Aim the tank at the func_tank's enemy
|
||||
void AimFuncTankAtTarget( void );
|
||||
|
||||
// Returns true if the desired angles are out of range
|
||||
bool RotateTankToAngles( const QAngle &angles, float *pDistX = NULL, float *pDistY = NULL );
|
||||
|
||||
// We lost our target!
|
||||
void LostTarget( void );
|
||||
|
||||
// Purpose:
|
||||
void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition );
|
||||
|
||||
protected:
|
||||
virtual void ControllerPostFrame( void );
|
||||
|
||||
virtual void TankActivate(void);
|
||||
virtual void TankDeactivate(void);
|
||||
|
||||
float m_fireLast; // Last time I fired
|
||||
float m_fireRate; // How many rounds/second
|
||||
|
||||
EHANDLE m_hTarget;
|
||||
|
||||
TANKBULLET m_bulletType; // Bullet type
|
||||
int m_iBulletDamage; // 0 means use Bullet type's default damage
|
||||
int m_iBulletDamageVsPlayer; // Damage vs player. 0 means use m_iBulletDamage
|
||||
|
||||
#ifdef HL2_EPISODIC
|
||||
string_t m_iszAmmoType; // The name of the ammodef that we use when we fire. Bullet damage still comes from keyvalues.
|
||||
int m_iAmmoType; // The cached index of the ammodef that we use when we fire.
|
||||
#else
|
||||
int m_iSmallAmmoType;
|
||||
int m_iMediumAmmoType;
|
||||
int m_iLargeAmmoType;
|
||||
#endif // HL2_EPISODIC
|
||||
|
||||
int m_spread; // firing spread
|
||||
|
||||
EntityMatrix m_parentMatrix;
|
||||
|
||||
Vector m_sightOrigin; // Last sight of target
|
||||
EHANDLE m_hFuncTankTarget;
|
||||
|
||||
int m_nBulletCount;
|
||||
|
||||
private:
|
||||
|
||||
// This is either the player manning the func_tank, or an NPC. The NPC is either manning the tank, or running
|
||||
// to the man point. If he's en-route, m_bNPCInRoute will be true.
|
||||
CHandle<CBaseCombatCharacter> m_hController;
|
||||
|
||||
float m_flNextAttack;
|
||||
Vector m_vecControllerUsePos;
|
||||
|
||||
float m_yawCenter; // "Center" yaw
|
||||
float m_yawCenterWorld; // "Center" yaw in world space
|
||||
float m_yawRate; // Max turn rate to track targets
|
||||
float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center)
|
||||
// Zero is full rotation
|
||||
float m_yawTolerance; // Tolerance angle
|
||||
|
||||
float m_pitchCenter; // "Center" pitch
|
||||
float m_pitchCenterWorld; // "Center" pitch in world space
|
||||
float m_pitchRate; // Max turn rate on pitch
|
||||
float m_pitchRange; // Range of pitch motion as above
|
||||
float m_pitchTolerance; // Tolerance angle
|
||||
|
||||
float m_fireTime; // How much time has been used to fire the weapon so far.
|
||||
float m_lastSightTime;// Last time I saw target
|
||||
float m_persist; // Persistence of firing (how long do I shoot when I can't see)
|
||||
float m_persist2; // Secondary persistence of firing (randomly shooting when I can't see)
|
||||
float m_persist2burst;// How long secondary persistence burst lasts
|
||||
float m_minRange; // Minimum range to aim/track
|
||||
float m_maxRange; // Max range to aim/track
|
||||
float m_flMinRange2;
|
||||
float m_flMaxRange2;
|
||||
int m_iAmmoCount; // ammo
|
||||
|
||||
Vector m_barrelPos; // Length of the freakin barrel
|
||||
float m_spriteScale; // Scale of any sprites we shoot
|
||||
string_t m_iszSpriteSmoke;
|
||||
string_t m_iszSpriteFlash;
|
||||
|
||||
string_t m_iszMaster; // Master entity (game_team_master or multisource)
|
||||
|
||||
string_t m_soundStartRotate;
|
||||
string_t m_soundStopRotate;
|
||||
string_t m_soundLoopRotate;
|
||||
|
||||
float m_flPlayerGracePeriod;
|
||||
float m_flIgnoreGraceUpto;
|
||||
float m_flPlayerLockTimeBeforeFire;
|
||||
float m_flLastSawNonPlayer;
|
||||
|
||||
string_t m_targetEntityName;
|
||||
Vector m_vTargetPosition;
|
||||
Vector m_vecNPCIdleTarget;
|
||||
|
||||
// Used for when the gun is attached to another entity
|
||||
string_t m_iszBarrelAttachment;
|
||||
int m_nBarrelAttachment;
|
||||
string_t m_iszBaseAttachment;
|
||||
|
||||
// Used when the gun is actually a part of the parent entity, and pose params aim it
|
||||
string_t m_iszYawPoseParam;
|
||||
string_t m_iszPitchPoseParam;
|
||||
float m_flYawPoseCenter;
|
||||
float m_flPitchPoseCenter;
|
||||
bool m_bUsePoseParameters;
|
||||
|
||||
// Lead the target?
|
||||
bool m_bPerformLeading;
|
||||
float m_flStartLeadFactor;
|
||||
float m_flStartLeadFactorTime;
|
||||
float m_flNextLeadFactor;
|
||||
float m_flNextLeadFactorTime;
|
||||
|
||||
COutputEvent m_OnFire;
|
||||
COutputEvent m_OnLoseTarget;
|
||||
COutputEvent m_OnAquireTarget;
|
||||
COutputEvent m_OnAmmoDepleted;
|
||||
COutputEvent m_OnGotController;
|
||||
COutputEvent m_OnLostController;
|
||||
COutputEvent m_OnGotPlayerController;
|
||||
COutputEvent m_OnLostPlayerController;
|
||||
COutputEvent m_OnReadyToFire;
|
||||
|
||||
CHandle<CBaseTrigger> m_hControlVolume;
|
||||
string_t m_iszControlVolume;
|
||||
|
||||
float m_flNextControllerSearch;
|
||||
bool m_bShouldFindNPCs;
|
||||
bool m_bNPCInRoute;
|
||||
string_t m_iszNPCManPoint;
|
||||
|
||||
bool m_bReadyToFire;
|
||||
|
||||
int m_iEffectHandling;
|
||||
};
|
||||
|
||||
#endif // FUNC_TANK_H
|
||||
@@ -0,0 +1,253 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_ar2.h"
|
||||
#include "weapon_ar2.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "shake.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "ar2_explosion.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "world.h"
|
||||
|
||||
#ifdef PORTAL
|
||||
#include "portal_util_shared.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define AR2_GRENADE_MAX_DANGER_RADIUS 300
|
||||
|
||||
extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud
|
||||
|
||||
// Moved to HL2_SharedGameRules because these are referenced by shared AmmoDef functions
|
||||
extern ConVar sk_plr_dmg_smg1_grenade;
|
||||
extern ConVar sk_npc_dmg_smg1_grenade;
|
||||
extern ConVar sk_max_smg1_grenade;
|
||||
|
||||
ConVar sk_smg1_grenade_radius ( "sk_smg1_grenade_radius","0");
|
||||
|
||||
ConVar g_CV_SmokeTrail("smoke_trail", "1", 0); // temporary dust explosion switch
|
||||
|
||||
BEGIN_DATADESC( CGrenadeAR2 )
|
||||
|
||||
DEFINE_FIELD( m_hSmokeTrail, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_fSpawnTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_fDangerRadius, FIELD_FLOAT ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_ENTITYFUNC( GrenadeAR2Touch ),
|
||||
DEFINE_THINKFUNC( GrenadeAR2Think ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_ar2, CGrenadeAR2 );
|
||||
|
||||
void CGrenadeAR2::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
|
||||
// Hits everything but debris
|
||||
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
|
||||
|
||||
SetModel( "models/Weapons/ar2_grenade.mdl");
|
||||
UTIL_SetSize(this, Vector(-3, -3, -3), Vector(3, 3, 3));
|
||||
// UTIL_SetSize(this, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
SetUse( &CGrenadeAR2::DetonateUse );
|
||||
SetTouch( &CGrenadeAR2::GrenadeAR2Touch );
|
||||
SetThink( &CGrenadeAR2::GrenadeAR2Think );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() )
|
||||
{
|
||||
m_flDamage = sk_plr_dmg_smg1_grenade.GetFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flDamage = sk_npc_dmg_smg1_grenade.GetFloat();
|
||||
}
|
||||
|
||||
m_DmgRadius = sk_smg1_grenade_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_bIsLive = true;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for grenades to make them easier to see
|
||||
SetFriction( 0.8 );
|
||||
SetSequence( 0 );
|
||||
|
||||
m_fDangerRadius = 100;
|
||||
|
||||
m_fSpawnTime = gpGlobals->curtime;
|
||||
|
||||
// -------------
|
||||
// Smoke trail.
|
||||
// -------------
|
||||
if( g_CV_SmokeTrail.GetInt() && !IsXbox() )
|
||||
{
|
||||
m_hSmokeTrail = SmokeTrail::CreateSmokeTrail();
|
||||
|
||||
if( m_hSmokeTrail )
|
||||
{
|
||||
m_hSmokeTrail->m_SpawnRate = 48;
|
||||
m_hSmokeTrail->m_ParticleLifetime = 1;
|
||||
m_hSmokeTrail->m_StartColor.Init(0.1f, 0.1f, 0.1f);
|
||||
m_hSmokeTrail->m_EndColor.Init(0,0,0);
|
||||
m_hSmokeTrail->m_StartSize = 12;
|
||||
m_hSmokeTrail->m_EndSize = m_hSmokeTrail->m_StartSize * 4;
|
||||
m_hSmokeTrail->m_SpawnRadius = 4;
|
||||
m_hSmokeTrail->m_MinSpeed = 4;
|
||||
m_hSmokeTrail->m_MaxSpeed = 24;
|
||||
m_hSmokeTrail->m_Opacity = 0.2f;
|
||||
|
||||
m_hSmokeTrail->SetLifetime(10.0f);
|
||||
m_hSmokeTrail->FollowEntity(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The grenade has a slight delay before it goes live. That way the
|
||||
// person firing it can bounce it off a nearby wall. However if it
|
||||
// hits another character it blows up immediately
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeAR2::GrenadeAR2Think( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.05f );
|
||||
|
||||
if (!m_bIsLive)
|
||||
{
|
||||
// Go live after a short delay
|
||||
if (m_fSpawnTime + MAX_AR2_NO_COLLIDE_TIME < gpGlobals->curtime)
|
||||
{
|
||||
m_bIsLive = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If I just went solid and my velocity is zero, it means I'm resting on
|
||||
// the floor already when I went solid so blow up
|
||||
if (m_bIsLive)
|
||||
{
|
||||
if (GetAbsVelocity().Length() == 0.0 ||
|
||||
GetGroundEntity() != NULL )
|
||||
{
|
||||
Detonate();
|
||||
}
|
||||
}
|
||||
|
||||
// The old way of making danger sounds would scare the crap out of EVERYONE between you and where the grenade
|
||||
// was going to hit. The radius of the danger sound now 'blossoms' over the grenade's lifetime, making it seem
|
||||
// dangerous to a larger area downrange than it does from where it was fired.
|
||||
if( m_fDangerRadius <= AR2_GRENADE_MAX_DANGER_RADIUS )
|
||||
{
|
||||
m_fDangerRadius += ( AR2_GRENADE_MAX_DANGER_RADIUS * 0.05 );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, m_fDangerRadius, 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER );
|
||||
}
|
||||
|
||||
void CGrenadeAR2::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
void CGrenadeAR2::GrenadeAR2Touch( CBaseEntity *pOther )
|
||||
{
|
||||
Assert( pOther );
|
||||
if ( !pOther->IsSolid() )
|
||||
return;
|
||||
|
||||
// If I'm live go ahead and blow up
|
||||
if (m_bIsLive)
|
||||
{
|
||||
Detonate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If I'm not live, only blow up if I'm hitting an chacter that
|
||||
// is not the owner of the weapon
|
||||
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pOther );
|
||||
if (pBCC && GetThrower() != pBCC)
|
||||
{
|
||||
m_bIsLive = true;
|
||||
Detonate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeAR2::Detonate(void)
|
||||
{
|
||||
if (!m_bIsLive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_bIsLive = false;
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
if(m_hSmokeTrail)
|
||||
{
|
||||
UTIL_Remove(m_hSmokeTrail);
|
||||
m_hSmokeTrail = NULL;
|
||||
}
|
||||
|
||||
CPASFilter filter( GetAbsOrigin() );
|
||||
|
||||
te->Explosion( filter, 0.0,
|
||||
&GetAbsOrigin(),
|
||||
g_sModelIndexFireball,
|
||||
2.0,
|
||||
15,
|
||||
TE_EXPLFLAG_NONE,
|
||||
m_DmgRadius,
|
||||
m_flDamage );
|
||||
|
||||
Vector vecForward = GetAbsVelocity();
|
||||
VectorNormalize(vecForward);
|
||||
trace_t tr;
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + 60*vecForward, MASK_SHOT,
|
||||
this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
|
||||
if ((tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0))
|
||||
{
|
||||
// non-world needs smaller decals
|
||||
if( tr.m_pEnt && !tr.m_pEnt->IsNPC() )
|
||||
{
|
||||
UTIL_DecalTrace( &tr, "SmallScorch" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_DecalTrace( &tr, "Scorch" );
|
||||
}
|
||||
|
||||
UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
|
||||
|
||||
RadiusDamage ( CTakeDamageInfo( this, GetThrower(), m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
void CGrenadeAR2::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/Weapons/ar2_grenade.mdl");
|
||||
}
|
||||
|
||||
|
||||
CGrenadeAR2::CGrenadeAR2(void)
|
||||
{
|
||||
m_hSmokeTrail = NULL;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot from the AR2
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEAR2_H
|
||||
#define GRENADEAR2_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
#define MAX_AR2_NO_COLLIDE_TIME 0.2
|
||||
|
||||
class SmokeTrail;
|
||||
class CWeaponAR2;
|
||||
|
||||
class CGrenadeAR2 : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeAR2, CBaseGrenade );
|
||||
|
||||
CHandle< SmokeTrail > m_hSmokeTrail;
|
||||
float m_fSpawnTime;
|
||||
float m_fDangerRadius;
|
||||
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void GrenadeAR2Touch( CBaseEntity *pOther );
|
||||
void GrenadeAR2Think( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
public:
|
||||
void EXPORT Detonate(void);
|
||||
CGrenadeAR2(void);
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEAR2_H
|
||||
@@ -0,0 +1,443 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by mortar synth.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_beam.h"
|
||||
#include "beam_shared.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "decals.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define GRENADEBEAM_DEFAULTWIDTH 2.0
|
||||
|
||||
// ==============================================================================
|
||||
// > CGrenadeBeamChaser
|
||||
// ==============================================================================
|
||||
BEGIN_DATADESC( CGrenadeBeamChaser )
|
||||
|
||||
DEFINE_FIELD( m_pTarget, FIELD_CLASSPTR ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_FUNCTION( ChaserThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_beam_chaser, CGrenadeBeamChaser );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeamChaser::Spawn( void )
|
||||
{
|
||||
SetSolid( SOLID_NONE );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
SetThink(&CGrenadeBeamChaser::ChaserThink);
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeamChaser::ChaserThink( void )
|
||||
{
|
||||
Vector vTargetPos;
|
||||
m_pTarget->GetChaserTargetPos(&vTargetPos);
|
||||
Vector vTargetDir = (vTargetPos - GetLocalOrigin());
|
||||
|
||||
// -------------------------------------------------
|
||||
// Check to see if we'll pass our target this frame
|
||||
// If so get the next target
|
||||
// -------------------------------------------------
|
||||
float flTargetDist = vTargetDir.Length();
|
||||
if ((gpGlobals->frametime * m_pTarget->m_flBeamSpeed) > flTargetDist)
|
||||
{
|
||||
m_pTarget->GetNextTargetPos(&vTargetPos);
|
||||
vTargetDir = (vTargetPos - GetLocalOrigin());
|
||||
flTargetDist = vTargetDir.Length();
|
||||
}
|
||||
|
||||
if (flTargetDist != 0)
|
||||
{
|
||||
//--------------------------------------
|
||||
// Set our velocity to chase the target
|
||||
//--------------------------------------
|
||||
VectorNormalize(vTargetDir);
|
||||
SetAbsVelocity( vTargetDir * m_pTarget->m_flBeamSpeed );
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
CGrenadeBeamChaser* CGrenadeBeamChaser::ChaserCreate( CGrenadeBeam *pTarget )
|
||||
{
|
||||
CGrenadeBeamChaser *pChaser = (CGrenadeBeamChaser *)CreateEntityByName( "grenade_beam_chaser" );
|
||||
pChaser->SetLocalOrigin( pTarget->GetLocalOrigin() );
|
||||
pChaser->m_pTarget = pTarget;
|
||||
pChaser->Spawn();
|
||||
return pChaser;
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
// > CGrenadeBeam
|
||||
// ==============================================================================
|
||||
BEGIN_DATADESC( CGrenadeBeam )
|
||||
|
||||
DEFINE_FIELD( m_vLaunchPos, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_flBeamWidth, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flBeamSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flBeamLag, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flLaunchTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flLastTouchTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_hBeamChaser, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_nNumHits, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_ARRAY( m_pHitLocation, FIELD_VECTOR, GRENADEBEAM_MAXHITS ),
|
||||
DEFINE_ARRAY( m_pBeam, FIELD_CLASSPTR, GRENADEBEAM_MAXBEAMS ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_ENTITYFUNC( GrenadeBeamTouch ),
|
||||
DEFINE_THINKFUNC( KillBeam ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_beam, CGrenadeBeam );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
|
||||
//UNDONE/HACK: this model is never used but one is needed
|
||||
SetModel( "Models/weapons/flare.mdl" );
|
||||
AddEffects( EF_NODRAW );
|
||||
|
||||
SetTouch( &CGrenadeBeam::GrenadeBeamTouch );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
m_iHealth = 1;
|
||||
SetGravity( 0.0001 );
|
||||
m_nNumHits = 0;
|
||||
UTIL_SetSize( this, vec3_origin, vec3_origin );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
CGrenadeBeam* CGrenadeBeam::Create( CBaseEntity* pOwner, const Vector &vStart)
|
||||
{
|
||||
CGrenadeBeam *pEnergy = (CGrenadeBeam *)CreateEntityByName( "grenade_beam" );
|
||||
pEnergy->Spawn();
|
||||
pEnergy->SetOwnerEntity( pOwner );
|
||||
pEnergy->SetRenderColor( 255, 0, 0, 0 );
|
||||
pEnergy->m_flBeamWidth = GRENADEBEAM_DEFAULTWIDTH;
|
||||
UTIL_SetOrigin( pEnergy, vStart );
|
||||
|
||||
return pEnergy;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::Format(color32 clrColor, float flWidth)
|
||||
{
|
||||
m_clrRender = clrColor;
|
||||
m_flBeamWidth = flWidth;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::Shoot(Vector vDirection, float flSpeed, float flLifetime, float flLag, float flDamage )
|
||||
{
|
||||
SetThink ( &CGrenadeBeam::KillBeam );
|
||||
SetNextThink( gpGlobals->curtime + flLifetime );
|
||||
m_hBeamChaser = CGrenadeBeamChaser::ChaserCreate(this);
|
||||
m_flBeamSpeed = flSpeed;
|
||||
SetAbsVelocity( vDirection * flSpeed );
|
||||
m_flBeamLag = flLag;
|
||||
m_flDamage = flDamage;
|
||||
m_flLaunchTime = gpGlobals->curtime;
|
||||
m_vLaunchPos = GetAbsOrigin();
|
||||
m_flLastTouchTime = 0;
|
||||
CreateBeams();
|
||||
UpdateBeams();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::KillBeam(void)
|
||||
{
|
||||
SetThink(NULL);
|
||||
SetTouch(NULL);
|
||||
m_hBeamChaser->SetThink(NULL);
|
||||
UTIL_Remove(m_hBeamChaser);
|
||||
UTIL_Remove(this);
|
||||
|
||||
for (int i=0;i<GRENADEBEAM_MAXBEAMS;i++)
|
||||
{
|
||||
if (m_pBeam[i])
|
||||
{
|
||||
UTIL_Remove(m_pBeam[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::GrenadeBeamTouch( CBaseEntity *pOther )
|
||||
{
|
||||
//---------------------------------------------------------
|
||||
// Make sure I'm not caught in a corner, if so remove me
|
||||
//---------------------------------------------------------
|
||||
if (gpGlobals->curtime - m_flLastTouchTime < 0.01)
|
||||
{
|
||||
KillBeam();
|
||||
return;
|
||||
}
|
||||
m_flLastTouchTime = gpGlobals->curtime;
|
||||
|
||||
// ---------------------------------------
|
||||
// If I have room for another hit, add it
|
||||
// ---------------------------------------
|
||||
if (m_nNumHits < GRENADEBEAM_MAXHITS)
|
||||
{
|
||||
m_pHitLocation[m_nNumHits] = GetLocalOrigin();
|
||||
m_nNumHits++;
|
||||
}
|
||||
// Otherwise copy over old hit, and force chaser into last hit position
|
||||
else
|
||||
{
|
||||
m_hBeamChaser->SetLocalOrigin( m_pHitLocation[0] );
|
||||
for (int i=0;i<m_nNumHits-1;i++)
|
||||
{
|
||||
m_pHitLocation[i] = m_pHitLocation[i+1];
|
||||
}
|
||||
m_pHitLocation[m_nNumHits-1]=GetLocalOrigin();
|
||||
}
|
||||
UpdateBeams();
|
||||
|
||||
// --------------------------------------
|
||||
// Smoke or bubbles effect
|
||||
// --------------------------------------
|
||||
if (UTIL_PointContents ( GetAbsOrigin() ) & MASK_WATER)
|
||||
{
|
||||
UTIL_Bubbles(GetAbsOrigin()-Vector(3,3,3),GetAbsOrigin()+Vector(3,3,3),10);
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Smoke(GetAbsOrigin(), random->RandomInt(5, 10), 10);
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
// Play burn sounds
|
||||
// --------------------------------------------
|
||||
if (pOther->m_takedamage)
|
||||
{
|
||||
pOther->TakeDamage( CTakeDamageInfo( this, this, m_flDamage, DMG_BURN ) );
|
||||
KillBeam();
|
||||
return;
|
||||
}
|
||||
|
||||
EmitSound( "GrenadeBeam.HitSound" );
|
||||
|
||||
trace_t tr;
|
||||
Vector vDirection = GetAbsVelocity();
|
||||
VectorNormalize(vDirection);
|
||||
UTIL_TraceLine( GetAbsOrigin()-vDirection, GetAbsOrigin()+vDirection, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
|
||||
UTIL_DecalTrace( &tr, "RedGlowFade" );
|
||||
UTIL_ImpactTrace( &tr, DMG_ENERGYBEAM );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::GetNextTargetPos(Vector *vPosition)
|
||||
{
|
||||
// Only advance if tail launch time has passed
|
||||
if (gpGlobals->curtime - m_flLaunchTime > m_flBeamLag)
|
||||
{
|
||||
if (m_nNumHits > 0)
|
||||
{
|
||||
for (int i=0;i<m_nNumHits-1;i++)
|
||||
{
|
||||
m_pHitLocation[i] = m_pHitLocation[i+1];
|
||||
}
|
||||
m_nNumHits--;
|
||||
|
||||
UpdateBeams();
|
||||
}
|
||||
}
|
||||
GetChaserTargetPos(vPosition);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::GetChaserTargetPos(Vector *vPosition)
|
||||
{
|
||||
// -----------------------------
|
||||
// Launch chaser after a delay
|
||||
// -----------------------------
|
||||
if (gpGlobals->curtime - m_flLaunchTime < m_flBeamLag)
|
||||
{
|
||||
*vPosition = m_vLaunchPos;
|
||||
}
|
||||
else if (m_nNumHits > 0)
|
||||
{
|
||||
*vPosition = m_pHitLocation[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
*vPosition = GetLocalOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::CreateBeams(void)
|
||||
{
|
||||
for ( int i=0; i < GRENADEBEAM_MAXBEAMS; ++i )
|
||||
{
|
||||
m_pBeam[i] = CBeam::BeamCreate( "sprites/laser.vmt", m_flBeamWidth );
|
||||
m_pBeam[i]->SetColor( m_clrRender->r, m_clrRender->g, m_clrRender->b );
|
||||
m_pBeam[i]->EntsInit( this, m_hBeamChaser );
|
||||
m_pBeam[i]->SetBrightness( 255 );
|
||||
m_pBeam[i]->SetNoise( 1 );
|
||||
m_pBeam[i]->SetBeamFlag( FBEAM_SHADEIN );
|
||||
m_pBeam[i]->SetBeamFlag( FBEAM_SHADEOUT );
|
||||
}
|
||||
}
|
||||
/*
|
||||
void CGrenadeBeam::DebugBeams(void)
|
||||
{
|
||||
if (m_nNumHits > 0)
|
||||
{
|
||||
NDebugOverlay::Line(GetLocalOrigin(), m_pHitLocation[m_nNumHits-1], 255,255,25, true, 0.1);
|
||||
NDebugOverlay::Line(m_hBeamChaser->GetLocalOrigin(), m_pHitLocation[0], 255,255,25, true, 0.1);
|
||||
|
||||
for (int i=0;i<m_nNumHits-1;i++)
|
||||
{
|
||||
NDebugOverlay::Line(m_pHitLocation[i], m_pHitLocation[i+1], 255,255,25, true, 0.1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NDebugOverlay::Line(GetLocalOrigin(), m_hBeamChaser->GetLocalOrigin(), 255,255,25, true, 0.1);
|
||||
}
|
||||
|
||||
for (int i=0;i<m_nNumHits;i++)
|
||||
{
|
||||
NDebugOverlay::Cross3D(m_pHitLocation[i], Vector(-8,-8,-8),Vector(8,8,8),0,255,0,true,0.1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::UpdateBeams(void)
|
||||
{
|
||||
// ------------------------------------------------------------------
|
||||
// If no hits, draw a single beam between the grenade and the chaser
|
||||
// ------------------------------------------------------------------
|
||||
if (m_nNumHits == 0)
|
||||
{
|
||||
m_pBeam[0]->EntsInit( this, m_hBeamChaser );
|
||||
for (int i=1;i<GRENADEBEAM_MAXBEAMS;i++)
|
||||
{
|
||||
m_pBeam[i]->SetBrightness(0);
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Otherwise draw beams between hits
|
||||
// ------------------------------------------------------------------
|
||||
else
|
||||
{
|
||||
m_pBeam[0]->PointEntInit( m_pHitLocation[0], m_hBeamChaser );
|
||||
|
||||
for (int i=1;i<GRENADEBEAM_MAXBEAMS-1;i++)
|
||||
{
|
||||
if (i<m_nNumHits)
|
||||
{
|
||||
m_pBeam[i]->PointsInit(m_pHitLocation[i-1],m_pHitLocation[i]);
|
||||
m_pBeam[i]->SetBrightness(255);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pBeam[i]->SetBrightness(0);
|
||||
}
|
||||
}
|
||||
|
||||
m_pBeam[GRENADEBEAM_MAXBEAMS-1]->PointEntInit( m_pHitLocation[m_nNumHits-1], this );
|
||||
m_pBeam[GRENADEBEAM_MAXBEAMS-1]->SetBrightness(255);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeBeam::Precache( void )
|
||||
{
|
||||
PrecacheModel("sprites/laser.vmt");
|
||||
|
||||
//UNDONE/HACK: this model is never used but one is needed
|
||||
PrecacheModel("Models/weapons/flare.mdl");
|
||||
|
||||
PrecacheScriptSound( "GrenadeBeam.HitSound" );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Send even though we don't have a model
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
int CGrenadeBeam::UpdateTransmitState(void)
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_PVSCHECK );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by mortar synth
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEBEAM_H
|
||||
#define GRENADEBEAM_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
#define GRENADEBEAM_MAXBEAMS 2
|
||||
#define GRENADEBEAM_MAXHITS GRENADEBEAM_MAXBEAMS-1
|
||||
|
||||
class CGrenadeBeam;
|
||||
class CBeam;
|
||||
|
||||
// End of the grenade beam
|
||||
class CGrenadeBeamChaser : public CBaseAnimating
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeBeamChaser, CBaseAnimating );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
static CGrenadeBeamChaser* ChaserCreate( CGrenadeBeam *pTarget );
|
||||
|
||||
void Spawn( void );
|
||||
void ChaserThink();
|
||||
|
||||
CGrenadeBeam* m_pTarget;
|
||||
};
|
||||
|
||||
class CGrenadeBeam : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeBeam, CBaseGrenade );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
static CGrenadeBeam* Create( CBaseEntity* pOwner, const Vector &vStart);
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Format( color32 clrColor, float flWidth);
|
||||
void GrenadeBeamTouch( CBaseEntity *pOther );
|
||||
void KillBeam();
|
||||
void CreateBeams(void);
|
||||
void UpdateBeams(void);
|
||||
//void DebugBeams(void);
|
||||
void GetChaserTargetPos(Vector *vPosition);
|
||||
void GetNextTargetPos(Vector *vPosition);
|
||||
int UpdateTransmitState(void);
|
||||
void Shoot(Vector vDirection, float flSpeed, float flLifetime, float flLag, float flDamage );
|
||||
|
||||
Vector m_vLaunchPos;
|
||||
float m_flBeamWidth;
|
||||
float m_flBeamSpeed;
|
||||
float m_flBeamLag;
|
||||
float m_flLaunchTime;
|
||||
float m_flLastTouchTime;
|
||||
EHANDLE m_hBeamChaser;
|
||||
int m_nNumHits;
|
||||
Vector m_pHitLocation[GRENADEBEAM_MAXHITS];
|
||||
CBeam* m_pBeam[GRENADEBEAM_MAXBEAMS];
|
||||
};
|
||||
|
||||
#endif //GRENADEBEAM_H
|
||||
@@ -0,0 +1,274 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Things thrown from the hand
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "ammodef.h"
|
||||
#include "gamerules.h"
|
||||
#include "grenade_brickbat.h"
|
||||
#include "weapon_brickbat.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "IEffects.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// Global Savedata for changelevel trigger
|
||||
BEGIN_DATADESC( CGrenade_Brickbat )
|
||||
|
||||
DEFINE_FIELD( m_nType, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_bExplodes, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bBounceToFlat, FIELD_BOOLEAN ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( BrickbatTouch ),
|
||||
DEFINE_FUNCTION( BrickbatThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( brickbat, CGrenade_Brickbat );
|
||||
|
||||
void CGrenade_Brickbat::Spawn( void )
|
||||
{
|
||||
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
|
||||
SetTouch( BrickbatTouch );
|
||||
SetThink( BrickbatThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( 1.0 );
|
||||
SetSequence( 1 );
|
||||
|
||||
CreateVPhysics();
|
||||
}
|
||||
|
||||
bool CGrenade_Brickbat::CreateVPhysics()
|
||||
{
|
||||
VPhysicsInitNormal( SOLID_VPHYSICS, 0, false );
|
||||
IPhysicsObject *pPhysics = VPhysicsGetObject();
|
||||
if ( pPhysics )
|
||||
{
|
||||
// we want world touches
|
||||
unsigned int flags = pPhysics->GetCallbackFlags();
|
||||
pPhysics->SetCallbackFlags( flags | CALLBACK_GLOBAL_TOUCH_STATIC );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenade_Brickbat::BrickbatTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// -----------------------------------------------------------
|
||||
// Might be physically simulated so get my velocity manually
|
||||
// -----------------------------------------------------------
|
||||
Vector vVelocity;
|
||||
GetVelocity(&vVelocity,NULL);
|
||||
|
||||
// -----------------------------------
|
||||
// Do damage if we moving fairly fast
|
||||
// -----------------------------------
|
||||
if (vVelocity.Length() > 100)
|
||||
{
|
||||
if (GetThrower())
|
||||
{
|
||||
trace_t tr;
|
||||
tr = CBaseEntity::GetTouchTrace( );
|
||||
ClearMultiDamage( );
|
||||
Vector forward;
|
||||
AngleVectors( GetLocalAngles(), &forward );
|
||||
|
||||
CTakeDamageInfo info( this, GetThrower(), m_flDamage, DMG_CRUSH );
|
||||
CalculateMeleeDamageForce( &info, forward, tr.endpos );
|
||||
pOther->DispatchTraceAttack( info, forward, &tr );
|
||||
ApplyMultiDamage();
|
||||
}
|
||||
// If this thrown item explodes, blow it up
|
||||
if (m_bExplodes)
|
||||
{
|
||||
Detonate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (pOther->GetFlags() & FL_CLIENT)
|
||||
{
|
||||
SpawnBrickbatWeapon();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Brickbat grenade turns back into a brickbat weapon
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenade_Brickbat::SpawnBrickbatWeapon( void )
|
||||
{
|
||||
CWeaponBrickbat *pBrickbat = (CWeaponBrickbat*)CBaseEntity::CreateNoSpawn(
|
||||
"weapon_brickbat", GetLocalOrigin(), GetLocalAngles(), NULL );
|
||||
// Spawn after we set the ammo type so the correct model is used
|
||||
if (pBrickbat)
|
||||
{
|
||||
pBrickbat->m_iCurrentAmmoType = m_nType;
|
||||
pBrickbat->Spawn();
|
||||
VPhysicsDestroyObject();
|
||||
SetThink(NULL);
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenade_Brickbat::BrickbatThink( void )
|
||||
{
|
||||
// -----------------------------------------------------------
|
||||
// Might be physically simulated so get my velocity manually
|
||||
// -----------------------------------------------------------
|
||||
Vector vVelocity;
|
||||
AngularImpulse vAngVel;
|
||||
GetVelocity(&vVelocity,&vAngVel);
|
||||
|
||||
// See if I can lose my owner (has dropper moved out of way?)
|
||||
// Want do this so owner can throw the brickbat
|
||||
if (GetOwnerEntity())
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vUpABit = GetAbsOrigin();
|
||||
vUpABit.z += 5.0;
|
||||
|
||||
CBaseEntity* saveOwner = GetOwnerEntity();
|
||||
SetOwnerEntity( NULL );
|
||||
UTIL_TraceEntity( this, GetAbsOrigin(), vUpABit, MASK_SOLID, &tr );
|
||||
if ( tr.startsolid || tr.fraction != 1.0 )
|
||||
{
|
||||
SetOwnerEntity( saveOwner );
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Make sure we're not resting on a living thing's bounding box
|
||||
// ---------------------------------------------------------------
|
||||
if (vVelocity.Length() < 0.01)
|
||||
{
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.fraction < 1.0 && tr.m_pEnt)
|
||||
{
|
||||
CBaseEntity *pEntity = tr.m_pEnt;
|
||||
if (pEntity->GetFlags() & (FL_CLIENT | FL_NPC))
|
||||
{
|
||||
// --------------------
|
||||
// Bounce me off
|
||||
// --------------------
|
||||
Vector vNewVel;
|
||||
vNewVel.y = 100;
|
||||
vNewVel.x = random->RandomInt(-100,100);
|
||||
vNewVel.z = random->RandomInt(-100,100);
|
||||
|
||||
// If physically simulated
|
||||
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
||||
if ( pPhysicsObject )
|
||||
{
|
||||
pPhysicsObject->AddVelocity( &vNewVel, &vAngVel );
|
||||
}
|
||||
// Otherwise
|
||||
else
|
||||
{
|
||||
SetAbsVelocity( vNewVel );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vVelocity.Length() < 0.01)
|
||||
{
|
||||
SpawnBrickbatWeapon();
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
//=====================================================================
|
||||
// > Rock
|
||||
//=====================================================================
|
||||
class CGrenadeRockBB : public CGrenade_Brickbat
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeRockBB, CGrenade_Brickbat );
|
||||
|
||||
void Spawn(void)
|
||||
{
|
||||
m_nType = BRICKBAT_ROCK;
|
||||
SetModel( "models/props_junk/Rock001a.mdl" );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel("models/props_junk/Rock001a.mdl");
|
||||
BaseClass::Precache();
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( grenade_rockbb, CGrenadeRockBB );
|
||||
PRECACHE_REGISTER(grenade_rockbb);
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// > BeerBottle
|
||||
//=====================================================================
|
||||
class CGrenadeBottle : public CGrenade_Brickbat
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeBottle, CGrenade_Brickbat );
|
||||
|
||||
void Spawn(void)
|
||||
{
|
||||
m_nType = BRICKBAT_BOTTLE;
|
||||
m_bExplodes = true;
|
||||
SetModel( "models/weapons/w_bb_bottle.mdl" );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void );
|
||||
void Detonate( void );
|
||||
};
|
||||
|
||||
void CGrenadeBottle::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/weapons/w_bb_bottle.mdl");
|
||||
|
||||
PrecacheScriptSound( "GrenadeBottle.Detonate" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
void CGrenadeBottle::Detonate( void )
|
||||
{
|
||||
trace_t trace;
|
||||
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity(), MASK_SOLID, this, COLLISION_GROUP_NONE, &trace);
|
||||
UTIL_DecalTrace( &trace, "BeerSplash" );
|
||||
|
||||
EmitSound( "GrenadeBottle.Detonate" );
|
||||
|
||||
CSoundEnt::InsertSound(SOUND_COMBAT, GetAbsOrigin(), 400, 0.5);
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_beerbottle, CGrenadeBottle );
|
||||
PRECACHE_REGISTER(grenade_beerbottle);
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Things thrown from the hand
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEBRICKBAT_H
|
||||
#define GRENADEBRICKBAT_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
enum BrickbatAmmo_t;
|
||||
|
||||
class CGrenade_Brickbat : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenade_Brickbat, CBaseGrenade );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void SpawnBrickbatWeapon( void );
|
||||
virtual void Detonate( void ) { return;};
|
||||
virtual bool CreateVPhysics();
|
||||
void BrickbatTouch( CBaseEntity *pOther );
|
||||
void BrickbatThink( void );
|
||||
|
||||
BrickbatAmmo_t m_nType;
|
||||
bool m_bExplodes;
|
||||
bool m_bBounceToFlat; // Bouncing to flatten
|
||||
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEBRICKBAT_H
|
||||
@@ -0,0 +1,326 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Bugbait weapon to summon and direct antlions
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_bugbait.h"
|
||||
#include "decals.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "soundent.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "npc_bullseye.h"
|
||||
#include "entitylist.h"
|
||||
#include "antlion_maker.h"
|
||||
#include "eventqueue.h"
|
||||
|
||||
#ifdef PORTAL
|
||||
#include "portal_util_shared.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// Setup the sensor list template
|
||||
|
||||
CEntityClassList<CBugBaitSensor> g_BugBaitSensorList;
|
||||
template <> CBugBaitSensor *CEntityClassList<CBugBaitSensor>::m_pClassList = NULL;
|
||||
|
||||
CBugBaitSensor* GetBugBaitSensorList()
|
||||
{
|
||||
return g_BugBaitSensorList.m_pClassList;
|
||||
}
|
||||
|
||||
CBugBaitSensor::CBugBaitSensor( void )
|
||||
{
|
||||
g_BugBaitSensorList.Insert( this );
|
||||
}
|
||||
|
||||
CBugBaitSensor::~CBugBaitSensor( void )
|
||||
{
|
||||
g_BugBaitSensorList.Remove( this );
|
||||
}
|
||||
|
||||
BEGIN_DATADESC( CBugBaitSensor )
|
||||
|
||||
// This is re-set up in the constructor
|
||||
//DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
|
||||
|
||||
DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "Enabled" ),
|
||||
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_OUTPUT( m_OnBaited, "OnBaited" ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( point_bugbait, CBugBaitSensor );
|
||||
|
||||
//=============================================================================
|
||||
// Bugbait grenade
|
||||
//=============================================================================
|
||||
|
||||
#define GRENADE_MODEL "models/weapons/w_bugbait.mdl"
|
||||
|
||||
BEGIN_DATADESC( CGrenadeBugBait )
|
||||
|
||||
DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_pSporeTrail, FIELD_CLASSPTR ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_ENTITYFUNC( BugBaitTouch ),
|
||||
DEFINE_THINKFUNC( ThinkBecomeSolid ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_grenade_bugbait, CGrenadeBugBait );
|
||||
|
||||
//Radius of the bugbait's effect on other creatures
|
||||
ConVar bugbait_radius( "bugbait_radius", "512" );
|
||||
ConVar bugbait_hear_radius( "bugbait_hear_radius", "2500" );
|
||||
ConVar bugbait_distract_time( "bugbait_distract_time", "5" );
|
||||
ConVar bugbait_grenade_radius( "bugbait_grenade_radius", "150" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeBugBait::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetModel( GRENADE_MODEL );
|
||||
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_DEFAULT );
|
||||
SetSolid( SOLID_BBOX );
|
||||
|
||||
UTIL_SetSize( this, Vector( -2, -2, -2), Vector( 2, 2, 2 ) );
|
||||
|
||||
SetTouch( &CGrenadeBugBait::BugBaitTouch );
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
m_pSporeTrail = NULL;
|
||||
|
||||
/*
|
||||
m_pSporeTrail = SporeTrail::CreateSporeTrail();
|
||||
|
||||
m_pSporeTrail->m_bEmit = true;
|
||||
m_pSporeTrail->m_flSpawnRate = 100.0f;
|
||||
m_pSporeTrail->m_flParticleLifetime = 1.0f;
|
||||
m_pSporeTrail->m_flStartSize = 1.0f;
|
||||
m_pSporeTrail->m_flEndSize = 1.0f;
|
||||
m_pSporeTrail->m_flSpawnRadius = 8.0f;
|
||||
|
||||
m_pSporeTrail->m_vecEndColor = Vector( 0, 0, 0 );
|
||||
*/
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeBugBait::Precache( void )
|
||||
{
|
||||
PrecacheModel( GRENADE_MODEL );
|
||||
|
||||
PrecacheScriptSound( "GrenadeBugBait.Splat" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
#define NUM_SPLASHES 6
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeBugBait::BugBaitTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// Don't hit triggers or water
|
||||
Assert( pOther );
|
||||
if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) )
|
||||
return;
|
||||
|
||||
if ( m_pSporeTrail != NULL )
|
||||
{
|
||||
m_pSporeTrail->m_bEmit = false;
|
||||
}
|
||||
|
||||
//Do effect for the hit
|
||||
SporeExplosion *pSporeExplosion = SporeExplosion::CreateSporeExplosion();
|
||||
|
||||
if ( pSporeExplosion )
|
||||
{
|
||||
Vector dir = -GetAbsVelocity();
|
||||
VectorNormalize( dir );
|
||||
|
||||
QAngle angles;
|
||||
VectorAngles( dir, angles );
|
||||
|
||||
pSporeExplosion->SetLocalAngles( angles );
|
||||
pSporeExplosion->SetLocalOrigin( GetAbsOrigin() );
|
||||
pSporeExplosion->m_flSpawnRate = 8.0f;
|
||||
pSporeExplosion->m_flParticleLifetime = 2.0f;
|
||||
pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f );
|
||||
|
||||
pSporeExplosion->m_flStartSize = 32.0f;
|
||||
pSporeExplosion->m_flEndSize = 64.0f;
|
||||
pSporeExplosion->m_flSpawnRadius = 32.0f;
|
||||
|
||||
pSporeExplosion->SetLifetime( bugbait_distract_time.GetFloat() );
|
||||
}
|
||||
|
||||
trace_t tr;
|
||||
Vector traceDir = GetAbsVelocity();
|
||||
|
||||
VectorNormalize( traceDir );
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + traceDir * 64, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.fraction < 1.0f )
|
||||
{
|
||||
UTIL_DecalTrace( &tr, "BeerSplash" ); //TODO: Use real decal
|
||||
}
|
||||
|
||||
//Make a splat sound
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "GrenadeBugBait.Splat" );
|
||||
|
||||
//Make sure we want to call antlions
|
||||
if ( ActivateBugbaitTargets( GetThrower(), GetAbsOrigin(), false ) == false )
|
||||
{
|
||||
//Alert any antlions around
|
||||
CSoundEnt::InsertSound( SOUND_BUGBAIT, GetAbsOrigin(), bugbait_hear_radius.GetInt(), bugbait_distract_time.GetFloat(), GetThrower() );
|
||||
}
|
||||
|
||||
// Tell all spawners to now fight to this position
|
||||
g_AntlionMakerManager.BroadcastFightGoal( GetAbsOrigin() );
|
||||
|
||||
//Go away
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Activate any nearby bugbait targets
|
||||
// Returns true if the bugbait target wants to suppress the call.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CGrenadeBugBait::ActivateBugbaitTargets( CBaseEntity *pOwner, Vector vecOrigin, bool bSqueezed )
|
||||
{
|
||||
//Attempt to activate any spawners in a radius around the bugbait
|
||||
CBaseEntity* pList[100];
|
||||
Vector delta( bugbait_grenade_radius.GetFloat(), bugbait_grenade_radius.GetFloat(), bugbait_grenade_radius.GetFloat() );
|
||||
bool suppressCall = false;
|
||||
|
||||
int count = UTIL_EntitiesInBox( pList, 100, vecOrigin - delta, vecOrigin + delta, 0 );
|
||||
|
||||
// If the bugbait's been thrown, look for nearby targets to affect
|
||||
if ( !bSqueezed )
|
||||
{
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
// If close enough, make combine soldiers freak out when hit
|
||||
if ( UTIL_DistApprox( pList[i]->WorldSpaceCenter(), vecOrigin ) < bugbait_grenade_radius.GetFloat() )
|
||||
{
|
||||
// Must be a soldier
|
||||
if ( FClassnameIs( pList[i], "npc_combine_s") )
|
||||
{
|
||||
CAI_BaseNPC *pCombine = pList[i]->MyNPCPointer();
|
||||
|
||||
if ( pCombine != NULL )
|
||||
{
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( vecOrigin, pCombine->EyePosition(), MASK_ALL, pOwner, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if ( tr.fraction == 1.0 || tr.m_pEnt == pCombine )
|
||||
{
|
||||
// Randomize the start time a little so multiple combine hit by
|
||||
// the same bugbait don't all dance in synch.
|
||||
g_EventQueue.AddEvent( pCombine, "HitByBugbait", RandomFloat(0, 0.5), pOwner, pOwner );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Iterate over all sensors to see if they detected this impact
|
||||
for ( CBugBaitSensor *pSensor = GetBugBaitSensorList(); pSensor != NULL; pSensor = pSensor->m_pNext )
|
||||
{
|
||||
if ( pSensor == NULL )
|
||||
continue;
|
||||
|
||||
if ( pSensor->IsDisabled() )
|
||||
continue;
|
||||
|
||||
if ( bSqueezed && pSensor->DetectsSqueeze() == false )
|
||||
continue;
|
||||
|
||||
if ( !bSqueezed && pSensor->DetectsThrown() == false )
|
||||
continue;
|
||||
|
||||
//Make sure we're within range of the sensor
|
||||
if ( pSensor->GetRadius() > ( pSensor->GetAbsOrigin() - vecOrigin ).Length() )
|
||||
{
|
||||
//Tell the sensor it's been hit
|
||||
if ( pSensor->Baited( pOwner ) )
|
||||
{
|
||||
//If we're suppressing the call to antlions, then don't make a bugbait sound
|
||||
if ( pSensor->SuppressCall() )
|
||||
{
|
||||
suppressCall = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suppressCall;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeBugBait::ThinkBecomeSolid( void )
|
||||
{
|
||||
SetThink( NULL );
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : duration -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeBugBait::SetGracePeriod( float duration )
|
||||
{
|
||||
SetThink( &CGrenadeBugBait::ThinkBecomeSolid );
|
||||
SetNextThink( gpGlobals->curtime + duration );
|
||||
|
||||
// Become unsolid
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &position -
|
||||
// &angles -
|
||||
// &velocity -
|
||||
// &angVelocity -
|
||||
// *owner -
|
||||
// Output : CBaseGrenade
|
||||
//-----------------------------------------------------------------------------
|
||||
CGrenadeBugBait *BugBaitGrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const QAngle &angVelocity, CBaseEntity *owner )
|
||||
{
|
||||
CGrenadeBugBait *pGrenade = (CGrenadeBugBait *) CBaseEntity::Create( "npc_grenade_bugbait", position, angles, owner );
|
||||
|
||||
if ( pGrenade != NULL )
|
||||
{
|
||||
pGrenade->SetLocalAngularVelocity( angVelocity );
|
||||
pGrenade->SetAbsVelocity( velocity );
|
||||
pGrenade->SetThrower( ToBaseCombatCharacter( owner ) );
|
||||
}
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADE_BUGBAIT_H
|
||||
#define GRENADE_BUGBAIT_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "smoke_trail.h"
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
//Radius of the bugbait's effect on other creatures
|
||||
extern ConVar bugbait_radius;
|
||||
extern ConVar bugbait_hear_radius;
|
||||
extern ConVar bugbait_distract_time;
|
||||
extern ConVar bugbait_grenade_radius;
|
||||
|
||||
#define SF_BUGBAIT_SUPPRESS_CALL 0x00000001
|
||||
#define SF_BUGBAIT_NOT_THROWN 0x00000002 // Don't detect player throwing the bugbait near this point
|
||||
#define SF_BUGBAIT_NOT_SQUEEZE 0x00000004 // Don't detect player squeezing the bugbait
|
||||
|
||||
//=============================================================================
|
||||
// Bugbait sensor
|
||||
//=============================================================================
|
||||
|
||||
class CBugBaitSensor : public CPointEntity
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS( CBugBaitSensor, CPointEntity );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CBugBaitSensor( void );
|
||||
~CBugBaitSensor( void );
|
||||
|
||||
bool Baited( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !m_bEnabled )
|
||||
return false;
|
||||
|
||||
m_OnBaited.FireOutput( pOther, this );
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputEnable( inputdata_t &data )
|
||||
{
|
||||
m_bEnabled = true;
|
||||
}
|
||||
|
||||
void InputDisable( inputdata_t &data )
|
||||
{
|
||||
m_bEnabled = false;
|
||||
}
|
||||
|
||||
void InputToggle( inputdata_t &data )
|
||||
{
|
||||
m_bEnabled = !m_bEnabled;
|
||||
}
|
||||
|
||||
bool SuppressCall( void )
|
||||
{
|
||||
return ( HasSpawnFlags( SF_BUGBAIT_SUPPRESS_CALL ) );
|
||||
}
|
||||
|
||||
bool DetectsSqueeze( void )
|
||||
{
|
||||
return ( !HasSpawnFlags( SF_BUGBAIT_NOT_SQUEEZE ) );
|
||||
}
|
||||
|
||||
bool DetectsThrown( void )
|
||||
{
|
||||
return ( !HasSpawnFlags( SF_BUGBAIT_NOT_THROWN ) );
|
||||
}
|
||||
|
||||
float GetRadius( void ) const
|
||||
{
|
||||
if ( m_flRadius == 0 )
|
||||
return bugbait_radius.GetFloat();
|
||||
|
||||
return m_flRadius;
|
||||
}
|
||||
|
||||
bool IsDisabled( void ) const
|
||||
{
|
||||
return !m_bEnabled;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
float m_flRadius;
|
||||
bool m_bEnabled;
|
||||
COutputEvent m_OnBaited;
|
||||
|
||||
public:
|
||||
CBugBaitSensor *m_pNext;
|
||||
};
|
||||
|
||||
//
|
||||
// Bug Bait Grenade
|
||||
//
|
||||
|
||||
class CGrenadeBugBait : public CBaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CGrenadeBugBait, CBaseGrenade );
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void ThinkBecomeSolid( void );
|
||||
void SetGracePeriod( float duration );
|
||||
|
||||
void BugBaitTouch( CBaseEntity *pOther );
|
||||
|
||||
// Activate nearby bugbait targets
|
||||
static bool ActivateBugbaitTargets( CBaseEntity *pOwner, Vector vecOrigin, bool bSqueezed );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
protected:
|
||||
void CreateTarget( const Vector &position, CBaseEntity *pOther );
|
||||
|
||||
float m_flGracePeriodEndsAt;
|
||||
|
||||
SporeTrail *m_pSporeTrail;
|
||||
};
|
||||
|
||||
extern CGrenadeBugBait *BugBaitGrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const QAngle &angVelocity, CBaseEntity *owner );
|
||||
|
||||
#endif // GRENADE_BUGBAIT_H
|
||||
@@ -0,0 +1,157 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by mortar synth.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_energy.h"
|
||||
#include "soundent.h"
|
||||
#include "player.h"
|
||||
#include "hl2_shareddefs.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define ENERGY_GRENADE_LIFETIME 1
|
||||
|
||||
ConVar sk_dmg_energy_grenade ( "sk_dmg_energy_grenade","0");
|
||||
ConVar sk_energy_grenade_radius ( "sk_energy_grenade_radius","0");
|
||||
|
||||
BEGIN_DATADESC( CGrenadeEnergy )
|
||||
|
||||
DEFINE_FIELD( m_flMaxFrame, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_nEnergySprite, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flLaunchTime, FIELD_TIME ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_FUNCTION( Animate ),
|
||||
DEFINE_FUNCTION( GrenadeEnergyTouch ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_energy, CGrenadeEnergy );
|
||||
|
||||
void CGrenadeEnergy::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
|
||||
SetModel( "Models/weapons/w_energy_grenade.mdl" );
|
||||
|
||||
SetUse( DetonateUse );
|
||||
SetTouch( GrenadeEnergyTouch );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flDamage = sk_dmg_energy_grenade.GetFloat();
|
||||
m_DmgRadius = sk_energy_grenade_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
m_flCycle = 0;
|
||||
m_flLaunchTime = gpGlobals->curtime;
|
||||
|
||||
SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
|
||||
|
||||
UTIL_SetSize( this, vec3_origin, vec3_origin );
|
||||
|
||||
m_flMaxFrame = (float) modelinfo->GetModelFrameCount( GetModel() ) - 1;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeEnergy::Shoot( CBaseEntity* pOwner, const Vector &vStart, Vector vVelocity )
|
||||
{
|
||||
CGrenadeEnergy *pEnergy = (CGrenadeEnergy *)CreateEntityByName( "grenade_energy" );
|
||||
pEnergy->Spawn();
|
||||
|
||||
UTIL_SetOrigin( pEnergy, vStart );
|
||||
pEnergy->SetAbsVelocity( vVelocity );
|
||||
pEnergy->SetOwnerEntity( pOwner );
|
||||
|
||||
pEnergy->SetThink ( Animate );
|
||||
pEnergy->SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
pEnergy->m_nRenderMode = kRenderTransAdd;
|
||||
pEnergy->SetRenderColor( 160, 160, 160, 255 );
|
||||
pEnergy->m_nRenderFX = kRenderFxNone;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeEnergy::Animate( void )
|
||||
{
|
||||
float flLifeLeft = 1-(gpGlobals->curtime - m_flLaunchTime)/ENERGY_GRENADE_LIFETIME;
|
||||
|
||||
if (flLifeLeft < 0)
|
||||
{
|
||||
SetRenderColorA( 0 );
|
||||
SetThink(NULL);
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.01f );
|
||||
|
||||
QAngle angles;
|
||||
VectorAngles( GetAbsVelocity(), angles );
|
||||
SetLocalAngles( angles );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
StudioFrameAdvance( );
|
||||
|
||||
SetRenderColorA( flLifeLeft );
|
||||
}
|
||||
|
||||
void CGrenadeEnergy::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
void CGrenadeEnergy::GrenadeEnergyTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->m_takedamage )
|
||||
{
|
||||
float flLifeLeft = 1-(gpGlobals->curtime - m_flLaunchTime)/ENERGY_GRENADE_LIFETIME;
|
||||
|
||||
if ( pOther->GetFlags() & (FL_CLIENT) )
|
||||
{
|
||||
CBasePlayer *pPlayer = ( CBasePlayer * )pOther;
|
||||
float flKick = 120 * flLifeLeft;
|
||||
pPlayer->m_Local.m_vecPunchAngle.SetX( flKick * (random->RandomInt(0,1) == 1) ? -1 : 1 );
|
||||
pPlayer->m_Local.m_vecPunchAngle.SetY( flKick * (random->RandomInt(0,1) == 1) ? -1 : 1 );
|
||||
}
|
||||
float flDamage = m_flDamage * flLifeLeft;
|
||||
if (flDamage < 1)
|
||||
{
|
||||
flDamage = 1;
|
||||
}
|
||||
|
||||
trace_t tr;
|
||||
tr = GetTouchTrace();
|
||||
CTakeDamageInfo info( this, GetThrower(), m_flDamage * flLifeLeft, DMG_SONIC );
|
||||
CalculateMeleeDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos );
|
||||
pOther->TakeDamage( info );
|
||||
}
|
||||
Detonate();
|
||||
}
|
||||
|
||||
void CGrenadeEnergy::Detonate(void)
|
||||
{
|
||||
m_takedamage = DAMAGE_NO;
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
void CGrenadeEnergy::Precache( void )
|
||||
{
|
||||
PrecacheModel("Models/weapons/w_energy_grenade.mdl");
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by mortar synth
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEENERGY_H
|
||||
#define GRENADEENERGY_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
class CGrenadeEnergy : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeEnergy, CBaseGrenade );
|
||||
|
||||
static void Shoot( CBaseEntity* pOwner, const Vector &vStart, Vector vVelocity );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Animate( void );
|
||||
void GrenadeEnergyTouch( CBaseEntity *pOther );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
int m_flMaxFrame;
|
||||
int m_nEnergySprite;
|
||||
float m_flLaunchTime; // When was this thing launched
|
||||
|
||||
void EXPORT Detonate(void);
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEENERGY_H
|
||||
@@ -0,0 +1,453 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "basegrenade_shared.h"
|
||||
#include "grenade_frag.h"
|
||||
#include "Sprite.h"
|
||||
#include "SpriteTrail.h"
|
||||
#include "soundent.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define FRAG_GRENADE_BLIP_FREQUENCY 1.0f
|
||||
#define FRAG_GRENADE_BLIP_FAST_FREQUENCY 0.3f
|
||||
|
||||
#define FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP 1.5f
|
||||
#define FRAG_GRENADE_WARN_TIME 1.5f
|
||||
|
||||
const float GRENADE_COEFFICIENT_OF_RESTITUTION = 0.2f;
|
||||
|
||||
ConVar sk_plr_dmg_fraggrenade ( "sk_plr_dmg_fraggrenade","0");
|
||||
ConVar sk_npc_dmg_fraggrenade ( "sk_npc_dmg_fraggrenade","0");
|
||||
ConVar sk_fraggrenade_radius ( "sk_fraggrenade_radius", "0");
|
||||
|
||||
#define GRENADE_MODEL "models/Weapons/w_grenade.mdl"
|
||||
|
||||
class CGrenadeFrag : public CBaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CGrenadeFrag, CBaseGrenade );
|
||||
|
||||
#if !defined( CLIENT_DLL )
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
|
||||
~CGrenadeFrag( void );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void OnRestore( void );
|
||||
void Precache( void );
|
||||
bool CreateVPhysics( void );
|
||||
void CreateEffects( void );
|
||||
void SetTimer( float detonateDelay, float warnDelay );
|
||||
void SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity );
|
||||
int OnTakeDamage( const CTakeDamageInfo &inputInfo );
|
||||
void BlipSound() { EmitSound( "Grenade.Blip" ); }
|
||||
void DelayThink();
|
||||
void VPhysicsUpdate( IPhysicsObject *pPhysics );
|
||||
void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
|
||||
void SetCombineSpawned( bool combineSpawned ) { m_combineSpawned = combineSpawned; }
|
||||
bool IsCombineSpawned( void ) const { return m_combineSpawned; }
|
||||
void SetPunted( bool punt ) { m_punted = punt; }
|
||||
bool WasPunted( void ) const { return m_punted; }
|
||||
|
||||
// this function only used in episodic.
|
||||
#if defined(HL2_EPISODIC) && 0 // FIXME: HandleInteraction() is no longer called now that base grenade derives from CBaseAnimating
|
||||
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
|
||||
#endif
|
||||
|
||||
void InputSetTimer( inputdata_t &inputdata );
|
||||
|
||||
protected:
|
||||
CHandle<CSprite> m_pMainGlow;
|
||||
CHandle<CSpriteTrail> m_pGlowTrail;
|
||||
|
||||
float m_flNextBlipTime;
|
||||
bool m_inSolid;
|
||||
bool m_combineSpawned;
|
||||
bool m_punted;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_grenade_frag, CGrenadeFrag );
|
||||
|
||||
BEGIN_DATADESC( CGrenadeFrag )
|
||||
|
||||
// Fields
|
||||
DEFINE_FIELD( m_pMainGlow, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_flNextBlipTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_inSolid, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_combineSpawned, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_punted, FIELD_BOOLEAN ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_THINKFUNC( DelayThink ),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTimer", InputSetTimer ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CGrenadeFrag::~CGrenadeFrag( void )
|
||||
{
|
||||
}
|
||||
|
||||
void CGrenadeFrag::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( GRENADE_MODEL );
|
||||
|
||||
if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() )
|
||||
{
|
||||
m_flDamage = sk_plr_dmg_fraggrenade.GetFloat();
|
||||
m_DmgRadius = sk_fraggrenade_radius.GetFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flDamage = sk_npc_dmg_fraggrenade.GetFloat();
|
||||
m_DmgRadius = sk_fraggrenade_radius.GetFloat();
|
||||
}
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetSize( -Vector(4,4,4), Vector(4,4,4) );
|
||||
SetCollisionGroup( COLLISION_GROUP_WEAPON );
|
||||
CreateVPhysics();
|
||||
|
||||
BlipSound();
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY;
|
||||
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
|
||||
m_combineSpawned = false;
|
||||
m_punted = false;
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeFrag::OnRestore( void )
|
||||
{
|
||||
// If we were primed and ready to detonate, put FX on us.
|
||||
if (m_flDetonateTime > 0)
|
||||
CreateEffects();
|
||||
|
||||
BaseClass::OnRestore();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeFrag::CreateEffects( void )
|
||||
{
|
||||
// Start up the eye glow
|
||||
m_pMainGlow = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin(), false );
|
||||
|
||||
int nAttachment = LookupAttachment( "fuse" );
|
||||
|
||||
if ( m_pMainGlow != NULL )
|
||||
{
|
||||
m_pMainGlow->FollowEntity( this );
|
||||
m_pMainGlow->SetAttachment( this, nAttachment );
|
||||
m_pMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation );
|
||||
m_pMainGlow->SetScale( 0.2f );
|
||||
m_pMainGlow->SetGlowProxySize( 4.0f );
|
||||
}
|
||||
|
||||
// Start up the eye trail
|
||||
m_pGlowTrail = CSpriteTrail::SpriteTrailCreate( "sprites/bluelaser1.vmt", GetLocalOrigin(), false );
|
||||
|
||||
if ( m_pGlowTrail != NULL )
|
||||
{
|
||||
m_pGlowTrail->FollowEntity( this );
|
||||
m_pGlowTrail->SetAttachment( this, nAttachment );
|
||||
m_pGlowTrail->SetTransparency( kRenderTransAdd, 255, 0, 0, 255, kRenderFxNone );
|
||||
m_pGlowTrail->SetStartWidth( 8.0f );
|
||||
m_pGlowTrail->SetEndWidth( 1.0f );
|
||||
m_pGlowTrail->SetLifeTime( 0.5f );
|
||||
}
|
||||
}
|
||||
|
||||
bool CGrenadeFrag::CreateVPhysics()
|
||||
{
|
||||
// Create the object in the physics system
|
||||
VPhysicsInitNormal( SOLID_BBOX, 0, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
// this will hit only things that are in newCollisionGroup, but NOT in collisionGroupAlreadyChecked
|
||||
class CTraceFilterCollisionGroupDelta : public CTraceFilterEntitiesOnly
|
||||
{
|
||||
public:
|
||||
// It does have a base, but we'll never network anything below here..
|
||||
DECLARE_CLASS_NOBASE( CTraceFilterCollisionGroupDelta );
|
||||
|
||||
CTraceFilterCollisionGroupDelta( const IHandleEntity *passentity, int collisionGroupAlreadyChecked, int newCollisionGroup )
|
||||
: m_pPassEnt(passentity), m_collisionGroupAlreadyChecked( collisionGroupAlreadyChecked ), m_newCollisionGroup( newCollisionGroup )
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
|
||||
{
|
||||
if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
|
||||
return false;
|
||||
CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
|
||||
|
||||
if ( pEntity )
|
||||
{
|
||||
if ( g_pGameRules->ShouldCollide( m_collisionGroupAlreadyChecked, pEntity->GetCollisionGroup() ) )
|
||||
return false;
|
||||
if ( g_pGameRules->ShouldCollide( m_newCollisionGroup, pEntity->GetCollisionGroup() ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
const IHandleEntity *m_pPassEnt;
|
||||
int m_collisionGroupAlreadyChecked;
|
||||
int m_newCollisionGroup;
|
||||
};
|
||||
|
||||
void CGrenadeFrag::VPhysicsUpdate( IPhysicsObject *pPhysics )
|
||||
{
|
||||
BaseClass::VPhysicsUpdate( pPhysics );
|
||||
Vector vel;
|
||||
AngularImpulse angVel;
|
||||
pPhysics->GetVelocity( &vel, &angVel );
|
||||
|
||||
Vector start = GetAbsOrigin();
|
||||
// find all entities that my collision group wouldn't hit, but COLLISION_GROUP_NONE would and bounce off of them as a ray cast
|
||||
CTraceFilterCollisionGroupDelta filter( this, GetCollisionGroup(), COLLISION_GROUP_NONE );
|
||||
trace_t tr;
|
||||
|
||||
// UNDONE: Hull won't work with hitboxes - hits outer hull. But the whole point of this test is to hit hitboxes.
|
||||
#if 0
|
||||
UTIL_TraceHull( start, start + vel * gpGlobals->frametime, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr );
|
||||
#else
|
||||
UTIL_TraceLine( start, start + vel * gpGlobals->frametime, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr );
|
||||
#endif
|
||||
if ( tr.startsolid )
|
||||
{
|
||||
if ( !m_inSolid )
|
||||
{
|
||||
// UNDONE: Do a better contact solution that uses relative velocity?
|
||||
vel *= -GRENADE_COEFFICIENT_OF_RESTITUTION; // bounce backwards
|
||||
pPhysics->SetVelocity( &vel, NULL );
|
||||
}
|
||||
m_inSolid = true;
|
||||
return;
|
||||
}
|
||||
m_inSolid = false;
|
||||
if ( tr.DidHit() )
|
||||
{
|
||||
Vector dir = vel;
|
||||
VectorNormalize(dir);
|
||||
// send a tiny amount of damage so the character will react to getting bonked
|
||||
CTakeDamageInfo info( this, GetThrower(), pPhysics->GetMass() * vel, GetAbsOrigin(), 0.1f, DMG_CRUSH );
|
||||
tr.m_pEnt->TakeDamage( info );
|
||||
|
||||
// reflect velocity around normal
|
||||
vel = -2.0f * tr.plane.normal * DotProduct(vel,tr.plane.normal) + vel;
|
||||
|
||||
// absorb 80% in impact
|
||||
vel *= GRENADE_COEFFICIENT_OF_RESTITUTION;
|
||||
angVel *= -0.5f;
|
||||
pPhysics->SetVelocity( &vel, &angVel );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeFrag::Precache( void )
|
||||
{
|
||||
PrecacheModel( GRENADE_MODEL );
|
||||
|
||||
PrecacheScriptSound( "Grenade.Blip" );
|
||||
|
||||
PrecacheModel( "sprites/redglow1.vmt" );
|
||||
PrecacheModel( "sprites/bluelaser1.vmt" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
void CGrenadeFrag::SetTimer( float detonateDelay, float warnDelay )
|
||||
{
|
||||
m_flDetonateTime = gpGlobals->curtime + detonateDelay;
|
||||
m_flWarnAITime = gpGlobals->curtime + warnDelay;
|
||||
SetThink( &CGrenadeFrag::DelayThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
|
||||
CreateEffects();
|
||||
}
|
||||
|
||||
void CGrenadeFrag::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
|
||||
{
|
||||
SetThrower( pPhysGunUser );
|
||||
|
||||
#ifdef HL2MP
|
||||
SetTimer( FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP, FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP / 2);
|
||||
|
||||
BlipSound();
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY;
|
||||
m_bHasWarnedAI = true;
|
||||
#else
|
||||
if( IsX360() )
|
||||
{
|
||||
// Give 'em a couple of seconds to aim and throw.
|
||||
SetTimer( 2.0f, 1.0f);
|
||||
BlipSound();
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HL2_EPISODIC
|
||||
SetPunted( true );
|
||||
#endif
|
||||
|
||||
BaseClass::OnPhysGunPickup( pPhysGunUser, reason );
|
||||
}
|
||||
|
||||
void CGrenadeFrag::DelayThink()
|
||||
{
|
||||
if( gpGlobals->curtime > m_flDetonateTime )
|
||||
{
|
||||
Detonate();
|
||||
return;
|
||||
}
|
||||
|
||||
if( !m_bHasWarnedAI && gpGlobals->curtime >= m_flWarnAITime )
|
||||
{
|
||||
#if !defined( CLIENT_DLL )
|
||||
CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 1.5, this );
|
||||
#endif
|
||||
m_bHasWarnedAI = true;
|
||||
}
|
||||
|
||||
if( gpGlobals->curtime > m_flNextBlipTime )
|
||||
{
|
||||
BlipSound();
|
||||
|
||||
if( m_bHasWarnedAI )
|
||||
{
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY;
|
||||
}
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
|
||||
void CGrenadeFrag::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity )
|
||||
{
|
||||
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
||||
if ( pPhysicsObject )
|
||||
{
|
||||
pPhysicsObject->AddVelocity( &velocity, &angVelocity );
|
||||
}
|
||||
}
|
||||
|
||||
int CGrenadeFrag::OnTakeDamage( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
// Manually apply vphysics because BaseCombatCharacter takedamage doesn't call back to CBaseEntity OnTakeDamage
|
||||
VPhysicsTakeDamage( inputInfo );
|
||||
|
||||
// Grenades only suffer blast damage and burn damage.
|
||||
if( !(inputInfo.GetDamageType() & (DMG_BLAST|DMG_BURN) ) )
|
||||
return 0;
|
||||
|
||||
return BaseClass::OnTakeDamage( inputInfo );
|
||||
}
|
||||
|
||||
#if defined(HL2_EPISODIC) && 0 // FIXME: HandleInteraction() is no longer called now that base grenade derives from CBaseAnimating
|
||||
extern int g_interactionBarnacleVictimGrab; ///< usually declared in ai_interactions.h but no reason to haul all of that in here.
|
||||
extern int g_interactionBarnacleVictimBite;
|
||||
extern int g_interactionBarnacleVictimReleased;
|
||||
bool CGrenadeFrag::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
|
||||
{
|
||||
// allow fragnades to be grabbed by barnacles.
|
||||
if ( interactionType == g_interactionBarnacleVictimGrab )
|
||||
{
|
||||
// give the grenade another five seconds seconds so the player can have the satisfaction of blowing up the barnacle with it
|
||||
float timer = m_flDetonateTime - gpGlobals->curtime + 5.0f;
|
||||
SetTimer( timer, timer - FRAG_GRENADE_WARN_TIME );
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( interactionType == g_interactionBarnacleVictimBite )
|
||||
{
|
||||
// detonate the grenade immediately
|
||||
SetTimer( 0, 0 );
|
||||
return true;
|
||||
}
|
||||
else if ( interactionType == g_interactionBarnacleVictimReleased )
|
||||
{
|
||||
// take the five seconds back off the timer.
|
||||
float timer = MAX(m_flDetonateTime - gpGlobals->curtime - 5.0f,0.0f);
|
||||
SetTimer( timer, timer - FRAG_GRENADE_WARN_TIME );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CGrenadeFrag::InputSetTimer( inputdata_t &inputdata )
|
||||
{
|
||||
SetTimer( inputdata.value.Float(), inputdata.value.Float() - FRAG_GRENADE_WARN_TIME );
|
||||
}
|
||||
|
||||
CBaseGrenade *Fraggrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer, bool combineSpawned )
|
||||
{
|
||||
// Don't set the owner here, or the player can't interact with grenades he's thrown
|
||||
CGrenadeFrag *pGrenade = (CGrenadeFrag *)CBaseEntity::Create( "npc_grenade_frag", position, angles, pOwner );
|
||||
|
||||
pGrenade->SetTimer( timer, timer - FRAG_GRENADE_WARN_TIME );
|
||||
pGrenade->SetVelocity( velocity, angVelocity );
|
||||
pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) );
|
||||
pGrenade->m_takedamage = DAMAGE_EVENTS_ONLY;
|
||||
pGrenade->SetCombineSpawned( combineSpawned );
|
||||
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
bool Fraggrenade_WasPunted( const CBaseEntity *pEntity )
|
||||
{
|
||||
const CGrenadeFrag *pFrag = dynamic_cast<const CGrenadeFrag *>( pEntity );
|
||||
if ( pFrag )
|
||||
{
|
||||
return pFrag->WasPunted();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Fraggrenade_WasCreatedByCombine( const CBaseEntity *pEntity )
|
||||
{
|
||||
const CGrenadeFrag *pFrag = dynamic_cast<const CGrenadeFrag *>( pEntity );
|
||||
if ( pFrag )
|
||||
{
|
||||
return pFrag->IsCombineSpawned();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADE_FRAG_H
|
||||
#define GRENADE_FRAG_H
|
||||
#pragma once
|
||||
|
||||
class CBaseGrenade;
|
||||
struct edict_t;
|
||||
|
||||
CBaseGrenade *Fraggrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer, bool combineSpawned );
|
||||
bool Fraggrenade_WasPunted( const CBaseEntity *pEntity );
|
||||
bool Fraggrenade_WasCreatedByCombine( const CBaseEntity *pEntity );
|
||||
|
||||
#endif // GRENADE_FRAG_H
|
||||
@@ -0,0 +1,696 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Grenade used by the city scanner
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_homer.h"
|
||||
#include "weapon_ar2.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "shake.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "ar2_explosion.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "game.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "hl2_shareddefs.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "movevars_shared.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define HOMER_TRAIL0_LIFE 0.1
|
||||
#define HOMER_TRAIL1_LIFE 0.2
|
||||
#define HOMER_TRAIL2_LIFE 3.0// 1.0
|
||||
|
||||
extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud
|
||||
|
||||
ConVar sk_dmg_homer_grenade( "sk_dmg_homer_grenade","0" );
|
||||
ConVar sk_homer_grenade_radius( "sk_homer_grenade_radius","0" );
|
||||
|
||||
BEGIN_DATADESC( CGrenadeHomer )
|
||||
|
||||
DEFINE_ARRAY( m_hRocketTrail, FIELD_EHANDLE, 3 ),
|
||||
DEFINE_FIELD( m_sFlySound, FIELD_STRING),
|
||||
DEFINE_FIELD( m_flNextFlySoundTime, FIELD_TIME),
|
||||
|
||||
DEFINE_FIELD( m_flHomingStrength, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flHomingDelay, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flHomingRampUp, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flHomingDuration, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flHomingRampDown, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flHomingSpeed, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flSpinMagnitude, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_flSpinSpeed, FIELD_FLOAT),
|
||||
DEFINE_FIELD( m_nRocketTrailType, FIELD_INTEGER),
|
||||
|
||||
// DEFINE_FIELD( m_spriteTexture, FIELD_INTEGER),
|
||||
|
||||
DEFINE_FIELD( m_flHomingLaunchTime, FIELD_TIME),
|
||||
DEFINE_FIELD( m_flHomingStartTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flHomingEndTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flSpinOffset, FIELD_FLOAT),
|
||||
|
||||
DEFINE_FIELD( m_hTarget, FIELD_EHANDLE),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_THINKFUNC( AimThink ),
|
||||
DEFINE_ENTITYFUNC( GrenadeHomerTouch ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_homer, CGrenadeHomer );
|
||||
|
||||
|
||||
///------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
CGrenadeHomer* CGrenadeHomer::CreateGrenadeHomer( string_t sModelName, string_t sFlySound, const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner )
|
||||
{
|
||||
CGrenadeHomer *pGrenade = (CGrenadeHomer*)CreateEntityByName( "grenade_homer" );
|
||||
if ( !pGrenade )
|
||||
{
|
||||
Warning( "NULL Ent in Create!\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( pGrenade->edict() )
|
||||
{
|
||||
pGrenade->m_sFlySound = sFlySound;
|
||||
pGrenade->SetOwnerEntity( Instance( pentOwner ) );
|
||||
pGrenade->SetLocalOrigin( vecOrigin );
|
||||
pGrenade->SetLocalAngles( vecAngles );
|
||||
pGrenade->SetModel( STRING(sModelName) );
|
||||
}
|
||||
return pGrenade;
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeHomer::Precache( void )
|
||||
{
|
||||
m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" );
|
||||
|
||||
PrecacheScriptSound( "GrenadeHomer.StopSounds" );
|
||||
if ( NULL_STRING != m_sFlySound )
|
||||
{
|
||||
PrecacheScriptSound( STRING(m_sFlySound) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
|
||||
UTIL_SetSize(this, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
m_flDamage = sk_dmg_homer_grenade.GetFloat();
|
||||
m_DmgRadius = sk_homer_grenade_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( 1.0 );
|
||||
SetFriction( 0.8 );
|
||||
SetSequence( 1 );
|
||||
|
||||
m_flHomingStrength = 0;
|
||||
m_flHomingDelay = 0;
|
||||
m_flHomingDuration = 0;
|
||||
|
||||
SetCollisionGroup( HL2COLLISION_GROUP_HOMING_MISSILE );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::SetSpin(float flSpinMagnitude, float flSpinSpeed)
|
||||
{
|
||||
m_flSpinMagnitude = flSpinMagnitude;
|
||||
m_flSpinSpeed = flSpinSpeed;
|
||||
m_flSpinOffset = random->RandomInt(-m_flSpinSpeed,m_flSpinSpeed);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::SetHoming(float flStrength, float flDelay, float flRampUp, float flDuration, float flRampDown)
|
||||
{
|
||||
m_flHomingStrength = flStrength;
|
||||
m_flHomingDelay = flDelay;
|
||||
m_flHomingRampUp = flRampUp;
|
||||
m_flHomingDuration = flDuration;
|
||||
m_flHomingRampDown = flRampDown;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::StartRocketTrail(void)
|
||||
{
|
||||
RocketTrail *pRocketTrail = RocketTrail::CreateRocketTrail();
|
||||
if(pRocketTrail)
|
||||
{
|
||||
pRocketTrail->m_SpawnRate = 80;
|
||||
pRocketTrail->m_ParticleLifetime = 2;
|
||||
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
|
||||
{
|
||||
pRocketTrail->m_StartColor.Init(0.5, 0.0, 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
pRocketTrail->m_StartColor.Init(0.75, 0.75, 0.75);
|
||||
}
|
||||
pRocketTrail->m_Opacity = 0.35f;
|
||||
pRocketTrail->m_EndColor.Init(0.4,0.4,0.4);
|
||||
pRocketTrail->m_StartSize = 8;
|
||||
pRocketTrail->m_EndSize = 16;
|
||||
pRocketTrail->m_SpawnRadius = 3;
|
||||
pRocketTrail->m_MinSpeed = 2;
|
||||
pRocketTrail->m_MaxSpeed = 10;
|
||||
pRocketTrail->SetLifetime(120);
|
||||
pRocketTrail->FollowEntity(this);
|
||||
|
||||
m_hRocketTrail[0] = pRocketTrail;
|
||||
}
|
||||
/*
|
||||
pRocketTrail = RocketTrail::CreateRocketTrail();
|
||||
if(pRocketTrail)
|
||||
{
|
||||
pRocketTrail->m_SpawnRate = 100;
|
||||
pRocketTrail->m_ParticleLifetime = HOMER_TRAIL1_LIFE;
|
||||
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
|
||||
{
|
||||
pRocketTrail->m_StartColor.Init(0.0, 0.0, 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
pRocketTrail->m_StartColor.Init(0.5, 0.5, 0.0);
|
||||
}
|
||||
pRocketTrail->m_EndColor.Init(0.5,0.5,0.5);
|
||||
pRocketTrail->m_StartSize = 3;
|
||||
pRocketTrail->m_EndSize = 6;
|
||||
pRocketTrail->m_SpawnRadius = 1;
|
||||
pRocketTrail->m_MinSpeed = 15;
|
||||
pRocketTrail->m_MaxSpeed = 25;
|
||||
pRocketTrail->SetLifetime(120);
|
||||
pRocketTrail->FollowEntity(this);
|
||||
|
||||
m_hRocketTrail[1] = pRocketTrail;
|
||||
}
|
||||
pRocketTrail = RocketTrail::CreateRocketTrail();
|
||||
if(pRocketTrail)
|
||||
{
|
||||
pRocketTrail->m_SpawnRate = 50;
|
||||
pRocketTrail->m_ParticleLifetime = HOMER_TRAIL2_LIFE;
|
||||
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
|
||||
{
|
||||
pRocketTrail->m_StartColor.Init(0.1, 0.0, 0.1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pRocketTrail->m_StartColor.Init(0.1, 0.1, 0.1);
|
||||
}
|
||||
pRocketTrail->m_EndColor.Init(0.5,0.5,0.5);
|
||||
pRocketTrail->m_StartSize = 8;
|
||||
pRocketTrail->m_EndSize = 20;
|
||||
pRocketTrail->m_SpawnRadius = 1;
|
||||
pRocketTrail->m_MinSpeed = 15;
|
||||
pRocketTrail->m_MaxSpeed = 25;
|
||||
pRocketTrail->SetLifetime(120);
|
||||
pRocketTrail->FollowEntity(this);
|
||||
|
||||
m_hRocketTrail[2] = pRocketTrail;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::UpdateRocketTrail(float fScale)
|
||||
{
|
||||
if (m_hRocketTrail[0] == NULL)
|
||||
{
|
||||
StartRocketTrail();
|
||||
}
|
||||
|
||||
if (m_hRocketTrail[0])
|
||||
{
|
||||
m_hRocketTrail[0]->m_ParticleLifetime = fScale*HOMER_TRAIL0_LIFE;
|
||||
}
|
||||
if (m_hRocketTrail[1])
|
||||
{
|
||||
m_hRocketTrail[1]->m_ParticleLifetime = fScale*HOMER_TRAIL1_LIFE;
|
||||
}
|
||||
|
||||
if (m_hRocketTrail[2])
|
||||
{
|
||||
m_hRocketTrail[2]->m_ParticleLifetime = fScale*HOMER_TRAIL2_LIFE;
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeHomer::StopRocketTrail()
|
||||
{
|
||||
// Stop emitting smoke
|
||||
for (int i=0;i<3;i++)
|
||||
{
|
||||
if(m_hRocketTrail[i])
|
||||
{
|
||||
m_hRocketTrail[i]->SetEmit(false);
|
||||
UTIL_Remove( m_hRocketTrail[i] );
|
||||
m_hRocketTrail[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::Launch( CBaseEntity* pOwner,
|
||||
CBaseEntity* pTarget,
|
||||
const Vector& vInitVelocity,
|
||||
float flHomingSpeed,
|
||||
float flGravity,
|
||||
int nRocketTrailType)
|
||||
{
|
||||
SetOwnerEntity( pOwner );
|
||||
m_hTarget = pTarget;
|
||||
SetAbsVelocity( vInitVelocity );
|
||||
m_flHomingSpeed = flHomingSpeed;
|
||||
SetGravity( flGravity );
|
||||
m_nRocketTrailType = nRocketTrailType;
|
||||
|
||||
// ----------------------------
|
||||
// Initialize homing parameters
|
||||
// ----------------------------
|
||||
m_flHomingLaunchTime = gpGlobals->curtime;
|
||||
|
||||
// -------------
|
||||
// Smoke trail.
|
||||
// -------------
|
||||
if ( (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ON) || (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN) )
|
||||
{
|
||||
StartRocketTrail();
|
||||
}
|
||||
|
||||
SetUse( &CGrenadeHomer::DetonateUse );
|
||||
SetTouch( &CGrenadeHomer::GrenadeHomerTouch );
|
||||
SetThink( &CGrenadeHomer::AimThink );
|
||||
AimThink();
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
|
||||
// Issue danger!
|
||||
if ( pTarget )
|
||||
{
|
||||
// Figure out how long it'll take for me to reach the target.
|
||||
float flDist = ( pTarget->WorldSpaceCenter() - WorldSpaceCenter() ).Length();
|
||||
float flTime = MAX( 0.5, flDist / GetAbsVelocity().Length() );
|
||||
|
||||
CSoundEnt::InsertSound ( SOUND_DANGER, m_hTarget->GetAbsOrigin(), 300, flTime, pOwner );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
void CGrenadeHomer::GrenadeHomerTouch( CBaseEntity *pOther )
|
||||
{
|
||||
Assert( pOther );
|
||||
|
||||
// Don't take damage from other homing grenades so can shoot in vollies
|
||||
if (FClassnameIs( pOther, "grenade_homer") || !pOther->IsSolid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// If I hit the sky, don't explode
|
||||
// ----------------------------------
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity(), MASK_SOLID_BRUSHONLY,
|
||||
this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if (tr.surface.flags & SURF_SKY)
|
||||
{
|
||||
StopRocketTrail();
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
Detonate();
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeHomer::Detonate(void)
|
||||
{
|
||||
StopRocketTrail();
|
||||
|
||||
StopSound(entindex(), CHAN_BODY, STRING(m_sFlySound));
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
CPASFilter filter( GetAbsOrigin() );
|
||||
|
||||
te->Explosion( filter, 0.0,
|
||||
&GetAbsOrigin(),
|
||||
g_sModelIndexFireball,
|
||||
2.0,
|
||||
15,
|
||||
TE_EXPLFLAG_NONE,
|
||||
m_DmgRadius,
|
||||
m_flDamage );
|
||||
|
||||
// int magnitude = 1.0;
|
||||
// int colorRamp = random->RandomInt( 128, 255 );
|
||||
|
||||
|
||||
if ( m_nRocketTrailType == HOMER_SMOKE_TRAIL_ALIEN )
|
||||
{
|
||||
// Add a shockring
|
||||
CBroadcastRecipientFilter filter3;
|
||||
te->BeamRingPoint( filter3, 0,
|
||||
GetAbsOrigin(), //origin
|
||||
16, //start radius
|
||||
1000, //end radius
|
||||
m_spriteTexture, //texture
|
||||
0, //halo index
|
||||
0, //start frame
|
||||
2, //framerate
|
||||
0.3f, //life
|
||||
128, //width
|
||||
16, //spread
|
||||
0, //amplitude
|
||||
100, //r
|
||||
0, //g
|
||||
200, //b
|
||||
50, //a
|
||||
128 //speed
|
||||
);
|
||||
|
||||
|
||||
// Add a shockring
|
||||
CBroadcastRecipientFilter filter4;
|
||||
te->BeamRingPoint( filter4, 0,
|
||||
GetAbsOrigin(), //origin
|
||||
16, //start radius
|
||||
500, //end radius
|
||||
m_spriteTexture, //texture
|
||||
0, //halo index
|
||||
0, //start frame
|
||||
2, //framerate
|
||||
0.3f, //life
|
||||
128, //width
|
||||
16, //spread
|
||||
0, //amplitude
|
||||
200, //r
|
||||
0, //g
|
||||
100, //b
|
||||
50, //a
|
||||
128 //speed
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Vector vecForward = GetAbsVelocity();
|
||||
VectorNormalize(vecForward);
|
||||
trace_t tr;
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + 60*vecForward, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr);
|
||||
|
||||
UTIL_DecalTrace( &tr, "Scorch" );
|
||||
|
||||
UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
|
||||
|
||||
RadiusDamage ( CTakeDamageInfo( this, GetOwnerEntity(), m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
|
||||
CPASAttenuationFilter filter2( this, "GrenadeHomer.StopSounds" );
|
||||
EmitSound( filter2, entindex(), "GrenadeHomer.StopSounds" );
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeHomer::PlayFlySound(void)
|
||||
{
|
||||
if (gpGlobals->curtime > m_flNextFlySoundTime)
|
||||
{
|
||||
CPASAttenuationFilter filter( this, 0.8 );
|
||||
|
||||
EmitSound_t ep;
|
||||
ep.m_nChannel = CHAN_BODY;
|
||||
ep.m_pSoundName = STRING(m_sFlySound);
|
||||
ep.m_flVolume = 1.0f;
|
||||
ep.m_SoundLevel = SNDLVL_NORM;
|
||||
ep.m_nPitch = 100;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
|
||||
m_flNextFlySoundTime = gpGlobals->curtime + 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Move toward targetmap
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadeHomer::AimThink( void )
|
||||
{
|
||||
// Blow up the missile if we have an explicit detonate time that
|
||||
// has been reached
|
||||
if (m_flDetonateTime != 0 &&
|
||||
gpGlobals->curtime > m_flDetonateTime)
|
||||
{
|
||||
Detonate();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayFlySound();
|
||||
|
||||
Vector vTargetPos = vec3_origin;
|
||||
Vector vTargetDir;
|
||||
float flCurHomingStrength = 0;
|
||||
|
||||
// ------------------------------------------------
|
||||
// If I'm homing
|
||||
// ------------------------------------------------
|
||||
if (m_hTarget != NULL)
|
||||
{
|
||||
vTargetPos = m_hTarget->EyePosition();
|
||||
vTargetDir = vTargetPos - GetAbsOrigin();
|
||||
VectorNormalize(vTargetDir);
|
||||
|
||||
// --------------------------------------------------
|
||||
// If my target is far away do some primitive
|
||||
// obstacle avoidance
|
||||
// --------------------------------------------------
|
||||
if ((vTargetPos - GetAbsOrigin()).Length() > 200)
|
||||
{
|
||||
Vector vTravelDir = GetAbsVelocity();
|
||||
VectorNormalize(vTravelDir);
|
||||
vTravelDir *= 50;
|
||||
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTravelDir, MASK_SHOT, m_hTarget, COLLISION_GROUP_NONE, &tr );
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
// Head off in normal
|
||||
float dotPr = DotProduct(vTravelDir,tr.plane.normal);
|
||||
Vector vBounce = -dotPr * tr.plane.normal;
|
||||
vBounce.z = 0;
|
||||
VectorNormalize(vBounce);
|
||||
vTargetDir += vBounce;
|
||||
VectorNormalize(vTargetDir);
|
||||
// DEBUG TOOL
|
||||
//NDebugOverlay::Line(GetOrigin(), GetOrigin()+vTravelDir, 255,0,0, true, 20);
|
||||
//NDebugOverlay::Line(GetOrigin(), GetOrigin()+(12*tr.plane.normal), 0,0,255, true, 20);
|
||||
//NDebugOverlay::Line(GetOrigin(), GetOrigin()+(vTargetDir), 0,255,0, true, 20);
|
||||
}
|
||||
}
|
||||
|
||||
float flTargetSpeed = GetAbsVelocity().Length();
|
||||
float flHomingRampUpStartTime = m_flHomingLaunchTime + m_flHomingDelay;
|
||||
float flHomingSustainStartTime = flHomingRampUpStartTime + m_flHomingRampUp;
|
||||
float flHomingRampDownStartTime = flHomingSustainStartTime + m_flHomingDuration;
|
||||
float flHomingEndHomingTime = flHomingRampDownStartTime + m_flHomingRampDown;
|
||||
// ---------
|
||||
// Delay
|
||||
// ---------
|
||||
if (gpGlobals->curtime < flHomingRampUpStartTime)
|
||||
{
|
||||
flCurHomingStrength = 0;
|
||||
flTargetSpeed = 0;
|
||||
}
|
||||
// ----------
|
||||
// Ramp Up
|
||||
// ----------
|
||||
else if (gpGlobals->curtime < flHomingSustainStartTime)
|
||||
{
|
||||
float flAge = gpGlobals->curtime - flHomingRampUpStartTime;
|
||||
flCurHomingStrength = m_flHomingStrength * (flAge/m_flHomingRampUp);
|
||||
flTargetSpeed = flCurHomingStrength * m_flHomingSpeed;
|
||||
}
|
||||
// ----------
|
||||
// Sustain
|
||||
// ----------
|
||||
else if (gpGlobals->curtime < flHomingRampDownStartTime)
|
||||
{
|
||||
flCurHomingStrength = m_flHomingStrength;
|
||||
flTargetSpeed = m_flHomingSpeed;
|
||||
}
|
||||
// -----------
|
||||
// Ramp Down
|
||||
// -----------
|
||||
else if (gpGlobals->curtime < flHomingEndHomingTime)
|
||||
{
|
||||
float flAge = gpGlobals->curtime - flHomingRampDownStartTime;
|
||||
flCurHomingStrength = m_flHomingStrength * (1-(flAge/m_flHomingRampDown));
|
||||
flTargetSpeed = m_flHomingSpeed;
|
||||
}
|
||||
// ---------------
|
||||
// Set Homing
|
||||
// ---------------
|
||||
if (flCurHomingStrength > 0)
|
||||
{
|
||||
// -------------
|
||||
// Smoke trail.
|
||||
// -------------
|
||||
if (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ON_HOMING)
|
||||
{
|
||||
UpdateRocketTrail(flCurHomingStrength);
|
||||
}
|
||||
|
||||
// Extract speed and direction
|
||||
Vector vCurDir = GetAbsVelocity();
|
||||
float flCurSpeed = VectorNormalize(vCurDir);
|
||||
flTargetSpeed = MAX(flTargetSpeed, flCurSpeed);
|
||||
|
||||
// Add in homing direction
|
||||
Vector vecNewVelocity = GetAbsVelocity();
|
||||
float flTimeToUse = gpGlobals->frametime;
|
||||
while (flTimeToUse > 0)
|
||||
{
|
||||
vecNewVelocity = (flCurHomingStrength * vTargetDir) + ((1 - flCurHomingStrength) * vCurDir);
|
||||
flTimeToUse = -0.1;
|
||||
}
|
||||
VectorNormalize(vecNewVelocity);
|
||||
vecNewVelocity *= flTargetSpeed;
|
||||
SetAbsVelocity( vecNewVelocity );
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Add time-coherent noise to the current velocity
|
||||
// ----------------------------------------------------------------------------------------
|
||||
Vector vecImpulse( 0, 0, 0 );
|
||||
if (m_flSpinMagnitude > 0)
|
||||
{
|
||||
vecImpulse.x += m_flSpinMagnitude*sin(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
|
||||
vecImpulse.y += m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
|
||||
vecImpulse.z -= m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset);
|
||||
}
|
||||
|
||||
// Add in gravity
|
||||
vecImpulse.z -= GetGravity() * GetCurrentGravity() * gpGlobals->frametime;
|
||||
ApplyAbsVelocityImpulse( vecImpulse );
|
||||
|
||||
QAngle angles;
|
||||
VectorAngles( GetAbsVelocity(), angles );
|
||||
SetLocalAngles( angles );
|
||||
|
||||
#if 0 // BUBBLE
|
||||
if( gpGlobals->curtime > m_flNextWarnTime )
|
||||
{
|
||||
// Make a bubble of warning sound in front of me.
|
||||
const float WARN_INTERVAL = 0.25f;
|
||||
float flSpeed = GetAbsVelocity().Length();
|
||||
Vector vecWarnLocation;
|
||||
|
||||
// warn a little bit ahead of us, please.
|
||||
vecWarnLocation = GetAbsOrigin() + GetAbsVelocity() * 0.75;
|
||||
|
||||
// Make a bubble of warning ahead of the missile.
|
||||
CSoundEnt::InsertSound ( SOUND_DANGER, vecWarnLocation, flSpeed * WARN_INTERVAL, 0.5 );
|
||||
|
||||
#if 0
|
||||
Vector vecRight, vecForward;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vecForward, &vecRight, NULL );
|
||||
|
||||
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
|
||||
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
|
||||
|
||||
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
|
||||
NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10);
|
||||
#endif
|
||||
m_flNextWarnTime = gpGlobals->curtime + WARN_INTERVAL;
|
||||
}
|
||||
#endif // BUBBLE
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
int CGrenadeHomer::OnTakeDamage( const CTakeDamageInfo &info )
|
||||
{
|
||||
// Don't take damage from other homing grenades so can shoot in vollies
|
||||
if (FClassnameIs( info.GetInflictor(), "grenade_homer"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return BaseClass::OnTakeDamage( info );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
CGrenadeHomer::CGrenadeHomer(void)
|
||||
{
|
||||
for (int i=0;i<3;i++)
|
||||
{
|
||||
m_hRocketTrail[i] = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by city scanner
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEHOMER_H
|
||||
#define GRENADEHOMER_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
#include "weapon_rpg.h"
|
||||
|
||||
enum HomerRocketTrail_t
|
||||
{
|
||||
HOMER_SMOKE_TRAIL_OFF, // No smoke trail
|
||||
HOMER_SMOKE_TRAIL_ON, // Smoke trail always on
|
||||
HOMER_SMOKE_TRAIL_ON_HOMING, // Smoke trail on when homing turned on
|
||||
HOMER_SMOKE_TRAIL_ALIEN, // Alien colors on smoke trail
|
||||
};
|
||||
|
||||
class CGrenadeHomer : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeHomer, CBaseGrenade );
|
||||
|
||||
static CGrenadeHomer* CreateGrenadeHomer( string_t nModelName, string_t sFlySound, const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner );
|
||||
|
||||
virtual void Precache( void );
|
||||
void Spawn( void );
|
||||
void Launch( CBaseEntity *pOwner, CBaseEntity *pTarget, const Vector &vInitVelocity, float m_flHomingSpeed, float fFallSpeed, int nRocketTrailType);
|
||||
void SetSpin(float flSpinMagnitude, float flSpinSpeed);
|
||||
void SetHoming(float flStrength, float flDelay, float flRampUp, float flDuration, float flRampDown);
|
||||
|
||||
CHandle<RocketTrail> m_hRocketTrail[3];
|
||||
|
||||
private:
|
||||
string_t m_sFlySound;
|
||||
float m_flNextFlySoundTime;
|
||||
|
||||
// Input Parameters
|
||||
float m_flHomingStrength;
|
||||
float m_flHomingDelay; // How long before homing starts
|
||||
float m_flHomingRampUp; // How long it take to reach full strength
|
||||
float m_flHomingDuration; // How long does homing last
|
||||
float m_flHomingRampDown; // How long to reach no homing again
|
||||
float m_flHomingSpeed;
|
||||
float m_flSpinMagnitude;
|
||||
float m_flSpinSpeed;
|
||||
int m_nRocketTrailType;
|
||||
int m_spriteTexture;
|
||||
|
||||
// In flight data
|
||||
float m_flHomingLaunchTime;
|
||||
float m_flHomingStartTime;
|
||||
float m_flHomingEndTime;
|
||||
float m_flSpinOffset; // For randomization
|
||||
|
||||
EHANDLE m_hTarget;
|
||||
|
||||
void AimThink( void );
|
||||
void StartRocketTrail(void);
|
||||
void UpdateRocketTrail(float fScale);
|
||||
void StopRocketTrail(void);
|
||||
void PlayFlySound( void );
|
||||
void GrenadeHomerTouch( CBaseEntity *pOther );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
|
||||
public:
|
||||
void EXPORT Detonate(void);
|
||||
CGrenadeHomer(void);
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEHOMER_H
|
||||
@@ -0,0 +1,244 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Flaming bottle thrown from the hand
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "ammodef.h"
|
||||
#include "gamerules.h"
|
||||
#include "grenade_molotov.h"
|
||||
#include "weapon_brickbat.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "fire.h"
|
||||
#include "shake.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern short g_sModelIndexFireball;
|
||||
|
||||
extern ConVar sk_plr_dmg_molotov;
|
||||
extern ConVar sk_npc_dmg_molotov;
|
||||
ConVar sk_molotov_radius ( "sk_molotov_radius","0");
|
||||
|
||||
#define MOLOTOV_EXPLOSION_VOLUME 1024
|
||||
|
||||
BEGIN_DATADESC( CGrenade_Molotov )
|
||||
|
||||
DEFINE_FIELD( m_pFireTrail, FIELD_CLASSPTR ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( MolotovTouch ),
|
||||
DEFINE_FUNCTION( MolotovThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_molotov, CGrenade_Molotov );
|
||||
|
||||
void CGrenade_Molotov::Spawn( void )
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
|
||||
|
||||
SetModel( "models/weapons/w_molotov.mdl");
|
||||
|
||||
UTIL_SetSize(this, Vector( -6, -6, -2), Vector(6, 6, 2));
|
||||
|
||||
SetTouch( MolotovTouch );
|
||||
SetThink( MolotovThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flDamage = sk_plr_dmg_molotov.GetFloat();
|
||||
m_DmgRadius = sk_molotov_radius.GetFloat();
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( 1.0 );
|
||||
SetFriction( 0.8 ); // Give a little bounce so can flatten
|
||||
SetSequence( 1 );
|
||||
|
||||
m_pFireTrail = SmokeTrail::CreateSmokeTrail();
|
||||
|
||||
if( m_pFireTrail )
|
||||
{
|
||||
m_pFireTrail->m_SpawnRate = 48;
|
||||
m_pFireTrail->m_ParticleLifetime = 1.0f;
|
||||
|
||||
m_pFireTrail->m_StartColor.Init( 0.2f, 0.2f, 0.2f );
|
||||
m_pFireTrail->m_EndColor.Init( 0.0, 0.0, 0.0 );
|
||||
|
||||
m_pFireTrail->m_StartSize = 8;
|
||||
m_pFireTrail->m_EndSize = 32;
|
||||
m_pFireTrail->m_SpawnRadius = 4;
|
||||
m_pFireTrail->m_MinSpeed = 8;
|
||||
m_pFireTrail->m_MaxSpeed = 16;
|
||||
m_pFireTrail->m_Opacity = 0.25f;
|
||||
|
||||
m_pFireTrail->SetLifetime( 20.0f );
|
||||
m_pFireTrail->FollowEntity( this, "0" );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenade_Molotov::MolotovTouch( CBaseEntity *pOther )
|
||||
{
|
||||
Detonate();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenade_Molotov::Detonate( void )
|
||||
{
|
||||
SetModelName( NULL_STRING ); //invisible
|
||||
AddSolidFlags( FSOLID_NOT_SOLID ); // intangible
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
trace_t trace;
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + Vector ( 0, 0, -128 ), MASK_SOLID_BRUSHONLY,
|
||||
this, COLLISION_GROUP_NONE, &trace);
|
||||
|
||||
// Pull out of the wall a bit
|
||||
if ( trace.fraction != 1.0 )
|
||||
{
|
||||
SetLocalOrigin( trace.endpos + (trace.plane.normal * (m_flDamage - 24) * 0.6) );
|
||||
}
|
||||
|
||||
int contents = UTIL_PointContents ( GetAbsOrigin() );
|
||||
|
||||
if ( (contents & MASK_WATER) )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
EmitSound( "Grenade_Molotov.Detonate");
|
||||
|
||||
// Start some fires
|
||||
int i;
|
||||
QAngle vecTraceAngles;
|
||||
Vector vecTraceDir;
|
||||
trace_t firetrace;
|
||||
|
||||
for( i = 0 ; i < 16 ; i++ )
|
||||
{
|
||||
// build a little ray
|
||||
vecTraceAngles[PITCH] = random->RandomFloat(45, 135);
|
||||
vecTraceAngles[YAW] = random->RandomFloat(0, 360);
|
||||
vecTraceAngles[ROLL] = 0.0f;
|
||||
|
||||
AngleVectors( vecTraceAngles, &vecTraceDir );
|
||||
|
||||
Vector vecStart, vecEnd;
|
||||
|
||||
vecStart = GetAbsOrigin() + ( trace.plane.normal * 128 );
|
||||
vecEnd = vecStart + vecTraceDir * 512;
|
||||
|
||||
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &firetrace );
|
||||
|
||||
Vector ofsDir = ( firetrace.endpos - GetAbsOrigin() );
|
||||
float offset = VectorNormalize( ofsDir );
|
||||
|
||||
if ( offset > 128 )
|
||||
offset = 128;
|
||||
|
||||
//Get our scale based on distance
|
||||
float scale = 0.1f + ( 0.75f * ( 1.0f - ( offset / 128.0f ) ) );
|
||||
float growth = 0.1f + ( 0.75f * ( offset / 128.0f ) );
|
||||
|
||||
if( firetrace.fraction != 1.0 )
|
||||
{
|
||||
FireSystem_StartFire( firetrace.endpos, scale, growth, 30.0f, (SF_FIRE_START_ON|SF_FIRE_SMOKELESS|SF_FIRE_NO_GLOW), (CBaseEntity*) this, FIRE_NATURAL );
|
||||
}
|
||||
}
|
||||
// End Start some fires
|
||||
|
||||
CPASFilter filter2( trace.endpos );
|
||||
|
||||
te->Explosion( filter2, 0.0,
|
||||
&trace.endpos,
|
||||
g_sModelIndexFireball,
|
||||
2.0,
|
||||
15,
|
||||
TE_EXPLFLAG_NOPARTICLES,
|
||||
m_DmgRadius,
|
||||
m_flDamage );
|
||||
|
||||
CBaseEntity *pOwner;
|
||||
pOwner = GetOwnerEntity();
|
||||
SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
|
||||
|
||||
UTIL_DecalTrace( &trace, "Scorch" );
|
||||
|
||||
UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
|
||||
CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), BASEGRENADE_EXPLOSION_VOLUME, 3.0 );
|
||||
|
||||
RadiusDamage( CTakeDamageInfo( this, pOwner, m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
|
||||
|
||||
AddEffects( EF_NODRAW );
|
||||
SetAbsVelocity( vec3_origin );
|
||||
SetNextThink( gpGlobals->curtime + 0.2 );
|
||||
|
||||
if ( m_pFireTrail )
|
||||
{
|
||||
UTIL_Remove( m_pFireTrail );
|
||||
}
|
||||
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenade_Molotov::MolotovThink( void )
|
||||
{
|
||||
// See if I can lose my owner (has dropper moved out of way?)
|
||||
// Want do this so owner can throw the brickbat
|
||||
if (GetOwnerEntity())
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vUpABit = GetAbsOrigin();
|
||||
vUpABit.z += 5.0;
|
||||
|
||||
CBaseEntity* saveOwner = GetOwnerEntity();
|
||||
SetOwnerEntity( NULL );
|
||||
UTIL_TraceEntity( this, GetAbsOrigin(), vUpABit, MASK_SOLID, &tr );
|
||||
if ( tr.startsolid || tr.fraction != 1.0 )
|
||||
{
|
||||
SetOwnerEntity( saveOwner );
|
||||
}
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
void CGrenade_Molotov::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel("models/weapons/w_bb_bottle.mdl");
|
||||
|
||||
UTIL_PrecacheOther("_firesmoke");
|
||||
|
||||
PrecacheScriptSound( "Grenade_Molotov.Detonate" );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Molotov grenades
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEMOLOTOV_H
|
||||
#define GRENADEMOLOTOV_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
#include "smoke_trail.h"
|
||||
|
||||
class CGrenade_Molotov : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenade_Molotov, CBaseGrenade );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Precache( void );
|
||||
virtual void Detonate( void );
|
||||
void MolotovTouch( CBaseEntity *pOther );
|
||||
void MolotovThink( void );
|
||||
|
||||
protected:
|
||||
|
||||
SmokeTrail *m_pFireTrail;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEMOLOTOV_H
|
||||
@@ -0,0 +1,330 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: This is the brickbat weapon
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_pathfollower.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "shake.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "entitylist.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define GRENADE_PF_TURN_RATE 30
|
||||
#define GRENADE_PF_TOLERANCE 300
|
||||
#define GRENADE_PF_MODEL "models/Weapons/w_missile.mdl"
|
||||
|
||||
extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud
|
||||
|
||||
ConVar sk_dmg_pathfollower_grenade ( "sk_dmg_pathfollower_grenade","0");
|
||||
ConVar sk_pathfollower_grenade_radius ( "sk_pathfollower_grenade_radius","0");
|
||||
|
||||
BEGIN_DATADESC( CGrenadePathfollower )
|
||||
|
||||
DEFINE_FIELD( m_pPathTarget, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_flFlySpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_sFlySound, FIELD_SOUNDNAME ),
|
||||
DEFINE_FIELD( m_flNextFlySoundTime, FIELD_TIME),
|
||||
DEFINE_FIELD( m_hRocketTrail, FIELD_EHANDLE),
|
||||
|
||||
DEFINE_THINKFUNC( AimThink ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_ENTITYFUNC( GrenadeTouch ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_pathfollower, CGrenadePathfollower );
|
||||
|
||||
void CGrenadePathfollower::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "GrenadePathfollower.StopSounds" );
|
||||
}
|
||||
|
||||
void CGrenadePathfollower::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
// -------------------------
|
||||
// Inert when first spawned
|
||||
// -------------------------
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
AddFlag( FL_OBJECT ); // So can be shot down
|
||||
AddEffects( EF_NODRAW );
|
||||
|
||||
UTIL_SetSize(this, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
m_flDamage = sk_dmg_pathfollower_grenade.GetFloat();
|
||||
m_DmgRadius = sk_pathfollower_grenade_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 200;
|
||||
|
||||
SetGravity( 0.00001 );
|
||||
SetFriction( 0.8 );
|
||||
SetSequence( 1 );
|
||||
}
|
||||
|
||||
void CGrenadePathfollower::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
void CGrenadePathfollower::GrenadeTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// ----------------------------------
|
||||
// If I hit the sky, don't explode
|
||||
// ----------------------------------
|
||||
trace_t tr;
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity(), MASK_SOLID_BRUSHONLY,
|
||||
this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if (tr.surface.flags & SURF_SKY)
|
||||
{
|
||||
if(m_hRocketTrail)
|
||||
{
|
||||
UTIL_Remove(m_hRocketTrail);
|
||||
m_hRocketTrail = NULL;
|
||||
}
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
Detonate();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadePathfollower::Detonate(void)
|
||||
{
|
||||
StopSound(entindex(), CHAN_BODY, STRING(m_sFlySound));
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
if(m_hRocketTrail)
|
||||
{
|
||||
UTIL_Remove(m_hRocketTrail);
|
||||
m_hRocketTrail = NULL;
|
||||
}
|
||||
|
||||
CPASFilter filter( GetAbsOrigin() );
|
||||
|
||||
te->Explosion( filter, 0.0,
|
||||
&GetAbsOrigin(),
|
||||
g_sModelIndexFireball,
|
||||
0.5,
|
||||
15,
|
||||
TE_EXPLFLAG_NONE,
|
||||
m_DmgRadius,
|
||||
m_flDamage );
|
||||
|
||||
Vector vecForward = GetAbsVelocity();
|
||||
VectorNormalize(vecForward);
|
||||
trace_t tr;
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + 60*vecForward, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr);
|
||||
|
||||
UTIL_DecalTrace( &tr, "Scorch" );
|
||||
|
||||
UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
|
||||
CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 0.2 );
|
||||
|
||||
RadiusDamage ( CTakeDamageInfo( this, GetThrower(), m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
|
||||
CPASAttenuationFilter filter2( this, "GrenadePathfollower.StopSounds" );
|
||||
EmitSound( filter2, entindex(), "GrenadePathfollower.StopSounds" );
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadePathfollower::Launch( float flLaunchSpeed, string_t sPathCornerName)
|
||||
{
|
||||
m_pPathTarget = gEntList.FindEntityByName( NULL, sPathCornerName );
|
||||
if (m_pPathTarget)
|
||||
{
|
||||
m_flFlySpeed = flLaunchSpeed;
|
||||
Vector vTargetDir = (m_pPathTarget->GetAbsOrigin() - GetAbsOrigin());
|
||||
VectorNormalize(vTargetDir);
|
||||
SetAbsVelocity( m_flFlySpeed * vTargetDir );
|
||||
QAngle angles;
|
||||
VectorAngles( GetAbsVelocity(), angles );
|
||||
SetLocalAngles( angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "ERROR: Grenade_Pathfollower (%s) with no pathcorner!\n",GetDebugName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Make this thing come to life
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
RemoveEffects( EF_NODRAW );
|
||||
|
||||
SetUse( &CGrenadePathfollower::DetonateUse );
|
||||
SetTouch( &CGrenadePathfollower::GrenadeTouch );
|
||||
SetThink( &CGrenadePathfollower::AimThink );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
// Make the trail
|
||||
m_hRocketTrail = RocketTrail::CreateRocketTrail();
|
||||
|
||||
if ( m_hRocketTrail )
|
||||
{
|
||||
m_hRocketTrail->m_Opacity = 0.2f;
|
||||
m_hRocketTrail->m_SpawnRate = 100;
|
||||
m_hRocketTrail->m_ParticleLifetime = 0.5f;
|
||||
m_hRocketTrail->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
|
||||
m_hRocketTrail->m_EndColor.Init( 0.0, 0.0, 0.0 );
|
||||
m_hRocketTrail->m_StartSize = 8;
|
||||
m_hRocketTrail->m_EndSize = 16;
|
||||
m_hRocketTrail->m_SpawnRadius = 4;
|
||||
m_hRocketTrail->m_MinSpeed = 2;
|
||||
m_hRocketTrail->m_MaxSpeed = 16;
|
||||
|
||||
m_hRocketTrail->SetLifetime( 999 );
|
||||
m_hRocketTrail->FollowEntity( this, "0" );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadePathfollower::PlayFlySound(void)
|
||||
{
|
||||
if (gpGlobals->curtime > m_flNextFlySoundTime)
|
||||
{
|
||||
CPASAttenuationFilter filter( this, 0.8 );
|
||||
|
||||
EmitSound_t ep;
|
||||
ep.m_nChannel = CHAN_BODY;
|
||||
ep.m_pSoundName = STRING(m_sFlySound);
|
||||
ep.m_flVolume = 1.0f;
|
||||
ep.m_SoundLevel = SNDLVL_NORM;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
m_flNextFlySoundTime = gpGlobals->curtime + 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CGrenadePathfollower::AimThink( void )
|
||||
{
|
||||
PlayFlySound();
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Check if it's time to skip to the next path corner
|
||||
// ---------------------------------------------------
|
||||
if (m_pPathTarget)
|
||||
{
|
||||
float flLength = (GetAbsOrigin() - m_pPathTarget->GetAbsOrigin()).Length();
|
||||
if (flLength < GRENADE_PF_TOLERANCE)
|
||||
{
|
||||
m_pPathTarget = gEntList.FindEntityByName( NULL, m_pPathTarget->m_target );
|
||||
if (!m_pPathTarget)
|
||||
{
|
||||
SetGravity( 1.0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// If I have a pathcorner, aim towards it
|
||||
// --------------------------------------------------
|
||||
if (m_pPathTarget)
|
||||
{
|
||||
Vector vTargetDir = (m_pPathTarget->GetAbsOrigin() - GetAbsOrigin());
|
||||
VectorNormalize(vTargetDir);
|
||||
|
||||
Vector vecNewVelocity = GetAbsVelocity();
|
||||
VectorNormalize(vecNewVelocity);
|
||||
|
||||
float flTimeToUse = gpGlobals->frametime;
|
||||
while (flTimeToUse > 0)
|
||||
{
|
||||
vecNewVelocity += vTargetDir;
|
||||
flTimeToUse = -0.1;
|
||||
}
|
||||
vecNewVelocity *= m_flFlySpeed;
|
||||
SetAbsVelocity( vecNewVelocity );
|
||||
}
|
||||
|
||||
QAngle angles;
|
||||
VectorAngles( GetAbsVelocity(), angles );
|
||||
SetLocalAngles( angles );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
Class_T CGrenadePathfollower::Classify( void)
|
||||
{
|
||||
return CLASS_MISSILE;
|
||||
};
|
||||
|
||||
CGrenadePathfollower::CGrenadePathfollower(void)
|
||||
{
|
||||
m_hRocketTrail = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : In case somehow we get removed w/o detonating, make sure
|
||||
// we stop making sounds
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
CGrenadePathfollower::~CGrenadePathfollower(void)
|
||||
{
|
||||
StopSound(entindex(), CHAN_BODY, STRING(m_sFlySound));
|
||||
}
|
||||
|
||||
///------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
CGrenadePathfollower* CGrenadePathfollower::CreateGrenadePathfollower( string_t sModelName, string_t sFlySound, const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner )
|
||||
{
|
||||
CGrenadePathfollower *pGrenade = (CGrenadePathfollower*)CreateEntityByName( "grenade_pathfollower" );
|
||||
if ( !pGrenade )
|
||||
{
|
||||
Warning( "NULL Ent in CGrenadePathfollower!\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( pGrenade->edict() )
|
||||
{
|
||||
pGrenade->m_sFlySound = sFlySound;
|
||||
pGrenade->SetOwnerEntity( Instance( pentOwner ) );
|
||||
pGrenade->SetLocalOrigin( vecOrigin );
|
||||
pGrenade->SetLocalAngles( vecAngles );
|
||||
pGrenade->SetModel( STRING(sModelName) );
|
||||
pGrenade->Spawn();
|
||||
}
|
||||
return pGrenade;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by wasteland scanner
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEPATHFOLLOWER_H
|
||||
#define GRENADEPATHFOLLOWER_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
class RocketTrail;
|
||||
|
||||
class CGrenadePathfollower : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadePathfollower, CBaseGrenade );
|
||||
|
||||
static CGrenadePathfollower* CreateGrenadePathfollower( string_t sModelName, string_t sFlySound, const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner );
|
||||
|
||||
CHandle<RocketTrail> m_hRocketTrail;
|
||||
CBaseEntity* m_pPathTarget; // path corner we are heading towards
|
||||
float m_flFlySpeed;
|
||||
string_t m_sFlySound;
|
||||
float m_flNextFlySoundTime;
|
||||
|
||||
Class_T Classify( void);
|
||||
void Spawn( void );
|
||||
void AimThink( void );
|
||||
void GrenadeTouch( CBaseEntity *pOther );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void Launch( float flLaunchSpeed, string_t sPathCornerName);
|
||||
void PlayFlySound(void);
|
||||
|
||||
void EXPORT Detonate(void);
|
||||
|
||||
CGrenadePathfollower(void);
|
||||
~CGrenadePathfollower(void);
|
||||
|
||||
virtual void Precache();
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEPATHFOLLOWER_H
|
||||
@@ -0,0 +1,365 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_satchel.h"
|
||||
#include "player.h"
|
||||
#include "soundenvelope.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar sk_plr_dmg_satchel ( "sk_plr_dmg_satchel","0");
|
||||
ConVar sk_npc_dmg_satchel ( "sk_npc_dmg_satchel","0");
|
||||
ConVar sk_satchel_radius ( "sk_satchel_radius","0");
|
||||
|
||||
BEGIN_DATADESC( CSatchelCharge )
|
||||
|
||||
DEFINE_SOUNDPATCH( m_soundSlide ),
|
||||
|
||||
DEFINE_FIELD( m_flSlideVolume, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flNextBounceSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_bInAir, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_vLastPosition, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_pMyWeaponSLAM, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_bIsAttached, FIELD_BOOLEAN ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( SatchelTouch ),
|
||||
DEFINE_FUNCTION( SatchelThink ),
|
||||
DEFINE_FUNCTION( SatchelUse ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_satchel, CSatchelCharge );
|
||||
|
||||
//=========================================================
|
||||
// Deactivate - do whatever it is we do to an orphaned
|
||||
// satchel when we don't want it in the world anymore.
|
||||
//=========================================================
|
||||
void CSatchelCharge::Deactivate( void )
|
||||
{
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
|
||||
void CSatchelCharge::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
|
||||
SetModel( "models/Weapons/w_slam.mdl" );
|
||||
|
||||
UTIL_SetSize(this, Vector( -6, -6, -2), Vector(6, 6, 2));
|
||||
|
||||
SetTouch( SatchelTouch );
|
||||
SetUse( SatchelUse );
|
||||
SetThink( SatchelThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flDamage = sk_plr_dmg_satchel.GetFloat();
|
||||
m_DmgRadius = sk_satchel_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( UTIL_ScaleForGravity( 560 ) ); // slightly lower gravity
|
||||
SetFriction( 1.0 );
|
||||
SetSequence( 1 );
|
||||
|
||||
m_bIsAttached = false;
|
||||
m_bInAir = true;
|
||||
m_flSlideVolume = -1.0;
|
||||
m_flNextBounceSoundTime = 0;
|
||||
|
||||
m_vLastPosition = vec3_origin;
|
||||
|
||||
InitSlideSound();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CSatchelCharge::InitSlideSound(void)
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
CPASAttenuationFilter filter( this );
|
||||
m_soundSlide = controller.SoundCreate( filter, entindex(), CHAN_STATIC, "SatchelCharge.Slide", ATTN_NORM );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSatchelCharge::KillSlideSound(void)
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
controller.CommandClear( m_soundSlide );
|
||||
controller.SoundFadeOut( m_soundSlide, 0.0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSatchelCharge::SatchelUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
KillSlideSound();
|
||||
SetThink( Detonate );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSatchelCharge::SatchelTouch( CBaseEntity *pOther )
|
||||
{
|
||||
Assert( pOther );
|
||||
if ( !pOther->IsSolid() )
|
||||
return;
|
||||
|
||||
// If successfully thrown and touching the
|
||||
// NPC that released this grenade, pick it up
|
||||
if ( pOther == GetThrower() && GetOwnerEntity() == NULL )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( m_pMyWeaponSLAM->GetOwner() );
|
||||
if (pPlayer)
|
||||
{
|
||||
// Give the player ammo
|
||||
pPlayer->GiveAmmo(1, m_pMyWeaponSLAM->m_iSecondaryAmmoType);
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, "SatchelCharge.Pickup" );
|
||||
EmitSound( filter, pPlayer->entindex(), "SatchelCharge.Pickup" );
|
||||
|
||||
m_bIsLive = false;
|
||||
|
||||
// Take weapon out of detonate mode if necessary
|
||||
if (!m_pMyWeaponSLAM->AnyUndetonatedCharges())
|
||||
{
|
||||
m_pMyWeaponSLAM->m_bDetonatorArmed = false;
|
||||
m_pMyWeaponSLAM->m_bNeedDetonatorHolster = true;
|
||||
|
||||
// Put detonator away right away
|
||||
m_pMyWeaponSLAM->SetWeaponIdleTime( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
// Kill any sliding sound
|
||||
KillSlideSound();
|
||||
|
||||
// Remove satchel charge from world
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StudioFrameAdvance( );
|
||||
|
||||
// Is it attached to a wall?
|
||||
if (m_bIsAttached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetGravity( 1 );// normal gravity now
|
||||
|
||||
// HACKHACK - On ground isn't always set, so look for ground underneath
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.fraction < 1.0 )
|
||||
{
|
||||
// add a bit of static friction
|
||||
SetAbsVelocity( GetAbsVelocity() * 0.85 );
|
||||
SetLocalAngularVelocity( GetLocalAngularVelocity() * 0.8 );
|
||||
}
|
||||
|
||||
UpdateSlideSound();
|
||||
|
||||
if (m_bInAir)
|
||||
{
|
||||
BounceSound();
|
||||
m_bInAir = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CSatchelCharge::UpdateSlideSound( void )
|
||||
{
|
||||
if (!m_soundSlide)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float volume = GetAbsVelocity().Length2D()/1000;
|
||||
if (volume < 0.01 && m_soundSlide)
|
||||
{
|
||||
KillSlideSound();
|
||||
return;
|
||||
}
|
||||
// HACKHACK - On ground isn't always set, so look for ground underneath
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,10), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
|
||||
if ( tr.fraction < 1.0 )
|
||||
{
|
||||
if (m_flSlideVolume == -1.0)
|
||||
{
|
||||
controller.CommandClear( m_soundSlide );
|
||||
controller.Play( m_soundSlide, 1.0, 100 );
|
||||
m_flSlideVolume = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float volume = GetAbsVelocity().Length()/1000;
|
||||
if ( volume < m_flSlideVolume )
|
||||
{
|
||||
m_flSlideVolume = volume;
|
||||
controller.CommandClear( m_soundSlide );
|
||||
controller.SoundChangeVolume( m_soundSlide, volume, 0.1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
controller.CommandClear( m_soundSlide );
|
||||
controller.SoundChangeVolume( m_soundSlide, 0.0, 0.01 );
|
||||
m_flSlideVolume = -1.0;
|
||||
m_bInAir = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CSatchelCharge::SatchelThink( void )
|
||||
{
|
||||
// If attached resize so player can pick up off wall
|
||||
if (m_bIsAttached)
|
||||
{
|
||||
UTIL_SetSize(this, Vector( -2, -2, -6), Vector(2, 2, 6));
|
||||
}
|
||||
|
||||
UpdateSlideSound();
|
||||
|
||||
// See if I can lose my owner (has dropper moved out of way?)
|
||||
// Want do this so owner can shoot the satchel charge
|
||||
if (GetOwnerEntity())
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vUpABit = GetAbsOrigin();
|
||||
vUpABit.z += 5.0;
|
||||
|
||||
CBaseEntity* saveOwner = GetOwnerEntity();
|
||||
SetOwnerEntity( NULL );
|
||||
UTIL_TraceEntity( this, GetAbsOrigin(), vUpABit, MASK_SOLID, &tr );
|
||||
if ( tr.startsolid || tr.fraction != 1.0 )
|
||||
{
|
||||
SetOwnerEntity( saveOwner );
|
||||
}
|
||||
}
|
||||
|
||||
// Bounce movement code gets this think stuck occasionally so check if I've
|
||||
// succeeded in moving, otherwise kill my motions.
|
||||
else if ((GetAbsOrigin() - m_vLastPosition).LengthSqr()<1)
|
||||
{
|
||||
SetAbsVelocity( vec3_origin );
|
||||
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
angVel.y = 0;
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
// Kill any remaining sound
|
||||
KillSlideSound();
|
||||
|
||||
// Clear think function
|
||||
SetThink(NULL);
|
||||
return;
|
||||
}
|
||||
m_vLastPosition= GetAbsOrigin();
|
||||
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if (!IsInWorld())
|
||||
{
|
||||
// Kill any remaining sound
|
||||
KillSlideSound();
|
||||
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
// Is it attached to a wall?
|
||||
if (m_bIsAttached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector vecNewVel = GetAbsVelocity();
|
||||
if (GetWaterLevel() == 3)
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
vecNewVel *= 0.8;
|
||||
vecNewVel.z += 8;
|
||||
SetLocalAngularVelocity( GetLocalAngularVelocity() * 0.9 );
|
||||
}
|
||||
else if (GetWaterLevel() == 0)
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
}
|
||||
else
|
||||
{
|
||||
vecNewVel.z -= 8;
|
||||
}
|
||||
SetAbsVelocity( vecNewVel );
|
||||
}
|
||||
|
||||
void CSatchelCharge::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/Weapons/w_slam.mdl");
|
||||
|
||||
PrecacheScriptSound( "SatchelCharge.Pickup" );
|
||||
PrecacheScriptSound( "SatchelCharge.Bounce" );
|
||||
|
||||
PrecacheScriptSound( "SatchelCharge.Slide" );
|
||||
}
|
||||
|
||||
void CSatchelCharge::BounceSound( void )
|
||||
{
|
||||
if (gpGlobals->curtime > m_flNextBounceSoundTime)
|
||||
{
|
||||
EmitSound( "SatchelCharge.Bounce" );
|
||||
|
||||
m_flNextBounceSoundTime = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
CSatchelCharge::CSatchelCharge(void)
|
||||
{
|
||||
m_vLastPosition.Init();
|
||||
m_pMyWeaponSLAM = NULL;
|
||||
}
|
||||
|
||||
CSatchelCharge::~CSatchelCharge(void)
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
controller.SoundDestroy( m_soundSlide );
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Satchel Charge
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SATCHEL_H
|
||||
#define SATCHEL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
#include "hl2mp/weapon_slam.h"
|
||||
|
||||
class CSoundPatch;
|
||||
|
||||
class CSatchelCharge : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CSatchelCharge, CBaseGrenade );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void BounceSound( void );
|
||||
void UpdateSlideSound( void );
|
||||
void KillSlideSound(void);
|
||||
void SatchelTouch( CBaseEntity *pOther );
|
||||
void SatchelThink( void );
|
||||
void SatchelUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
CSoundPatch* m_soundSlide;
|
||||
float m_flSlideVolume;
|
||||
float m_flNextBounceSoundTime;
|
||||
bool m_bInAir;
|
||||
Vector m_vLastPosition;
|
||||
|
||||
public:
|
||||
CWeapon_SLAM* m_pMyWeaponSLAM; // Who shot me..
|
||||
bool m_bIsAttached;
|
||||
void Deactivate( void );
|
||||
|
||||
CSatchelCharge();
|
||||
~CSatchelCharge();
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
void InitSlideSound(void);
|
||||
};
|
||||
|
||||
#endif //SATCHEL_H
|
||||
@@ -0,0 +1,284 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "grenade_spit.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "hl2_shareddefs.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "particle_parse.h"
|
||||
#include "particle_system.h"
|
||||
#include "soundenvelope.h"
|
||||
#include "ai_utils.h"
|
||||
#include "te_effect_dispatch.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar sk_antlion_worker_spit_grenade_dmg ( "sk_antlion_worker_spit_grenade_dmg", "20", FCVAR_NONE, "Total damage done by an individual antlion worker loogie.");
|
||||
ConVar sk_antlion_worker_spit_grenade_radius ( "sk_antlion_worker_spit_grenade_radius","40", FCVAR_NONE, "Radius of effect for an antlion worker spit grenade.");
|
||||
ConVar sk_antlion_worker_spit_grenade_poison_ratio ( "sk_antlion_worker_spit_grenade_poison_ratio","0.3", FCVAR_NONE, "Percentage of an antlion worker's spit damage done as poison (which regenerates)");
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_spit, CGrenadeSpit );
|
||||
|
||||
BEGIN_DATADESC( CGrenadeSpit )
|
||||
|
||||
DEFINE_FIELD( m_bPlaySound, FIELD_BOOLEAN ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_ENTITYFUNC( GrenadeSpitTouch ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
CGrenadeSpit::CGrenadeSpit( void ) : m_bPlaySound( true ), m_pHissSound( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeSpit::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
SetSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
|
||||
SetModel( "models/spitball_large.mdl" );
|
||||
UTIL_SetSize( this, vec3_origin, vec3_origin );
|
||||
|
||||
SetUse( &CBaseGrenade::DetonateUse );
|
||||
SetTouch( &CGrenadeSpit::GrenadeSpitTouch );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flDamage = sk_antlion_worker_spit_grenade_dmg.GetFloat();
|
||||
m_DmgRadius = sk_antlion_worker_spit_grenade_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_NO;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( UTIL_ScaleForGravity( SPIT_GRAVITY ) );
|
||||
SetFriction( 0.8f );
|
||||
|
||||
SetCollisionGroup( HL2COLLISION_GROUP_SPIT );
|
||||
|
||||
AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
|
||||
|
||||
// We're self-illuminating, so we don't take or give shadows
|
||||
AddEffects( EF_NOSHADOW|EF_NORECEIVESHADOW );
|
||||
|
||||
// Create the dust effect in place
|
||||
m_hSpitEffect = (CParticleSystem *) CreateEntityByName( "info_particle_system" );
|
||||
if ( m_hSpitEffect != NULL )
|
||||
{
|
||||
// Setup our basic parameters
|
||||
m_hSpitEffect->KeyValue( "start_active", "1" );
|
||||
m_hSpitEffect->KeyValue( "effect_name", "antlion_spit_trail" );
|
||||
m_hSpitEffect->SetParent( this );
|
||||
m_hSpitEffect->SetLocalOrigin( vec3_origin );
|
||||
DispatchSpawn( m_hSpitEffect );
|
||||
if ( gpGlobals->curtime > 0.5f )
|
||||
m_hSpitEffect->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeSpit::SetSpitSize( int nSize )
|
||||
{
|
||||
switch (nSize)
|
||||
{
|
||||
case SPIT_LARGE:
|
||||
{
|
||||
m_bPlaySound = true;
|
||||
SetModel( "models/spitball_large.mdl" );
|
||||
break;
|
||||
}
|
||||
case SPIT_MEDIUM:
|
||||
{
|
||||
m_bPlaySound = true;
|
||||
m_flDamage *= 0.5f;
|
||||
SetModel( "models/spitball_medium.mdl" );
|
||||
break;
|
||||
}
|
||||
case SPIT_SMALL:
|
||||
{
|
||||
m_bPlaySound = false;
|
||||
m_flDamage *= 0.25f;
|
||||
SetModel( "models/spitball_small.mdl" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeSpit::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handle spitting
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeSpit::GrenadeSpitTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) )
|
||||
{
|
||||
// Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
|
||||
if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) )
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't hit other spit
|
||||
if ( pOther->GetCollisionGroup() == HL2COLLISION_GROUP_SPIT )
|
||||
return;
|
||||
|
||||
// We want to collide with water
|
||||
const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
|
||||
|
||||
// copy out some important things about this trace, because the first TakeDamage
|
||||
// call below may cause another trace that overwrites the one global pTrace points
|
||||
// at.
|
||||
bool bHitWater = ( ( pTrace->contents & CONTENTS_WATER ) != 0 );
|
||||
CBaseEntity *const pTraceEnt = pTrace->m_pEnt;
|
||||
const Vector tracePlaneNormal = pTrace->plane.normal;
|
||||
|
||||
if ( bHitWater )
|
||||
{
|
||||
// Splash!
|
||||
CEffectData data;
|
||||
data.m_fFlags = 0;
|
||||
data.m_vOrigin = pTrace->endpos;
|
||||
data.m_vNormal = Vector( 0, 0, 1 );
|
||||
data.m_flScale = 8.0f;
|
||||
|
||||
DispatchEffect( "watersplash", data );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make a splat decal
|
||||
trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
|
||||
UTIL_DecalTrace( pNewTrace, "BeerSplash" );
|
||||
}
|
||||
|
||||
// Part normal damage, part poison damage
|
||||
float poisonratio = sk_antlion_worker_spit_grenade_poison_ratio.GetFloat();
|
||||
|
||||
// Take direct damage if hit
|
||||
// NOTE: assume that pTrace is invalidated from this line forward!
|
||||
if ( pTraceEnt )
|
||||
{
|
||||
pTraceEnt->TakeDamage( CTakeDamageInfo( this, GetThrower(), m_flDamage * (1.0f-poisonratio), DMG_ACID ) );
|
||||
pTraceEnt->TakeDamage( CTakeDamageInfo( this, GetThrower(), m_flDamage * poisonratio, DMG_POISON ) );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), m_DmgRadius * 2.0f, 0.5f, GetThrower() );
|
||||
|
||||
QAngle vecAngles;
|
||||
VectorAngles( tracePlaneNormal, vecAngles );
|
||||
|
||||
if ( pOther->IsPlayer() || bHitWater )
|
||||
{
|
||||
// Do a lighter-weight effect if we just hit a player
|
||||
DispatchParticleEffect( "antlion_spit_player", GetAbsOrigin(), vecAngles );
|
||||
}
|
||||
else
|
||||
{
|
||||
DispatchParticleEffect( "antlion_spit", GetAbsOrigin(), vecAngles );
|
||||
}
|
||||
|
||||
Detonate();
|
||||
}
|
||||
|
||||
void CGrenadeSpit::Detonate(void)
|
||||
{
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
EmitSound( "GrenadeSpit.Hit" );
|
||||
|
||||
// Stop our hissing sound
|
||||
if ( m_pHissSound != NULL )
|
||||
{
|
||||
CSoundEnvelopeController::GetController().SoundDestroy( m_pHissSound );
|
||||
m_pHissSound = NULL;
|
||||
}
|
||||
|
||||
if ( m_hSpitEffect )
|
||||
{
|
||||
UTIL_Remove( m_hSpitEffect );
|
||||
}
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
void CGrenadeSpit::InitHissSound( void )
|
||||
{
|
||||
if ( m_bPlaySound == false )
|
||||
return;
|
||||
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
if ( m_pHissSound == NULL )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
m_pHissSound = controller.SoundCreate( filter, entindex(), "NPC_Antlion.PoisonBall" );
|
||||
controller.Play( m_pHissSound, 1.0f, 100 );
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeSpit::Think( void )
|
||||
{
|
||||
InitHissSound();
|
||||
if ( m_pHissSound == NULL )
|
||||
return;
|
||||
|
||||
// Add a doppler effect to the balls as they travel
|
||||
CBaseEntity *pPlayer = AI_GetSinglePlayer();
|
||||
if ( pPlayer != NULL )
|
||||
{
|
||||
Vector dir;
|
||||
VectorSubtract( pPlayer->GetAbsOrigin(), GetAbsOrigin(), dir );
|
||||
VectorNormalize(dir);
|
||||
|
||||
float velReceiver = DotProduct( pPlayer->GetAbsVelocity(), dir );
|
||||
float velTransmitter = -DotProduct( GetAbsVelocity(), dir );
|
||||
|
||||
// speed of sound == 13049in/s
|
||||
int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049));
|
||||
|
||||
// clamp pitch shifts
|
||||
if ( iPitch > 250 )
|
||||
{
|
||||
iPitch = 250;
|
||||
}
|
||||
if ( iPitch < 50 )
|
||||
{
|
||||
iPitch = 50;
|
||||
}
|
||||
|
||||
// Set the pitch we've calculated
|
||||
CSoundEnvelopeController::GetController().SoundChangePitch( m_pHissSound, iPitch, 0.1f );
|
||||
}
|
||||
|
||||
// Set us up to think again shortly
|
||||
SetNextThink( gpGlobals->curtime + 0.05f );
|
||||
}
|
||||
|
||||
void CGrenadeSpit::Precache( void )
|
||||
{
|
||||
// m_nSquidSpitSprite = PrecacheModel("sprites/greenglow1.vmt");// client side spittle.
|
||||
|
||||
PrecacheModel( "models/spitball_large.mdl" );
|
||||
PrecacheModel("models/spitball_medium.mdl");
|
||||
PrecacheModel("models/spitball_small.mdl");
|
||||
|
||||
PrecacheScriptSound( "GrenadeSpit.Hit" );
|
||||
|
||||
PrecacheParticleSystem( "antlion_spit_player" );
|
||||
PrecacheParticleSystem( "antlion_spit" );
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by bullsquid
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADESPIT_H
|
||||
#define GRENADESPIT_H
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
class CParticleSystem;
|
||||
|
||||
enum SpitSize_e
|
||||
{
|
||||
SPIT_SMALL,
|
||||
SPIT_MEDIUM,
|
||||
SPIT_LARGE,
|
||||
};
|
||||
|
||||
#define SPIT_GRAVITY 600
|
||||
|
||||
class CGrenadeSpit : public CBaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CGrenadeSpit, CBaseGrenade );
|
||||
|
||||
public:
|
||||
CGrenadeSpit( void );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Precache( void );
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
virtual unsigned int PhysicsSolidMaskForEntity( void ) const { return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_WATER ); }
|
||||
|
||||
void GrenadeSpitTouch( CBaseEntity *pOther );
|
||||
void SetSpitSize( int nSize );
|
||||
void Detonate( void );
|
||||
void Think( void );
|
||||
|
||||
private:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void InitHissSound( void );
|
||||
|
||||
CHandle< CParticleSystem > m_hSpitEffect;
|
||||
CSoundPatch *m_pHissSound;
|
||||
bool m_bPlaySound;
|
||||
};
|
||||
|
||||
#endif //GRENADESPIT_H
|
||||
@@ -0,0 +1,266 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the tripmine grenade.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "beam_shared.h"
|
||||
#include "shake.h"
|
||||
#include "grenade_tripmine.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern const char* g_pModelNameLaser;
|
||||
|
||||
ConVar sk_plr_dmg_tripmine ( "sk_plr_dmg_tripmine","0");
|
||||
ConVar sk_npc_dmg_tripmine ( "sk_npc_dmg_tripmine","0");
|
||||
ConVar sk_tripmine_radius ( "sk_tripmine_radius","0");
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_tripmine, CTripmineGrenade );
|
||||
|
||||
BEGIN_DATADESC( CTripmineGrenade )
|
||||
|
||||
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_flPowerUp, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_vecDir, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_vecEnd, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_flBeamLength, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_posOwner, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_angleOwner, FIELD_VECTOR ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( WarningThink ),
|
||||
DEFINE_FUNCTION( PowerupThink ),
|
||||
DEFINE_FUNCTION( BeamBreakThink ),
|
||||
DEFINE_FUNCTION( DelayDeathThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
CTripmineGrenade::CTripmineGrenade()
|
||||
{
|
||||
m_vecDir.Init();
|
||||
m_vecEnd.Init();
|
||||
m_posOwner.Init();
|
||||
m_angleOwner.Init();
|
||||
}
|
||||
|
||||
void CTripmineGrenade::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
SetModel( "models/Weapons/w_slam.mdl" );
|
||||
|
||||
|
||||
m_flCycle = 0;
|
||||
m_nBody = 3;
|
||||
m_flDamage = sk_plr_dmg_tripmine.GetFloat();
|
||||
m_DmgRadius = sk_tripmine_radius.GetFloat();
|
||||
|
||||
ResetSequenceInfo( );
|
||||
m_flPlaybackRate = 0;
|
||||
|
||||
UTIL_SetSize(this, Vector( -4, -4, -2), Vector(4, 4, 2));
|
||||
|
||||
m_flPowerUp = gpGlobals->curtime + 2.0;
|
||||
|
||||
SetThink( PowerupThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.2 );
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
|
||||
m_iHealth = 1;
|
||||
|
||||
EmitSound( "TripmineGrenade.Charge" );
|
||||
|
||||
// Tripmine sits at 90 on wall so rotate back to get m_vecDir
|
||||
QAngle angles = GetLocalAngles();
|
||||
angles.x -= 90;
|
||||
|
||||
AngleVectors( angles, &m_vecDir );
|
||||
m_vecEnd = GetLocalOrigin() + m_vecDir * 2048;
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/Weapons/w_slam.mdl");
|
||||
|
||||
PrecacheScriptSound( "TripmineGrenade.Charge" );
|
||||
PrecacheScriptSound( "TripmineGrenade.PowerUp" );
|
||||
PrecacheScriptSound( "TripmineGrenade.StopSound" );
|
||||
PrecacheScriptSound( "TripmineGrenade.Activate" );
|
||||
PrecacheScriptSound( "TripmineGrenade.ShootRope" );
|
||||
PrecacheScriptSound( "TripmineGrenade.Hook" );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::WarningThink( void )
|
||||
{
|
||||
// set to power up
|
||||
SetThink( PowerupThink );
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::PowerupThink( void )
|
||||
{
|
||||
if (gpGlobals->curtime > m_flPowerUp)
|
||||
{
|
||||
MakeBeam( );
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
m_bIsLive = true;
|
||||
|
||||
// play enabled sound
|
||||
EmitSound( "TripmineGrenade.PowerUp" );;
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::KillBeam( void )
|
||||
{
|
||||
if ( m_pBeam )
|
||||
{
|
||||
UTIL_Remove( m_pBeam );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::MakeBeam( void )
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
m_flBeamLength = tr.fraction;
|
||||
|
||||
|
||||
|
||||
// If I hit a living thing, send the beam through me so it turns on briefly
|
||||
// and then blows the living thing up
|
||||
CBaseEntity *pEntity = tr.m_pEnt;
|
||||
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
|
||||
|
||||
// Draw length is not the beam length if entity is in the way
|
||||
float drawLength = tr.fraction;
|
||||
if (pBCC)
|
||||
{
|
||||
SetOwnerEntity( pBCC );
|
||||
UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
||||
m_flBeamLength = tr.fraction;
|
||||
SetOwnerEntity( NULL );
|
||||
|
||||
}
|
||||
|
||||
// set to follow laser spot
|
||||
SetThink( BeamBreakThink );
|
||||
|
||||
// Delay first think slightly so beam has time
|
||||
// to appear if person right in front of it
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
|
||||
Vector vecTmpEnd = GetLocalOrigin() + m_vecDir * 2048 * drawLength;
|
||||
|
||||
m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 1.0 );
|
||||
m_pBeam->PointEntInit( vecTmpEnd, this );
|
||||
m_pBeam->SetColor( 0, 214, 198 );
|
||||
m_pBeam->SetScrollRate( 25.6 );
|
||||
m_pBeam->SetBrightness( 64 );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::BeamBreakThink( void )
|
||||
{
|
||||
// See if I can go solid yet (has dropper moved out of way?)
|
||||
if (IsSolidFlagSet( FSOLID_NOT_SOLID ))
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vUpBit = GetAbsOrigin();
|
||||
vUpBit.z += 5.0;
|
||||
|
||||
UTIL_TraceEntity( this, GetAbsOrigin(), vUpBit, MASK_SHOT, &tr );
|
||||
if ( !tr.startsolid && (tr.fraction == 1.0) )
|
||||
{
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
}
|
||||
}
|
||||
|
||||
trace_t tr;
|
||||
|
||||
// NOT MASK_SHOT because we want only simple hit boxes
|
||||
UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
// ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength );
|
||||
|
||||
// respawn detect.
|
||||
if ( !m_pBeam )
|
||||
{
|
||||
MakeBeam( );
|
||||
if ( tr.m_pEnt )
|
||||
m_hOwner = tr.m_pEnt; // reset owner too
|
||||
}
|
||||
|
||||
|
||||
CBaseEntity *pEntity = tr.m_pEnt;
|
||||
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
|
||||
|
||||
if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001)
|
||||
{
|
||||
m_iHealth = 0;
|
||||
Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, this, 100, GIB_NORMAL ) );
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
int CTripmineGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info )
|
||||
{
|
||||
if (gpGlobals->curtime < m_flPowerUp && info.GetDamage() < m_iHealth)
|
||||
{
|
||||
// disable
|
||||
// Create( "weapon_tripmine", GetLocalOrigin() + m_vecDir * 24, GetAngles() );
|
||||
SetThink( SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
KillBeam();
|
||||
return FALSE;
|
||||
}
|
||||
return BaseClass::OnTakeDamage_Alive( info );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
SetThink( DelayDeathThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.5 );
|
||||
|
||||
EmitSound( "TripmineGrenade.StopSound" );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::DelayDeathThink( void )
|
||||
{
|
||||
KillBeam();
|
||||
trace_t tr;
|
||||
UTIL_TraceLine ( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, & tr);
|
||||
UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START );
|
||||
|
||||
Explode( &tr, DMG_BLAST );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADE_TRIPMINE_H
|
||||
#define GRENADE_TRIPMINE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
class CBeam;
|
||||
|
||||
|
||||
class CTripmineGrenade : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTripmineGrenade, CBaseGrenade );
|
||||
|
||||
CTripmineGrenade();
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
|
||||
void WarningThink( void );
|
||||
void PowerupThink( void );
|
||||
void BeamBreakThink( void );
|
||||
void DelayDeathThink( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
void MakeBeam( void );
|
||||
void KillBeam( void );
|
||||
|
||||
public:
|
||||
EHANDLE m_hOwner;
|
||||
|
||||
private:
|
||||
float m_flPowerUp;
|
||||
Vector m_vecDir;
|
||||
Vector m_vecEnd;
|
||||
float m_flBeamLength;
|
||||
|
||||
CBeam *m_pBeam;
|
||||
Vector m_posOwner;
|
||||
Vector m_angleOwner;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif // GRENADE_TRIPMINE_H
|
||||
@@ -0,0 +1,397 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the tripmine grenade.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "util.h"
|
||||
#include "shake.h"
|
||||
#include "grenade_tripwire.h"
|
||||
#include "grenade_homer.h"
|
||||
#include "rope.h"
|
||||
#include "rope_shared.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar sk_dmg_tripwire ( "sk_dmg_tripwire","0");
|
||||
ConVar sk_tripwire_radius ( "sk_tripwire_radius","0");
|
||||
|
||||
#define GRENADETRIPWIRE_MISSILEMDL "models/Weapons/ar2_grenade.mdl"
|
||||
|
||||
#define TGRENADE_LAUNCH_VEL 1200
|
||||
#define TGRENADE_SPIN_MAG 50
|
||||
#define TGRENADE_SPIN_SPEED 100
|
||||
#define TGRENADE_MISSILE_OFFSET 50
|
||||
#define TGRENADE_MAX_ROPE_LEN 1500
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_tripwire, CTripwireGrenade );
|
||||
|
||||
BEGIN_DATADESC( CTripwireGrenade )
|
||||
|
||||
DEFINE_FIELD( m_flPowerUp, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_nMissileCount, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_vecDir, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_vTargetPos, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vTargetOffset, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_pRope, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_pHook, FIELD_CLASSPTR ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( WarningThink ),
|
||||
DEFINE_FUNCTION( PowerupThink ),
|
||||
DEFINE_FUNCTION( RopeBreakThink ),
|
||||
DEFINE_FUNCTION( FireThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
CTripwireGrenade::CTripwireGrenade()
|
||||
{
|
||||
m_vecDir.Init();
|
||||
}
|
||||
|
||||
void CTripwireGrenade::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
SetModel( "models/Weapons/w_slam.mdl" );
|
||||
|
||||
m_nMissileCount = 0;
|
||||
|
||||
UTIL_SetSize(this, Vector( -4, -4, -2), Vector(4, 4, 2));
|
||||
|
||||
m_flPowerUp = gpGlobals->curtime + 1.2;//<<CHECK>>get rid of this
|
||||
|
||||
SetThink( WarningThink );
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
|
||||
m_iHealth = 1;
|
||||
|
||||
m_pRope = NULL;
|
||||
m_pHook = NULL;
|
||||
|
||||
// Tripwire grenade sits at 90 on wall so rotate back to get m_vecDir
|
||||
QAngle angles = GetLocalAngles();
|
||||
angles.x -= 90;
|
||||
|
||||
AngleVectors( angles, &m_vecDir );
|
||||
}
|
||||
|
||||
|
||||
void CTripwireGrenade::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/Weapons/w_slam.mdl");
|
||||
|
||||
PrecacheModel(GRENADETRIPWIRE_MISSILEMDL);
|
||||
}
|
||||
|
||||
|
||||
void CTripwireGrenade::WarningThink( void )
|
||||
{
|
||||
// play activate sound
|
||||
EmitSound( "TripwireGrenade.Activate" );
|
||||
|
||||
// set to power up
|
||||
SetThink( PowerupThink );
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
}
|
||||
|
||||
|
||||
void CTripwireGrenade::PowerupThink( void )
|
||||
{
|
||||
if (gpGlobals->curtime > m_flPowerUp)
|
||||
{
|
||||
MakeRope( );
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
m_bIsLive = true;
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
|
||||
void CTripwireGrenade::BreakRope( void )
|
||||
{
|
||||
if (m_pRope)
|
||||
{
|
||||
m_pRope->m_RopeFlags |= ROPE_COLLIDE;
|
||||
m_pRope->DetachPoint(0);
|
||||
|
||||
Vector vVelocity;
|
||||
m_pHook->GetVelocity( &vVelocity, NULL );
|
||||
if (vVelocity.Length() > 1)
|
||||
{
|
||||
m_pRope->DetachPoint(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CTripwireGrenade::MakeRope( void )
|
||||
{
|
||||
SetThink( RopeBreakThink );
|
||||
|
||||
// Delay first think slightly so rope has time
|
||||
// to appear if person right in front of it
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
|
||||
// Create hook for end of tripwire
|
||||
m_pHook = (CTripwireHook*)CBaseEntity::Create( "tripwire_hook", GetLocalOrigin(), GetLocalAngles() );
|
||||
if (m_pHook)
|
||||
{
|
||||
Vector vShootVel = 800*(m_vecDir + Vector(0,0,0.3)+RandomVector(-0.01,0.01));
|
||||
m_pHook->SetVelocity( vShootVel, vec3_origin);
|
||||
m_pHook->SetOwnerEntity( this );
|
||||
m_pHook->m_hGrenade = this;
|
||||
|
||||
m_pRope = CRopeKeyframe::Create(this,m_pHook,0,0);
|
||||
if (m_pRope)
|
||||
{
|
||||
m_pRope->m_Width = 1;
|
||||
m_pRope->m_RopeLength = 3;
|
||||
m_pRope->m_Slack = 100;
|
||||
|
||||
CPASAttenuationFilter filter( this,"TripwireGrenade.ShootRope" );
|
||||
EmitSound( filter, entindex(),"TripwireGrenade.ShootRope" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTripwireGrenade::Attach( void )
|
||||
{
|
||||
StopSound( "TripwireGrenade.ShootRope" );
|
||||
}
|
||||
|
||||
void CTripwireGrenade::RopeBreakThink( void )
|
||||
{
|
||||
// See if I can go solid yet (has dropper moved out of way?)
|
||||
if (IsSolidFlagSet(FSOLID_NOT_SOLID))
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vUpBit = GetAbsOrigin();
|
||||
vUpBit.z += 5.0;
|
||||
|
||||
UTIL_TraceEntity( this, GetAbsOrigin(), vUpBit, MASK_SHOT, &tr );
|
||||
if ( !tr.startsolid && (tr.fraction == 1.0) )
|
||||
{
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
}
|
||||
}
|
||||
|
||||
// Check if rope had gotten beyond it's max length
|
||||
float flRopeLength = (GetAbsOrigin()-m_pHook->GetAbsOrigin()).Length();
|
||||
if (flRopeLength > TGRENADE_MAX_ROPE_LEN)
|
||||
{
|
||||
// Shoot missiles at hook
|
||||
m_iHealth = 0;
|
||||
BreakRope();
|
||||
m_vTargetPos = m_pHook->GetAbsOrigin();
|
||||
CrossProduct ( m_vecDir, Vector(0,0,1), m_vTargetOffset );
|
||||
m_vTargetOffset *=TGRENADE_MISSILE_OFFSET;
|
||||
SetThink(FireThink);
|
||||
FireThink();
|
||||
}
|
||||
|
||||
// Check to see if can see hook
|
||||
// NOT MASK_SHOT because we want only simple hit boxes
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), m_pHook->GetAbsOrigin(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
// If can't see hook
|
||||
CBaseEntity *pEntity = tr.m_pEnt;
|
||||
if (tr.fraction != 1.0 && pEntity != m_pHook)
|
||||
{
|
||||
// Shoot missiles at place where rope was intersected
|
||||
m_iHealth = 0;
|
||||
BreakRope();
|
||||
m_vTargetPos = tr.endpos;
|
||||
CrossProduct ( m_vecDir, Vector(0,0,1), m_vTargetOffset );
|
||||
m_vTargetOffset *=TGRENADE_MISSILE_OFFSET;
|
||||
SetThink(FireThink);
|
||||
FireThink();
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Die if I take any damage
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
int CTripwireGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info )
|
||||
{
|
||||
// Killed upon any damage
|
||||
Event_Killed( info );
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: If someone damaged, me shoot of my missiles and die
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTripwireGrenade::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
if (m_iHealth > 0)
|
||||
{
|
||||
// Fire missiles and blow up
|
||||
for (int i=0;i<6;i++)
|
||||
{
|
||||
Vector vTargetPos = GetAbsOrigin() + RandomVector(-600,600);
|
||||
FireMissile(vTargetPos);
|
||||
}
|
||||
BreakRope();
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Fire a missile at the target position
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CTripwireGrenade::FireMissile(const Vector &vTargetPos)
|
||||
{
|
||||
Vector vTargetDir = (vTargetPos - GetAbsOrigin());
|
||||
VectorNormalize(vTargetDir);
|
||||
|
||||
float flGravity = 0.0001; // No gravity on the missiles
|
||||
bool bSmokeTrail = true;
|
||||
float flHomingSpeed = 0;
|
||||
Vector vLaunchVelocity = TGRENADE_LAUNCH_VEL*vTargetDir;
|
||||
float flSpinMagnitude = TGRENADE_SPIN_MAG;
|
||||
float flSpinSpeed = TGRENADE_SPIN_SPEED;
|
||||
|
||||
//<<CHECK>> hold in string_t
|
||||
CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer( MAKE_STRING(GRENADETRIPWIRE_MISSILEMDL), MAKE_STRING("TripwireGrenade.FlySound"), GetAbsOrigin(), vec3_angle, edict() );
|
||||
|
||||
pGrenade->Spawn( );
|
||||
pGrenade->SetSpin(flSpinMagnitude,flSpinSpeed);
|
||||
pGrenade->SetHoming(0,0,0,0,0);
|
||||
pGrenade->SetDamage(sk_dmg_tripwire.GetFloat());
|
||||
pGrenade->SetDamageRadius(sk_tripwire_radius.GetFloat());
|
||||
pGrenade->Launch(this,NULL,vLaunchVelocity,flHomingSpeed,flGravity,bSmokeTrail);
|
||||
|
||||
// Calculate travel time
|
||||
float flTargetDist = (GetAbsOrigin() - vTargetPos).Length();
|
||||
|
||||
pGrenade->m_flDetonateTime = gpGlobals->curtime + flTargetDist/TGRENADE_LAUNCH_VEL;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Shoot off a series of missiles over time, then go intert
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CTripwireGrenade::FireThink()
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.16f );
|
||||
|
||||
Vector vTargetPos = m_vTargetPos + (m_vTargetOffset * m_nMissileCount);
|
||||
FireMissile(vTargetPos);
|
||||
|
||||
vTargetPos = m_vTargetPos - (m_vTargetOffset * m_nMissileCount);
|
||||
FireMissile(vTargetPos);
|
||||
|
||||
|
||||
m_nMissileCount++;
|
||||
if (m_nMissileCount > 4)
|
||||
{
|
||||
m_iHealth = -1;
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
// CTripwireHook
|
||||
//
|
||||
// This is what the tripwire shoots out at the end of the rope
|
||||
// ####################################################################
|
||||
LINK_ENTITY_TO_CLASS( tripwire_hook, CTripwireHook );
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Save/Restore
|
||||
//---------------------------------------------------------
|
||||
BEGIN_DATADESC( CTripwireHook )
|
||||
|
||||
DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_bAttached, FIELD_BOOLEAN ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
void CTripwireHook::Spawn( void )
|
||||
{
|
||||
|
||||
Precache( );
|
||||
SetModel( "models/Weapons/w_grenade.mdl" );//<<CHECK>>
|
||||
|
||||
UTIL_SetSize(this, Vector( -1, -1, -1), Vector(1,1, 1));
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
m_bAttached = false;
|
||||
|
||||
CreateVPhysics();
|
||||
}
|
||||
|
||||
bool CTripwireHook::CreateVPhysics()
|
||||
{
|
||||
// Create the object in the physics system
|
||||
IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_BBOX, 0, false );
|
||||
|
||||
// Make sure I get touch called for static geometry
|
||||
if ( pPhysicsObject )
|
||||
{
|
||||
int flags = pPhysicsObject->GetCallbackFlags();
|
||||
flags |= CALLBACK_GLOBAL_TOUCH_STATIC;
|
||||
pPhysicsObject->SetCallbackFlags(flags);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CTripwireHook::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/Weapons/w_grenade.mdl"); //<<CHECK>>
|
||||
}
|
||||
|
||||
void CTripwireHook::EndTouch( CBaseEntity *pOther )
|
||||
{
|
||||
//<<CHECK>>do instead by clearing touch function
|
||||
if (!m_bAttached)
|
||||
{
|
||||
m_bAttached = true;
|
||||
|
||||
SetVelocity(vec3_origin, vec3_origin);
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
|
||||
EmitSound( "TripwireGrenade.Hook" );
|
||||
|
||||
// Let the tripwire grenade know that I've attached
|
||||
CTripwireGrenade* pGrenade = dynamic_cast<CTripwireGrenade*>((CBaseEntity*)m_hGrenade);
|
||||
if (pGrenade)
|
||||
{
|
||||
pGrenade->Attach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTripwireHook::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity )
|
||||
{
|
||||
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
||||
if ( pPhysicsObject )
|
||||
{
|
||||
pPhysicsObject->AddVelocity( &velocity, &angVelocity );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Tripmine
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TRIPWIRE_H
|
||||
#define TRIPWIRE_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
class CRopeKeyframe;
|
||||
|
||||
// ####################################################################
|
||||
// CTripwireHook
|
||||
//
|
||||
// This is what the tripwire shoots out at the end of the rope
|
||||
// ####################################################################
|
||||
class CTripwireHook : public CBaseAnimating
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
public:
|
||||
DECLARE_CLASS( CTripwireHook, CBaseAnimating );
|
||||
|
||||
EHANDLE m_hGrenade;
|
||||
bool m_bAttached;
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool CreateVPhysics( void );
|
||||
void EndTouch( CBaseEntity *pOther );
|
||||
void SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity );
|
||||
};
|
||||
|
||||
class CTripwireGrenade : public CBaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTripwireGrenade, CBaseGrenade );
|
||||
|
||||
CTripwireGrenade();
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
|
||||
void WarningThink( void );
|
||||
void PowerupThink( void );
|
||||
void RopeBreakThink( void );
|
||||
void FireThink( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void Attach( void );
|
||||
|
||||
void MakeRope( void );
|
||||
void BreakRope( void );
|
||||
void ShakeRope( void );
|
||||
void FireMissile(const Vector &vTargetPos);
|
||||
|
||||
private:
|
||||
float m_flPowerUp;
|
||||
Vector m_vecDir;
|
||||
|
||||
int m_nMissileCount;
|
||||
|
||||
Vector m_vTargetPos;
|
||||
Vector m_vTargetOffset;
|
||||
|
||||
CRopeKeyframe* m_pRope;
|
||||
CTripwireHook* m_pHook;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //TRIPWIRE_H
|
||||
@@ -0,0 +1,26 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "ai_networkmanager.h"
|
||||
#include "npc_strider.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CHL2NetworkBuildHelper : public CAI_NetworkBuildHelper
|
||||
{
|
||||
DECLARE_CLASS( CHL2NetworkBuildHelper, CAI_NetworkBuildHelper );
|
||||
|
||||
void PostInitNodePosition( CAI_Network *pNetwork, CAI_Node *pNode )
|
||||
{
|
||||
AdjustStriderNodePosition( pNetwork, pNode );
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(ai_network_build_helper,CHL2NetworkBuildHelper);
|
||||
@@ -0,0 +1,177 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== tf_client.cpp ========================================================
|
||||
|
||||
HL2 client/server game specific stuff
|
||||
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl2_player.h"
|
||||
#include "hl2_gamerules.h"
|
||||
#include "gamerules.h"
|
||||
#include "teamplay_gamerules.h"
|
||||
#include "entitylist.h"
|
||||
#include "physics.h"
|
||||
#include "game.h"
|
||||
#include "player_resource.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
#include "tier0/vprof.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
void Host_Say( edict_t *pEdict, bool teamonly );
|
||||
|
||||
extern CBaseEntity* FindPickerEntityClass( CBasePlayer *pPlayer, char *classname );
|
||||
extern bool g_fGameOver;
|
||||
|
||||
/*
|
||||
===========
|
||||
ClientPutInServer
|
||||
|
||||
called each time a player is spawned into the game
|
||||
============
|
||||
*/
|
||||
void ClientPutInServer( edict_t *pEdict, const char *playername )
|
||||
{
|
||||
// Allocate a CBasePlayer for pev, and call spawn
|
||||
CHL2_Player *pPlayer = CHL2_Player::CreatePlayer( "player", pEdict );
|
||||
pPlayer->SetPlayerName( playername );
|
||||
}
|
||||
|
||||
|
||||
void ClientActive( edict_t *pEdict, bool bLoadGame )
|
||||
{
|
||||
CHL2_Player *pPlayer = dynamic_cast< CHL2_Player* >( CBaseEntity::Instance( pEdict ) );
|
||||
Assert( pPlayer );
|
||||
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pPlayer->InitialSpawn();
|
||||
|
||||
if ( !bLoadGame )
|
||||
{
|
||||
pPlayer->Spawn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
const char *GetGameDescription()
|
||||
|
||||
Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
|
||||
===============
|
||||
*/
|
||||
const char *GetGameDescription()
|
||||
{
|
||||
if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized
|
||||
return g_pGameRules->GetGameDescription();
|
||||
else
|
||||
return "Half-Life 2";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Given a player and optional name returns the entity of that
|
||||
// classname that the player is nearest facing
|
||||
//
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity* FindEntity( edict_t *pEdict, char *classname)
|
||||
{
|
||||
// If no name was given set bits based on the picked
|
||||
if (FStrEq(classname,""))
|
||||
{
|
||||
return (FindPickerEntityClass( static_cast<CBasePlayer*>(GetContainingEntity(pEdict)), classname ));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Precache game-specific models & sounds
|
||||
//-----------------------------------------------------------------------------
|
||||
void ClientGamePrecache( void )
|
||||
{
|
||||
CBaseEntity::PrecacheModel("models/player.mdl");
|
||||
CBaseEntity::PrecacheModel( "models/gibs/agibs.mdl" );
|
||||
CBaseEntity::PrecacheModel ("models/weapons/v_hands.mdl");
|
||||
|
||||
CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowAmmo" );
|
||||
CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowHealth" );
|
||||
|
||||
CBaseEntity::PrecacheScriptSound( "FX_AntlionImpact.ShellImpact" );
|
||||
CBaseEntity::PrecacheScriptSound( "Missile.ShotDown" );
|
||||
CBaseEntity::PrecacheScriptSound( "Bullets.DefaultNearmiss" );
|
||||
CBaseEntity::PrecacheScriptSound( "Bullets.GunshipNearmiss" );
|
||||
CBaseEntity::PrecacheScriptSound( "Bullets.StriderNearmiss" );
|
||||
|
||||
CBaseEntity::PrecacheScriptSound( "Geiger.BeepHigh" );
|
||||
CBaseEntity::PrecacheScriptSound( "Geiger.BeepLow" );
|
||||
}
|
||||
|
||||
|
||||
// called by ClientKill and DeadThink
|
||||
void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
|
||||
{
|
||||
if (gpGlobals->coop || gpGlobals->deathmatch)
|
||||
{
|
||||
if ( fCopyCorpse )
|
||||
{
|
||||
// make a copy of the dead body for appearances sake
|
||||
((CHL2_Player *)pEdict)->CreateCorpse();
|
||||
}
|
||||
|
||||
// respawn player
|
||||
pEdict->Spawn();
|
||||
}
|
||||
else
|
||||
{ // restart the entire server
|
||||
engine->ServerCommand("reload\n");
|
||||
}
|
||||
}
|
||||
|
||||
void GameStartFrame( void )
|
||||
{
|
||||
VPROF("GameStartFrame()");
|
||||
if ( g_fGameOver )
|
||||
return;
|
||||
|
||||
gpGlobals->teamplay = (teamplay.GetInt() != 0);
|
||||
}
|
||||
|
||||
#ifdef HL2_EPISODIC
|
||||
extern ConVar gamerules_survival;
|
||||
#endif
|
||||
|
||||
//=========================================================
|
||||
// instantiate the proper game rules object
|
||||
//=========================================================
|
||||
void InstallGameRules()
|
||||
{
|
||||
#ifdef HL2_EPISODIC
|
||||
if ( gamerules_survival.GetBool() )
|
||||
{
|
||||
// Survival mode
|
||||
CreateGameRulesObject( "CHalfLife2Survival" );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// generic half-life
|
||||
CreateGameRulesObject( "CHalfLife2" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "../EventLog.h"
|
||||
#include "KeyValues.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CHL2EventLog : public CEventLog
|
||||
{
|
||||
private:
|
||||
typedef CEventLog BaseClass;
|
||||
|
||||
public:
|
||||
virtual char const *Name() { return "CHL2EventLog"; }
|
||||
|
||||
virtual ~CHL2EventLog() {};
|
||||
|
||||
public:
|
||||
bool PrintEvent( IGameEvent * event ) // override virtual function
|
||||
{
|
||||
if ( BaseClass::PrintEvent( event ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( Q_strcmp(event->GetName(), "hl2_") == 0 )
|
||||
{
|
||||
return PrintHL2Event( event );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool PrintHL2Event( IGameEvent * event ) // print Mod specific logs
|
||||
{
|
||||
// const char * name = event->GetName() + Q_strlen("hl2_"); // remove prefix
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static CHL2EventLog s_HL2EventLog;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton access
|
||||
//-----------------------------------------------------------------------------
|
||||
IGameSystem* GameLogSystem()
|
||||
{
|
||||
return &s_HL2EventLog;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl2_gamestats.h"
|
||||
#include "achievementmgr.h"
|
||||
|
||||
static CHL2GameStats s_HL2GameStats;
|
||||
|
||||
CHL2GameStats::CHL2GameStats( void )
|
||||
{
|
||||
gamestats = &s_HL2GameStats;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef HL2_GAMESTATS_H
|
||||
#define HL2_GAMESTATS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gamestats.h"
|
||||
|
||||
class CHL2GameStats : public CBaseGameStats
|
||||
{
|
||||
typedef CBaseGameStats BaseClass;
|
||||
|
||||
public:
|
||||
CHL2GameStats( void );
|
||||
};
|
||||
|
||||
#endif // EP1_GAMESTATS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,384 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Player for HL2.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL2_PLAYER_H
|
||||
#define HL2_PLAYER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "player.h"
|
||||
#include "hl2_playerlocaldata.h"
|
||||
#include "simtimer.h"
|
||||
#include "soundenvelope.h"
|
||||
|
||||
class CAI_Squad;
|
||||
class CPropCombineBall;
|
||||
|
||||
extern int TrainSpeed(int iSpeed, int iMax);
|
||||
extern void CopyToBodyQue( CBaseAnimating *pCorpse );
|
||||
|
||||
#define ARMOR_DECAY_TIME 3.5f
|
||||
|
||||
enum HL2PlayerPhysFlag_e
|
||||
{
|
||||
// 1 -- 5 are used by enum PlayerPhysFlag_e in player.h
|
||||
|
||||
PFLAG_ONBARNACLE = ( 1<<6 ) // player is hangning from the barnalce
|
||||
};
|
||||
|
||||
class IPhysicsPlayerController;
|
||||
class CLogicPlayerProxy;
|
||||
|
||||
struct commandgoal_t
|
||||
{
|
||||
Vector m_vecGoalLocation;
|
||||
CBaseEntity *m_pGoalEntity;
|
||||
};
|
||||
|
||||
// Time between checks to determine whether NPCs are illuminated by the flashlight
|
||||
#define FLASHLIGHT_NPC_CHECK_INTERVAL 0.4
|
||||
|
||||
//----------------------------------------------------
|
||||
// Definitions for weapon slots
|
||||
//----------------------------------------------------
|
||||
#define WEAPON_MELEE_SLOT 0
|
||||
#define WEAPON_SECONDARY_SLOT 1
|
||||
#define WEAPON_PRIMARY_SLOT 2
|
||||
#define WEAPON_EXPLOSIVE_SLOT 3
|
||||
#define WEAPON_TOOL_SLOT 4
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
class CSuitPowerDevice
|
||||
{
|
||||
public:
|
||||
CSuitPowerDevice( int bitsID, float flDrainRate ) { m_bitsDeviceID = bitsID; m_flDrainRate = flDrainRate; }
|
||||
private:
|
||||
int m_bitsDeviceID; // tells what the device is. DEVICE_SPRINT, DEVICE_FLASHLIGHT, etc. BITMASK!!!!!
|
||||
float m_flDrainRate; // how quickly does this device deplete suit power? ( percent per second )
|
||||
|
||||
public:
|
||||
int GetDeviceID( void ) const { return m_bitsDeviceID; }
|
||||
float GetDeviceDrainRate( void ) const
|
||||
{
|
||||
if( g_pGameRules->GetSkillLevel() == SKILL_EASY && hl2_episodic.GetBool() && !(GetDeviceID()&bits_SUIT_DEVICE_SPRINT) )
|
||||
return m_flDrainRate * 0.5f;
|
||||
else
|
||||
return m_flDrainRate;
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// >> HL2_PLAYER
|
||||
//=============================================================================
|
||||
class CHL2_Player : public CBasePlayer
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHL2_Player, CBasePlayer );
|
||||
|
||||
CHL2_Player();
|
||||
~CHL2_Player( void );
|
||||
|
||||
static CHL2_Player *CreatePlayer( const char *className, edict_t *ed )
|
||||
{
|
||||
CHL2_Player::s_PlayerEdict = ed;
|
||||
return (CHL2_Player*)CreateEntityByName( className );
|
||||
}
|
||||
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void CreateCorpse( void ) { CopyToBodyQue( this ); };
|
||||
|
||||
virtual void Precache( void );
|
||||
virtual void Spawn(void);
|
||||
virtual void Activate( void );
|
||||
virtual void CheatImpulseCommands( int iImpulse );
|
||||
virtual void PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper);
|
||||
virtual void PlayerUse ( void );
|
||||
virtual void SuspendUse( float flDuration ) { m_flTimeUseSuspended = gpGlobals->curtime + flDuration; }
|
||||
virtual void UpdateClientData( void );
|
||||
virtual void OnRestore();
|
||||
virtual void StopLoopingSounds( void );
|
||||
virtual void Splash( void );
|
||||
virtual void ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set );
|
||||
|
||||
void DrawDebugGeometryOverlays(void);
|
||||
|
||||
virtual Vector EyeDirection2D( void );
|
||||
virtual Vector EyeDirection3D( void );
|
||||
|
||||
virtual void CommanderMode();
|
||||
|
||||
virtual bool ClientCommand( const CCommand &args );
|
||||
|
||||
// from cbasecombatcharacter
|
||||
void InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity );
|
||||
WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon );
|
||||
|
||||
Class_T Classify ( void );
|
||||
|
||||
// from CBasePlayer
|
||||
virtual void SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize );
|
||||
|
||||
// Suit Power Interface
|
||||
void SuitPower_Update( void );
|
||||
bool SuitPower_Drain( float flPower ); // consume some of the suit's power.
|
||||
void SuitPower_Charge( float flPower ); // add suit power.
|
||||
void SuitPower_SetCharge( float flPower ) { m_HL2Local.m_flSuitPower = flPower; }
|
||||
void SuitPower_Initialize( void );
|
||||
bool SuitPower_IsDeviceActive( const CSuitPowerDevice &device );
|
||||
bool SuitPower_AddDevice( const CSuitPowerDevice &device );
|
||||
bool SuitPower_RemoveDevice( const CSuitPowerDevice &device );
|
||||
bool SuitPower_ShouldRecharge( void );
|
||||
float SuitPower_GetCurrentPercentage( void ) { return m_HL2Local.m_flSuitPower; }
|
||||
|
||||
void SetFlashlightEnabled( bool bState );
|
||||
|
||||
// Apply a battery
|
||||
bool ApplyBattery( float powerMultiplier = 1.0 );
|
||||
|
||||
// Commander Mode for controller NPCs
|
||||
enum CommanderCommand_t
|
||||
{
|
||||
CC_NONE,
|
||||
CC_TOGGLE,
|
||||
CC_FOLLOW,
|
||||
CC_SEND,
|
||||
};
|
||||
|
||||
void CommanderUpdate();
|
||||
void CommanderExecute( CommanderCommand_t command = CC_TOGGLE );
|
||||
bool CommanderFindGoal( commandgoal_t *pGoal );
|
||||
void NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity );
|
||||
CAI_BaseNPC *GetSquadCommandRepresentative();
|
||||
int GetNumSquadCommandables();
|
||||
int GetNumSquadCommandableMedics();
|
||||
|
||||
// Locator
|
||||
void UpdateLocatorPosition( const Vector &vecPosition );
|
||||
|
||||
// Sprint Device
|
||||
void StartAutoSprint( void );
|
||||
void StartSprinting( void );
|
||||
void StopSprinting( void );
|
||||
void InitSprinting( void );
|
||||
bool IsSprinting( void ) { return m_fIsSprinting; }
|
||||
bool CanSprint( void );
|
||||
void EnableSprint( bool bEnable);
|
||||
|
||||
bool CanZoom( CBaseEntity *pRequester );
|
||||
void ToggleZoom(void);
|
||||
void StartZooming( void );
|
||||
void StopZooming( void );
|
||||
bool IsZooming( void );
|
||||
void CheckSuitZoom( void );
|
||||
|
||||
// Walking
|
||||
void StartWalking( void );
|
||||
void StopWalking( void );
|
||||
bool IsWalking( void ) { return m_fIsWalking; }
|
||||
|
||||
// Aiming heuristics accessors
|
||||
virtual float GetIdleTime( void ) const { return ( m_flIdleTime - m_flMoveTime ); }
|
||||
virtual float GetMoveTime( void ) const { return ( m_flMoveTime - m_flIdleTime ); }
|
||||
virtual float GetLastDamageTime( void ) const { return m_flLastDamageTime; }
|
||||
virtual bool IsDucking( void ) const { return !!( GetFlags() & FL_DUCKING ); }
|
||||
|
||||
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
|
||||
void InputIgnoreFallDamage( inputdata_t &inputdata );
|
||||
void InputIgnoreFallDamageWithoutReset( inputdata_t &inputdata );
|
||||
void InputEnableFlashlight( inputdata_t &inputdata );
|
||||
void InputDisableFlashlight( inputdata_t &inputdata );
|
||||
|
||||
const impactdamagetable_t &GetPhysicsImpactDamageTable();
|
||||
virtual int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
virtual void OnDamagedByExplosion( const CTakeDamageInfo &info );
|
||||
bool ShouldShootMissTarget( CBaseCombatCharacter *pAttacker );
|
||||
|
||||
void CombineBallSocketed( CPropCombineBall *pCombineBall );
|
||||
|
||||
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
|
||||
|
||||
virtual void GetAutoaimVector( autoaim_params_t ¶ms );
|
||||
bool ShouldKeepLockedAutoaimTarget( EHANDLE hLockedTarget );
|
||||
|
||||
void SetLocatorTargetEntity( CBaseEntity *pEntity ) { m_hLocatorTargetEntity.Set( pEntity ); }
|
||||
|
||||
virtual int GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound);
|
||||
virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon );
|
||||
|
||||
virtual bool Weapon_CanUse( CBaseCombatWeapon *pWeapon );
|
||||
virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon );
|
||||
virtual bool Weapon_Lower( void );
|
||||
virtual bool Weapon_Ready( void );
|
||||
virtual bool Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex = 0 );
|
||||
virtual bool Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon );
|
||||
|
||||
void FirePlayerProxyOutput( const char *pszOutputName, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller );
|
||||
|
||||
CLogicPlayerProxy *GetPlayerProxy( void );
|
||||
|
||||
// Flashlight Device
|
||||
void CheckFlashlight( void );
|
||||
int FlashlightIsOn( void );
|
||||
void FlashlightTurnOn( void );
|
||||
void FlashlightTurnOff( void );
|
||||
bool IsIlluminatedByFlashlight( CBaseEntity *pEntity, float *flReturnDot );
|
||||
void SetFlashlightPowerDrainScale( float flScale ) { m_flFlashlightPowerDrainScale = flScale; }
|
||||
|
||||
// Underwater breather device
|
||||
virtual void SetPlayerUnderwater( bool state );
|
||||
virtual bool CanBreatheUnderwater() const { return m_HL2Local.m_flSuitPower > 0.0f; }
|
||||
|
||||
// physics interactions
|
||||
virtual void PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize );
|
||||
virtual bool IsHoldingEntity( CBaseEntity *pEnt );
|
||||
virtual void ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldindThis );
|
||||
virtual float GetHeldObjectMass( IPhysicsObject *pHeldObject );
|
||||
|
||||
virtual bool IsFollowingPhysics( void ) { return (m_afPhysicsFlags & PFLAG_ONBARNACLE) > 0; }
|
||||
void InputForceDropPhysObjects( inputdata_t &data );
|
||||
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
void NotifyScriptsOfDeath( void );
|
||||
|
||||
// override the test for getting hit
|
||||
virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
|
||||
|
||||
LadderMove_t *GetLadderMove() { return &m_HL2Local.m_LadderMove; }
|
||||
virtual void ExitLadder();
|
||||
virtual surfacedata_t *GetLadderSurface( const Vector &origin );
|
||||
|
||||
virtual void EquipSuit( bool bPlayEffects = true );
|
||||
virtual void RemoveSuit( void );
|
||||
void HandleAdmireGlovesAnimation( void );
|
||||
void StartAdmireGlovesAnimation( void );
|
||||
|
||||
void HandleSpeedChanges( void );
|
||||
|
||||
void SetControlClass( Class_T controlClass ) { m_nControlClass = controlClass; }
|
||||
|
||||
void StartWaterDeathSounds( void );
|
||||
void StopWaterDeathSounds( void );
|
||||
|
||||
bool IsWeaponLowered( void ) { return m_HL2Local.m_bWeaponLowered; }
|
||||
void HandleArmorReduction( void );
|
||||
void StartArmorReduction( void ) { m_flArmorReductionTime = gpGlobals->curtime + ARMOR_DECAY_TIME;
|
||||
m_iArmorReductionFrom = ArmorValue();
|
||||
}
|
||||
|
||||
void MissedAR2AltFire();
|
||||
|
||||
inline void EnableCappedPhysicsDamage();
|
||||
inline void DisableCappedPhysicsDamage();
|
||||
|
||||
// HUD HINTS
|
||||
void DisplayLadderHudHint();
|
||||
|
||||
CSoundPatch *m_sndLeeches;
|
||||
CSoundPatch *m_sndWaterSplashes;
|
||||
|
||||
protected:
|
||||
virtual void PreThink( void );
|
||||
virtual void PostThink( void );
|
||||
virtual bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
|
||||
|
||||
virtual void UpdateWeaponPosture( void );
|
||||
|
||||
virtual void ItemPostFrame();
|
||||
virtual void PlayUseDenySound();
|
||||
|
||||
private:
|
||||
bool CommanderExecuteOne( CAI_BaseNPC *pNpc, const commandgoal_t &goal, CAI_BaseNPC **Allies, int numAllies );
|
||||
|
||||
void OnSquadMemberKilled( inputdata_t &data );
|
||||
|
||||
Class_T m_nControlClass; // Class when player is controlling another entity
|
||||
// This player's HL2 specific data that should only be replicated to
|
||||
// the player and not to other players.
|
||||
CNetworkVarEmbedded( CHL2PlayerLocalData, m_HL2Local );
|
||||
|
||||
float m_flTimeAllSuitDevicesOff;
|
||||
|
||||
bool m_bSprintEnabled; // Used to disable sprint temporarily
|
||||
bool m_bIsAutoSprinting; // A proxy for holding down the sprint key.
|
||||
float m_fAutoSprintMinTime; // Minimum time to maintain autosprint regardless of player speed.
|
||||
|
||||
CNetworkVar( bool, m_fIsSprinting );
|
||||
CNetworkVarForDerived( bool, m_fIsWalking );
|
||||
|
||||
protected: // Jeep: Portal_Player needs access to this variable to overload PlayerUse for picking up objects through portals
|
||||
bool m_bPlayUseDenySound; // Signaled by PlayerUse, but can be unset by HL2 ladder code...
|
||||
|
||||
private:
|
||||
|
||||
CAI_Squad * m_pPlayerAISquad;
|
||||
CSimpleSimTimer m_CommanderUpdateTimer;
|
||||
float m_RealTimeLastSquadCommand;
|
||||
CommanderCommand_t m_QueuedCommand;
|
||||
|
||||
Vector m_vecMissPositions[16];
|
||||
int m_nNumMissPositions;
|
||||
|
||||
float m_flTimeIgnoreFallDamage;
|
||||
bool m_bIgnoreFallDamageResetAfterImpact;
|
||||
|
||||
// Suit power fields
|
||||
float m_flSuitPowerLoad; // net suit power drain (total of all device's drainrates)
|
||||
float m_flAdmireGlovesAnimTime;
|
||||
|
||||
float m_flNextFlashlightCheckTime;
|
||||
float m_flFlashlightPowerDrainScale;
|
||||
|
||||
// Aiming heuristics code
|
||||
float m_flIdleTime; //Amount of time we've been motionless
|
||||
float m_flMoveTime; //Amount of time we've been in motion
|
||||
float m_flLastDamageTime; //Last time we took damage
|
||||
float m_flTargetFindTime;
|
||||
|
||||
EHANDLE m_hPlayerProxy;
|
||||
|
||||
bool m_bFlashlightDisabled;
|
||||
bool m_bUseCappedPhysicsDamageTable;
|
||||
|
||||
float m_flArmorReductionTime;
|
||||
int m_iArmorReductionFrom;
|
||||
|
||||
float m_flTimeUseSuspended;
|
||||
|
||||
CSimpleSimTimer m_LowerWeaponTimer;
|
||||
CSimpleSimTimer m_AutoaimTimer;
|
||||
|
||||
EHANDLE m_hLockedAutoAimEntity;
|
||||
|
||||
EHANDLE m_hLocatorTargetEntity; // The entity that's being tracked by the suit locator.
|
||||
|
||||
float m_flTimeNextLadderHint; // Next time we're eligible to display a HUD hint about a ladder.
|
||||
|
||||
friend class CHL2GameMovement;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FIXME: find a better way to do this
|
||||
// Switches us to a physics damage table that caps the max damage.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHL2_Player::EnableCappedPhysicsDamage()
|
||||
{
|
||||
m_bUseCappedPhysicsDamageTable = true;
|
||||
}
|
||||
|
||||
|
||||
void CHL2_Player::DisableCappedPhysicsDamage()
|
||||
{
|
||||
m_bUseCappedPhysicsDamageTable = false;
|
||||
}
|
||||
|
||||
|
||||
#endif //HL2_PLAYER_H
|
||||
@@ -0,0 +1,69 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl2_playerlocaldata.h"
|
||||
#include "hl2_player.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "entitylist.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
BEGIN_SEND_TABLE_NOBASE( CHL2PlayerLocalData, DT_HL2Local )
|
||||
SendPropFloat( SENDINFO(m_flSuitPower), 10, SPROP_UNSIGNED | SPROP_ROUNDUP, 0.0, 100.0 ),
|
||||
SendPropInt( SENDINFO(m_bZooming), 1, SPROP_UNSIGNED ),
|
||||
SendPropInt( SENDINFO(m_bitsActiveDevices), MAX_SUIT_DEVICES, SPROP_UNSIGNED ),
|
||||
SendPropInt( SENDINFO(m_iSquadMemberCount) ),
|
||||
SendPropInt( SENDINFO(m_iSquadMedicCount) ),
|
||||
SendPropBool( SENDINFO(m_fSquadInFollowMode) ),
|
||||
SendPropBool( SENDINFO(m_bWeaponLowered) ),
|
||||
SendPropEHandle( SENDINFO(m_hAutoAimTarget) ),
|
||||
SendPropVector( SENDINFO(m_vecAutoAimPoint) ),
|
||||
SendPropEHandle( SENDINFO(m_hLadder) ),
|
||||
SendPropBool( SENDINFO(m_bDisplayReticle) ),
|
||||
SendPropBool( SENDINFO(m_bStickyAutoAim) ),
|
||||
SendPropBool( SENDINFO(m_bAutoAimTarget) ),
|
||||
#ifdef HL2_EPISODIC
|
||||
SendPropFloat( SENDINFO(m_flFlashBattery) ),
|
||||
SendPropVector( SENDINFO(m_vecLocatorOrigin) ),
|
||||
#endif
|
||||
END_SEND_TABLE()
|
||||
|
||||
BEGIN_SIMPLE_DATADESC( CHL2PlayerLocalData )
|
||||
DEFINE_FIELD( m_flSuitPower, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_bZooming, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bitsActiveDevices, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iSquadMemberCount, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iSquadMedicCount, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_fSquadInFollowMode, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bWeaponLowered, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bDisplayReticle, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bStickyAutoAim, FIELD_BOOLEAN ),
|
||||
#ifdef HL2_EPISODIC
|
||||
DEFINE_FIELD( m_flFlashBattery, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_vecLocatorOrigin, FIELD_POSITION_VECTOR ),
|
||||
#endif
|
||||
// Ladder related stuff
|
||||
DEFINE_FIELD( m_hLadder, FIELD_EHANDLE ),
|
||||
DEFINE_EMBEDDED( m_LadderMove ),
|
||||
END_DATADESC()
|
||||
|
||||
CHL2PlayerLocalData::CHL2PlayerLocalData()
|
||||
{
|
||||
m_flSuitPower = 0.0;
|
||||
m_bZooming = false;
|
||||
m_bWeaponLowered = false;
|
||||
m_hAutoAimTarget.Set(NULL);
|
||||
m_hLadder.Set(NULL);
|
||||
m_vecAutoAimPoint.GetForModify().Init();
|
||||
m_bDisplayReticle = false;
|
||||
#ifdef HL2_EPISODIC
|
||||
m_flFlashBattery = 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL2_PLAYERLOCALDATA_H
|
||||
#define HL2_PLAYERLOCALDATA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "networkvar.h"
|
||||
|
||||
#include "hl_movedata.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Player specific data for HL2 ( sent only to local player, too )
|
||||
//-----------------------------------------------------------------------------
|
||||
class CHL2PlayerLocalData
|
||||
{
|
||||
public:
|
||||
// Save/restore
|
||||
DECLARE_SIMPLE_DATADESC();
|
||||
DECLARE_CLASS_NOBASE( CHL2PlayerLocalData );
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
|
||||
CHL2PlayerLocalData();
|
||||
|
||||
CNetworkVar( float, m_flSuitPower );
|
||||
CNetworkVar( bool, m_bZooming );
|
||||
CNetworkVar( int, m_bitsActiveDevices );
|
||||
CNetworkVar( int, m_iSquadMemberCount );
|
||||
CNetworkVar( int, m_iSquadMedicCount );
|
||||
CNetworkVar( bool, m_fSquadInFollowMode );
|
||||
CNetworkVar( bool, m_bWeaponLowered );
|
||||
CNetworkVar( EHANDLE, m_hAutoAimTarget );
|
||||
CNetworkVar( Vector, m_vecAutoAimPoint );
|
||||
CNetworkVar( bool, m_bDisplayReticle );
|
||||
CNetworkVar( bool, m_bStickyAutoAim );
|
||||
CNetworkVar( bool, m_bAutoAimTarget );
|
||||
#ifdef HL2_EPISODIC
|
||||
CNetworkVar( float, m_flFlashBattery );
|
||||
CNetworkVar( Vector, m_vecLocatorOrigin );
|
||||
#endif
|
||||
|
||||
// Ladder related data
|
||||
CNetworkVar( EHANDLE, m_hLadder );
|
||||
LadderMove_t m_LadderMove;
|
||||
};
|
||||
|
||||
EXTERN_SEND_TABLE(DT_HL2Local);
|
||||
|
||||
|
||||
#endif // HL2_PLAYERLOCALDATA_H
|
||||
@@ -0,0 +1,875 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "weapon_physcannon.h"
|
||||
#include "hl2_player.h"
|
||||
#include "saverestore_utlvector.h"
|
||||
#include "triggers.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Weapon-dissolve trigger; all weapons in this field (sans the physcannon) are destroyed!
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTriggerWeaponDissolve : public CTriggerMultiple
|
||||
{
|
||||
DECLARE_CLASS( CTriggerWeaponDissolve, CTriggerMultiple );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
~CTriggerWeaponDissolve( void );
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Precache( void );
|
||||
virtual void Activate( void );
|
||||
virtual void StartTouch( CBaseEntity *pOther );
|
||||
|
||||
inline bool HasWeapon( CBaseCombatWeapon *pWeapon );
|
||||
|
||||
Vector GetConduitPoint( CBaseEntity *pTarget );
|
||||
|
||||
void InputStopSound( inputdata_t &inputdata );
|
||||
|
||||
void AddWeapon( CBaseCombatWeapon *pWeapon );
|
||||
void CreateBeam( const Vector &vecSource, CBaseEntity *pDest, float flLifetime );
|
||||
void DissolveThink( void );
|
||||
|
||||
private:
|
||||
|
||||
COutputEvent m_OnDissolveWeapon;
|
||||
COutputEvent m_OnChargingPhyscannon;
|
||||
|
||||
CUtlVector< CHandle<CBaseCombatWeapon> > m_pWeapons;
|
||||
CUtlVector< CHandle<CBaseEntity> > m_pConduitPoints;
|
||||
string_t m_strEmitterName;
|
||||
int m_spriteTexture;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( trigger_weapon_dissolve, CTriggerWeaponDissolve );
|
||||
|
||||
BEGIN_DATADESC( CTriggerWeaponDissolve )
|
||||
|
||||
DEFINE_KEYFIELD( m_strEmitterName, FIELD_STRING, "emittername" ),
|
||||
DEFINE_UTLVECTOR( m_pWeapons, FIELD_EHANDLE ),
|
||||
DEFINE_UTLVECTOR( m_pConduitPoints, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_spriteTexture, FIELD_MODELINDEX ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnDissolveWeapon, "OnDissolveWeapon" ),
|
||||
DEFINE_OUTPUT( m_OnChargingPhyscannon, "OnChargingPhyscannon" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "StopSound", InputStopSound ),
|
||||
|
||||
DEFINE_THINKFUNC( DissolveThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CTriggerWeaponDissolve::~CTriggerWeaponDissolve( void )
|
||||
{
|
||||
m_pWeapons.Purge();
|
||||
m_pConduitPoints.Purge();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Call precache for our sprite texture
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
Precache();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Precache our sprite texture
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" );
|
||||
|
||||
PrecacheScriptSound( "WeaponDissolve.Dissolve" );
|
||||
PrecacheScriptSound( "WeaponDissolve.Charge" );
|
||||
PrecacheScriptSound( "WeaponDissolve.Beam" );
|
||||
}
|
||||
|
||||
static const char *s_pDissolveThinkContext = "DissolveThinkContext";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Collect all our known conduit points
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::Activate( void )
|
||||
{
|
||||
BaseClass::Activate();
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
while ( ( pEntity = gEntList.FindEntityByName( pEntity, m_strEmitterName ) ) != NULL )
|
||||
{
|
||||
m_pConduitPoints.AddToTail( pEntity );
|
||||
}
|
||||
|
||||
SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + 0.1f, s_pDissolveThinkContext );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Checks to see if a weapon is already known
|
||||
// Input : *pWeapon - weapon to check for
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTriggerWeaponDissolve::HasWeapon( CBaseCombatWeapon *pWeapon )
|
||||
{
|
||||
if ( m_pWeapons.Find( pWeapon ) == m_pWeapons.InvalidIndex() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Adds a weapon to the known weapon list
|
||||
// Input : *pWeapon - weapon to add
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::AddWeapon( CBaseCombatWeapon *pWeapon )
|
||||
{
|
||||
if ( HasWeapon( pWeapon ) )
|
||||
return;
|
||||
|
||||
m_pWeapons.AddToTail( pWeapon );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Collect any weapons inside our volume
|
||||
// Input : *pOther -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::StartTouch( CBaseEntity *pOther )
|
||||
{
|
||||
BaseClass::StartTouch( pOther );
|
||||
|
||||
if ( PassesTriggerFilters( pOther ) == false )
|
||||
return;
|
||||
|
||||
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(pOther);
|
||||
|
||||
if ( pWeapon == NULL )
|
||||
return;
|
||||
|
||||
AddWeapon( pWeapon );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates a beam between a conduit point and a weapon
|
||||
// Input : &vecSource - conduit point
|
||||
// *pDest - weapon
|
||||
// flLifetime - amount of time
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::CreateBeam( const Vector &vecSource, CBaseEntity *pDest, float flLifetime )
|
||||
{
|
||||
CBroadcastRecipientFilter filter;
|
||||
|
||||
te->BeamEntPoint( filter, 0.0,
|
||||
0,
|
||||
&vecSource,
|
||||
pDest->entindex(),
|
||||
&(pDest->WorldSpaceCenter()),
|
||||
m_spriteTexture,
|
||||
0, // No halo
|
||||
1, // Frame
|
||||
30,
|
||||
flLifetime,
|
||||
16.0f, // Start width
|
||||
4.0f, // End width
|
||||
0, // No fade
|
||||
8, // Amplitude
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
16 ); // Speed
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns the closest conduit point to a weapon
|
||||
// Input : *pTarget - weapon to check for
|
||||
// Output : Vector - position of the conduit
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector CTriggerWeaponDissolve::GetConduitPoint( CBaseEntity *pTarget )
|
||||
{
|
||||
float nearDist = 9999999.0f;
|
||||
Vector bestPoint = vec3_origin;
|
||||
float testDist;
|
||||
|
||||
// Find the nearest conduit to the target
|
||||
for ( int i = 0; i < m_pConduitPoints.Count(); i++ )
|
||||
{
|
||||
testDist = ( m_pConduitPoints[i]->GetAbsOrigin() - pTarget->GetAbsOrigin() ).LengthSqr();
|
||||
|
||||
if ( testDist < nearDist )
|
||||
{
|
||||
bestPoint = m_pConduitPoints[i]->GetAbsOrigin();
|
||||
nearDist = testDist;
|
||||
}
|
||||
}
|
||||
|
||||
return bestPoint;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Dissolve all weapons within our volume
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::DissolveThink( void )
|
||||
{
|
||||
int numWeapons = m_pWeapons.Count();
|
||||
|
||||
// Dissolve all the items within the volume
|
||||
for ( int i = 0; i < numWeapons; i++ )
|
||||
{
|
||||
CBaseCombatWeapon *pWeapon = m_pWeapons[i];
|
||||
Vector vecConduit = GetConduitPoint( pWeapon );
|
||||
|
||||
// The physcannon upgrades when this happens
|
||||
if ( FClassnameIs( pWeapon, "weapon_physcannon" ) )
|
||||
{
|
||||
// This must be the last weapon for us to care
|
||||
if ( numWeapons > 1 )
|
||||
continue;
|
||||
|
||||
//FIXME: Make them do this on a stagger!
|
||||
|
||||
// All conduits send power to the weapon
|
||||
for ( int i = 0; i < m_pConduitPoints.Count(); i++ )
|
||||
{
|
||||
CreateBeam( m_pConduitPoints[i]->GetAbsOrigin(), pWeapon, 4.0f );
|
||||
}
|
||||
|
||||
PhysCannonBeginUpgrade( pWeapon );
|
||||
m_OnChargingPhyscannon.FireOutput( this, this );
|
||||
|
||||
EmitSound( "WeaponDissolve.Beam" );
|
||||
|
||||
// We're done
|
||||
m_pWeapons.Purge();
|
||||
m_pConduitPoints.Purge();
|
||||
SetContextThink( NULL, 0, s_pDissolveThinkContext );
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly dissolve them all
|
||||
float flLifetime = random->RandomFloat( 2.5f, 4.0f );
|
||||
CreateBeam( vecConduit, pWeapon, flLifetime );
|
||||
pWeapon->Dissolve( NULL, gpGlobals->curtime + ( 3.0f - flLifetime ), false );
|
||||
|
||||
m_OnDissolveWeapon.FireOutput( this, this );
|
||||
|
||||
CPASAttenuationFilter filter( pWeapon );
|
||||
EmitSound( filter, pWeapon->entindex(), "WeaponDissolve.Dissolve" );
|
||||
|
||||
// Beam looping sound
|
||||
EmitSound( "WeaponDissolve.Beam" );
|
||||
|
||||
m_pWeapons.Remove( i );
|
||||
SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + random->RandomFloat( 0.5f, 1.5f ), s_pDissolveThinkContext );
|
||||
return;
|
||||
}
|
||||
|
||||
SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + 0.1f, s_pDissolveThinkContext );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &inputdata -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponDissolve::InputStopSound( inputdata_t &inputdata )
|
||||
{
|
||||
StopSound( "WeaponDissolve.Beam" );
|
||||
StopSound( "WeaponDissolve.Charge" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Weapon-strip trigger; can't pick up weapons while in the field
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTriggerWeaponStrip : public CTriggerMultiple
|
||||
{
|
||||
DECLARE_CLASS( CTriggerWeaponStrip, CTriggerMultiple );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
void StartTouch(CBaseEntity *pOther);
|
||||
void EndTouch(CBaseEntity *pOther);
|
||||
|
||||
private:
|
||||
bool m_bKillWeapons;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Save/load
|
||||
//-----------------------------------------------------------------------------
|
||||
LINK_ENTITY_TO_CLASS( trigger_weapon_strip, CTriggerWeaponStrip );
|
||||
|
||||
BEGIN_DATADESC( CTriggerWeaponStrip )
|
||||
DEFINE_KEYFIELD( m_bKillWeapons, FIELD_BOOLEAN, "KillWeapons" ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Drops all weapons, marks the character as not being able to pick up weapons
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponStrip::StartTouch(CBaseEntity *pOther)
|
||||
{
|
||||
BaseClass::StartTouch( pOther );
|
||||
|
||||
if ( PassesTriggerFilters(pOther) == false )
|
||||
return;
|
||||
|
||||
CBaseCombatCharacter *pCharacter = pOther->MyCombatCharacterPointer();
|
||||
|
||||
if ( m_bKillWeapons )
|
||||
{
|
||||
for ( int i = 0 ; i < pCharacter->WeaponCount(); ++i )
|
||||
{
|
||||
CBaseCombatWeapon *pWeapon = pCharacter->GetWeapon( i );
|
||||
if ( !pWeapon )
|
||||
continue;
|
||||
|
||||
pCharacter->Weapon_Drop( pWeapon );
|
||||
UTIL_Remove( pWeapon );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Strip the player of his weapons
|
||||
if ( pCharacter && pCharacter->IsAllowedToPickupWeapons() )
|
||||
{
|
||||
CBaseCombatWeapon *pBugbait = pCharacter->Weapon_OwnsThisType( "weapon_bugbait" );
|
||||
if ( pBugbait )
|
||||
{
|
||||
pCharacter->Weapon_Drop( pBugbait );
|
||||
UTIL_Remove( pBugbait );
|
||||
}
|
||||
|
||||
pCharacter->Weapon_DropAll( true );
|
||||
pCharacter->SetPreventWeaponPickup( true );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called when an entity stops touching us.
|
||||
// Input : pOther - The entity that was touching us.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWeaponStrip::EndTouch(CBaseEntity *pOther)
|
||||
{
|
||||
if ( IsTouching( pOther ) )
|
||||
{
|
||||
CBaseCombatCharacter *pCharacter = pOther->MyCombatCharacterPointer();
|
||||
if ( pCharacter )
|
||||
{
|
||||
pCharacter->SetPreventWeaponPickup( false );
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::EndTouch( pOther );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Teleport trigger
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTriggerPhysicsTrap : public CTriggerMultiple
|
||||
{
|
||||
DECLARE_CLASS( CTriggerPhysicsTrap, CTriggerMultiple );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
void Touch( CBaseEntity *pOther );
|
||||
|
||||
private:
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
void InputToggle( inputdata_t &inputdata );
|
||||
|
||||
int m_nDissolveType;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Save/load
|
||||
//-----------------------------------------------------------------------------
|
||||
LINK_ENTITY_TO_CLASS( trigger_physics_trap, CTriggerPhysicsTrap );
|
||||
|
||||
BEGIN_DATADESC( CTriggerPhysicsTrap )
|
||||
|
||||
DEFINE_KEYFIELD( m_nDissolveType, FIELD_INTEGER, "dissolvetype" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inputs
|
||||
//------------------------------------------------------------------------------
|
||||
void CTriggerPhysicsTrap::InputToggle( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_bDisabled )
|
||||
{
|
||||
InputEnable( inputdata );
|
||||
}
|
||||
else
|
||||
{
|
||||
InputDisable( inputdata );
|
||||
}
|
||||
}
|
||||
|
||||
void CTriggerPhysicsTrap::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
if ( m_bDisabled )
|
||||
{
|
||||
Enable();
|
||||
}
|
||||
}
|
||||
|
||||
void CTriggerPhysicsTrap::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
if ( !m_bDisabled )
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Traps the entities
|
||||
//-----------------------------------------------------------------------------
|
||||
#define JOINTS_TO_CONSTRAIN 1
|
||||
|
||||
void CTriggerPhysicsTrap::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !PassesTriggerFilters(pOther) )
|
||||
return;
|
||||
|
||||
CBaseAnimating *pAnim = pOther->GetBaseAnimating();
|
||||
if ( !pAnim )
|
||||
return;
|
||||
|
||||
#ifdef HL2_DLL
|
||||
// HACK: Upgrade the physcannon
|
||||
if ( FClassnameIs( pAnim, "weapon_physcannon" ) )
|
||||
{
|
||||
PhysCannonBeginUpgrade( pAnim );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
pAnim->Dissolve( NULL, gpGlobals->curtime, false, m_nDissolveType );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CWateryDeathLeech : public CBaseAnimating
|
||||
{
|
||||
DECLARE_CLASS( CWateryDeathLeech, CBaseAnimating );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void LeechThink( void );
|
||||
|
||||
int m_iFadeState;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( ent_watery_leech, CWateryDeathLeech );
|
||||
|
||||
BEGIN_DATADESC( CWateryDeathLeech )
|
||||
DEFINE_THINKFUNC( LeechThink ),
|
||||
DEFINE_FIELD( m_iFadeState, FIELD_INTEGER ),
|
||||
END_DATADESC()
|
||||
|
||||
void CWateryDeathLeech::Precache( void )
|
||||
{
|
||||
//Ugh this is temporary until Jakob finishes the animations and doesn't need the command anymore.
|
||||
bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
|
||||
CBaseEntity::SetAllowPrecache( true );
|
||||
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel( "models/leech.mdl" );
|
||||
CBaseEntity::SetAllowPrecache( allowPrecache );
|
||||
}
|
||||
|
||||
void CWateryDeathLeech::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetSolid ( SOLID_NONE );
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
AddEffects( EF_NOSHADOW );
|
||||
|
||||
SetModel( "models/leech.mdl" );
|
||||
|
||||
SetThink( &CWateryDeathLeech::LeechThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
|
||||
m_flPlaybackRate = random->RandomFloat( 0.5, 1.5 );
|
||||
SetCycle( random->RandomFloat( 0.0f, 0.9f ) );
|
||||
|
||||
QAngle vAngle;
|
||||
vAngle[YAW] = random->RandomFloat( 0, 360 );
|
||||
SetAbsAngles( vAngle );
|
||||
|
||||
m_iFadeState = 1;
|
||||
SetRenderColorA( 1 );
|
||||
}
|
||||
|
||||
void CWateryDeathLeech::LeechThink( void )
|
||||
{
|
||||
if ( IsMarkedForDeletion() )
|
||||
return;
|
||||
|
||||
StudioFrameAdvance();
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
|
||||
if ( m_iFadeState != 0 )
|
||||
{
|
||||
float dt = gpGlobals->frametime;
|
||||
if ( dt > 0.1f )
|
||||
{
|
||||
dt = 0.1f;
|
||||
}
|
||||
m_nRenderMode = kRenderTransTexture;
|
||||
int speed = MAX(1,256*dt); // fade out over 1 second
|
||||
|
||||
if ( m_iFadeState == -1 )
|
||||
SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) );
|
||||
else
|
||||
SetRenderColorA( UTIL_Approach( 255, m_clrRender->a, speed ) );
|
||||
|
||||
if ( m_clrRender->a == 0 )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
else if ( m_clrRender->a == 255 )
|
||||
{
|
||||
m_iFadeState = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( GetOwnerEntity() )
|
||||
{
|
||||
if ( GetOwnerEntity()->GetWaterLevel() < 3 )
|
||||
{
|
||||
AddEffects( EF_NODRAW );
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveEffects( EF_NODRAW );
|
||||
}
|
||||
|
||||
SetAbsOrigin( GetOwnerEntity()->GetAbsOrigin() + GetOwnerEntity()->GetViewOffset() );
|
||||
}
|
||||
}
|
||||
|
||||
class CTriggerWateryDeath : public CBaseTrigger
|
||||
{
|
||||
DECLARE_CLASS( CTriggerWateryDeath, CBaseTrigger );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void SpawnLeeches( CBaseEntity *pOther );
|
||||
|
||||
// Ignore non-living entities
|
||||
virtual bool PassesTriggerFilters(CBaseEntity *pOther)
|
||||
{
|
||||
if ( !BaseClass::PassesTriggerFilters(pOther) )
|
||||
return false;
|
||||
|
||||
return (pOther->m_takedamage == DAMAGE_YES);
|
||||
}
|
||||
|
||||
virtual void StartTouch(CBaseEntity *pOther);
|
||||
virtual void EndTouch(CBaseEntity *pOther);
|
||||
|
||||
private:
|
||||
|
||||
CUtlVector< EHANDLE > m_hLeeches;
|
||||
|
||||
// Kill times for entities I'm touching
|
||||
CUtlVector< float > m_flEntityKillTimes;
|
||||
float m_flNextPullSound;
|
||||
float m_flPainValue;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CTriggerWateryDeath )
|
||||
DEFINE_UTLVECTOR( m_flEntityKillTimes, FIELD_TIME ),
|
||||
DEFINE_UTLVECTOR( m_hLeeches, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_flNextPullSound, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flPainValue, FIELD_FLOAT ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( trigger_waterydeath, CTriggerWateryDeath );
|
||||
|
||||
// Stages of the waterydeath trigger, in time offsets from the initial touch
|
||||
#define WD_KILLTIME_NEXT_BITE 0.3
|
||||
#define WD_PAINVALUE_STEP 2.0
|
||||
#define WD_MAX_DAMAGE 15.0f
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called when spawning, after keyvalues have been handled.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWateryDeath::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
Precache();
|
||||
|
||||
m_flNextPullSound = 0;
|
||||
m_flPainValue = 0;
|
||||
InitTrigger();
|
||||
}
|
||||
|
||||
void CTriggerWateryDeath::Precache( void )
|
||||
{
|
||||
//Ugh this is temporary until Jakob finishes the animations and doesn't need the command anymore.
|
||||
BaseClass::Precache();
|
||||
PrecacheModel( "models/leech.mdl" );
|
||||
|
||||
PrecacheScriptSound( "coast.leech_bites_loop" );
|
||||
PrecacheScriptSound( "coast.leech_water_churn_loop" );
|
||||
}
|
||||
|
||||
void CTriggerWateryDeath::SpawnLeeches( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther == NULL )
|
||||
return;
|
||||
|
||||
if ( m_hLeeches.Count() > 0 )
|
||||
return;
|
||||
|
||||
int iMaxLeeches = 12;
|
||||
|
||||
for ( int i = 0; i < iMaxLeeches; i++ )
|
||||
{
|
||||
CWateryDeathLeech *pLeech = (CWateryDeathLeech*)CreateEntityByName( "ent_watery_leech" );
|
||||
|
||||
if ( pLeech )
|
||||
{
|
||||
m_hLeeches.AddToTail( pLeech );
|
||||
|
||||
pLeech->Spawn();
|
||||
pLeech->SetAbsOrigin( pOther->GetAbsOrigin() );
|
||||
pLeech->SetOwnerEntity( pOther );
|
||||
|
||||
if ( i <= 8 )
|
||||
pLeech->SetSequence( i % 4 );
|
||||
else
|
||||
pLeech->SetSequence( ( i % 4 ) + 4 ) ;
|
||||
pLeech->ResetSequenceInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWateryDeath::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if (!PassesTriggerFilters(pOther))
|
||||
return;
|
||||
|
||||
// Find our index
|
||||
EHANDLE hOther;
|
||||
hOther = pOther;
|
||||
int iIndex = m_hTouchingEntities.Find( hOther );
|
||||
if ( iIndex == m_hTouchingEntities.InvalidIndex() )
|
||||
return;
|
||||
|
||||
float flKillTime = m_flEntityKillTimes[iIndex];
|
||||
|
||||
// Time to kill it?
|
||||
if ( gpGlobals->curtime > flKillTime )
|
||||
{
|
||||
//EmitSound( filter, entindex(), "WateryDeath.Bite", &pOther->GetAbsOrigin() );
|
||||
// Kill it
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
m_flPainValue = MIN( m_flPainValue + WD_PAINVALUE_STEP, WD_MAX_DAMAGE );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flPainValue = WD_MAX_DAMAGE;
|
||||
}
|
||||
|
||||
// Use DMG_GENERIC & make the target inflict the damage on himself.
|
||||
// This ensures that if the target is the player, the damage isn't modified by skill
|
||||
CTakeDamageInfo info = CTakeDamageInfo( pOther, pOther, m_flPainValue, DMG_GENERIC );
|
||||
|
||||
GuessDamageForce( &info, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() );
|
||||
pOther->TakeDamage( info );
|
||||
|
||||
m_flEntityKillTimes[iIndex] = gpGlobals->curtime + WD_KILLTIME_NEXT_BITE;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called when an entity starts touching us.
|
||||
// Input : pOther - The entity that is touching us.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWateryDeath::StartTouch(CBaseEntity *pOther)
|
||||
{
|
||||
BaseClass::StartTouch( pOther );
|
||||
|
||||
m_flPainValue = 0.0f;
|
||||
|
||||
// If we added him to our list, store the start time
|
||||
EHANDLE hOther;
|
||||
hOther = pOther;
|
||||
if ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() )
|
||||
{
|
||||
// Always added to the end
|
||||
// Players get warned, everything else gets et quick.
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
m_flEntityKillTimes.AddToTail( gpGlobals->curtime + WD_KILLTIME_NEXT_BITE );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flEntityKillTimes.AddToTail( gpGlobals->curtime + WD_KILLTIME_NEXT_BITE );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HL2_DLL
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
SpawnLeeches( pOther );
|
||||
|
||||
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pOther );
|
||||
|
||||
if ( pHL2Player )
|
||||
{
|
||||
pHL2Player->StartWaterDeathSounds();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called when an entity stops touching us.
|
||||
// Input : pOther - The entity that was touching us.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerWateryDeath::EndTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( IsTouching( pOther ) )
|
||||
{
|
||||
EHANDLE hOther;
|
||||
hOther = pOther;
|
||||
|
||||
// Remove the time from our list
|
||||
int iIndex = m_hTouchingEntities.Find( hOther );
|
||||
if ( iIndex != m_hTouchingEntities.InvalidIndex() )
|
||||
{
|
||||
m_flEntityKillTimes.Remove( iIndex );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HL2_DLL
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
for (int i = 0; i < m_hLeeches.Count(); i++ )
|
||||
{
|
||||
CWateryDeathLeech *pLeech = dynamic_cast<CWateryDeathLeech*>( m_hLeeches[i].Get() );
|
||||
|
||||
if ( pLeech )
|
||||
{
|
||||
pLeech->m_iFadeState = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_hLeeches.Count() > 0 )
|
||||
m_hLeeches.Purge();
|
||||
|
||||
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pOther );
|
||||
|
||||
if ( pHL2Player )
|
||||
{
|
||||
//Adrian: Hi, you might be wondering why I'm doing this, yes?
|
||||
// Well, EndTouch is called not only when the player leaves
|
||||
// the trigger, but also on level shutdown. We can't let the
|
||||
// soundpatch fade the sound out since we'll hit a nasty assert
|
||||
// cause it'll try to fade out a sound using an entity that might
|
||||
// be gone since we're shutting down the server.
|
||||
if ( !(pHL2Player->GetFlags() & FL_DONTTOUCH ) )
|
||||
pHL2Player->StopWaterDeathSounds();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BaseClass::EndTouch( pOther );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Triggers whenever an RPG is fired within it
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTriggerRPGFire : public CTriggerMultiple
|
||||
{
|
||||
DECLARE_CLASS( CTriggerRPGFire, CTriggerMultiple );
|
||||
public:
|
||||
~CTriggerRPGFire();
|
||||
|
||||
void Spawn( void );
|
||||
void OnRestore( void );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( trigger_rpgfire, CTriggerRPGFire );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTriggerRPGFire::~CTriggerRPGFire( void )
|
||||
{
|
||||
g_hWeaponFireTriggers.FindAndRemove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called when spawning, after keyvalues have been handled.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTriggerRPGFire::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
InitTrigger();
|
||||
|
||||
g_hWeaponFireTriggers.AddToTail( this );
|
||||
|
||||
// Stomp the touch function, because we don't want to respond to touch
|
||||
SetTouch( NULL );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
void CTriggerRPGFire::OnRestore()
|
||||
{
|
||||
BaseClass::OnRestore();
|
||||
|
||||
g_hWeaponFireTriggers.AddToTail( this );
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "player_command.h"
|
||||
#include "player.h"
|
||||
#include "igamemovement.h"
|
||||
#include "hl_movedata.h"
|
||||
#include "ipredictionsystem.h"
|
||||
#include "iservervehicle.h"
|
||||
#include "hl2_player.h"
|
||||
#include "vehicle_base.h"
|
||||
#include "gamestats.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CHLPlayerMove : public CPlayerMove
|
||||
{
|
||||
DECLARE_CLASS( CHLPlayerMove, CPlayerMove );
|
||||
public:
|
||||
CHLPlayerMove() :
|
||||
m_bWasInVehicle( false ),
|
||||
m_bVehicleFlipped( false ),
|
||||
m_bInGodMode( false ),
|
||||
m_bInNoClip( false )
|
||||
{
|
||||
m_vecSaveOrigin.Init();
|
||||
}
|
||||
|
||||
void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
|
||||
void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move );
|
||||
|
||||
private:
|
||||
Vector m_vecSaveOrigin;
|
||||
bool m_bWasInVehicle;
|
||||
bool m_bVehicleFlipped;
|
||||
bool m_bInGodMode;
|
||||
bool m_bInNoClip;
|
||||
};
|
||||
|
||||
//
|
||||
//
|
||||
// PlayerMove Interface
|
||||
static CHLPlayerMove g_PlayerMove;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton accessor
|
||||
//-----------------------------------------------------------------------------
|
||||
CPlayerMove *PlayerMove()
|
||||
{
|
||||
return &g_PlayerMove;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
static CHLMoveData g_HLMoveData;
|
||||
CMoveData *g_pMoveData = &g_HLMoveData;
|
||||
|
||||
IPredictionSystem *IPredictionSystem::g_pPredictionSystems = NULL;
|
||||
|
||||
void CHLPlayerMove::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
|
||||
{
|
||||
// Call the default SetupMove code.
|
||||
BaseClass::SetupMove( player, ucmd, pHelper, move );
|
||||
|
||||
// Convert to HL2 data.
|
||||
CHL2_Player *pHLPlayer = static_cast<CHL2_Player*>( player );
|
||||
Assert( pHLPlayer );
|
||||
|
||||
CHLMoveData *pHLMove = static_cast<CHLMoveData*>( move );
|
||||
Assert( pHLMove );
|
||||
|
||||
player->m_flForwardMove = ucmd->forwardmove;
|
||||
player->m_flSideMove = ucmd->sidemove;
|
||||
|
||||
pHLMove->m_bIsSprinting = pHLPlayer->IsSprinting();
|
||||
|
||||
if ( gpGlobals->frametime != 0 )
|
||||
{
|
||||
IServerVehicle *pVehicle = player->GetVehicle();
|
||||
|
||||
if ( pVehicle )
|
||||
{
|
||||
pVehicle->SetupMove( player, ucmd, pHelper, move );
|
||||
|
||||
if ( !m_bWasInVehicle )
|
||||
{
|
||||
m_bWasInVehicle = true;
|
||||
m_vecSaveOrigin.Init();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecSaveOrigin = player->GetAbsOrigin();
|
||||
if ( m_bWasInVehicle )
|
||||
{
|
||||
m_bWasInVehicle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CHLPlayerMove::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
|
||||
{
|
||||
// Call the default FinishMove code.
|
||||
BaseClass::FinishMove( player, ucmd, move );
|
||||
if ( gpGlobals->frametime != 0 )
|
||||
{
|
||||
float distance = 0.0f;
|
||||
IServerVehicle *pVehicle = player->GetVehicle();
|
||||
if ( pVehicle )
|
||||
{
|
||||
pVehicle->FinishMove( player, ucmd, move );
|
||||
IPhysicsObject *obj = player->GetVehicleEntity()->VPhysicsGetObject();
|
||||
if ( obj )
|
||||
{
|
||||
Vector newPos;
|
||||
obj->GetPosition( &newPos, NULL );
|
||||
distance = VectorLength( newPos - m_vecSaveOrigin );
|
||||
if ( m_vecSaveOrigin == vec3_origin || distance > 100.0f )
|
||||
distance = 0.0f;
|
||||
m_vecSaveOrigin = newPos;
|
||||
}
|
||||
|
||||
CPropVehicleDriveable *driveable = dynamic_cast< CPropVehicleDriveable * >( player->GetVehicleEntity() );
|
||||
if ( driveable )
|
||||
{
|
||||
// Overturned and at rest (if still moving it can fix itself)
|
||||
bool bFlipped = driveable->IsOverturned() && ( distance < 0.5f );
|
||||
if ( m_bVehicleFlipped != bFlipped )
|
||||
{
|
||||
if ( bFlipped )
|
||||
{
|
||||
gamestats->Event_FlippedVehicle( player, driveable );
|
||||
}
|
||||
m_bVehicleFlipped = bFlipped;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bVehicleFlipped = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bVehicleFlipped = false;
|
||||
distance = VectorLength( player->GetAbsOrigin() - m_vecSaveOrigin );
|
||||
}
|
||||
if ( distance > 0 )
|
||||
{
|
||||
gamestats->Event_PlayerTraveled( player, distance, pVehicle ? true : false, !pVehicle && static_cast< CHL2_Player * >( player )->IsSprinting() );
|
||||
}
|
||||
}
|
||||
|
||||
bool bGodMode = ( player->GetFlags() & FL_GODMODE ) ? true : false;
|
||||
if ( m_bInGodMode != bGodMode )
|
||||
{
|
||||
m_bInGodMode = bGodMode;
|
||||
if ( bGodMode )
|
||||
{
|
||||
gamestats->Event_PlayerEnteredGodMode( player );
|
||||
}
|
||||
}
|
||||
bool bNoClip = ( player->GetMoveType() == MOVETYPE_NOCLIP );
|
||||
if ( m_bInNoClip != bNoClip )
|
||||
{
|
||||
m_bInNoClip = bNoClip;
|
||||
if ( bNoClip )
|
||||
{
|
||||
gamestats->Event_PlayerEnteredNoClip( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "info_darknessmode_lightsource.h"
|
||||
#include "ai_debug_shared.h"
|
||||
|
||||
void CV_Debug_Darkness( IConVar *var, const char *pOldString, float flOldValue );
|
||||
ConVar g_debug_darkness( "g_debug_darkness", "0", FCVAR_NONE, "Show darkness mode lightsources.", CV_Debug_Darkness );
|
||||
ConVar darkness_ignore_LOS_to_sources( "darkness_ignore_LOS_to_sources", "1", FCVAR_NONE );
|
||||
|
||||
class CInfoDarknessLightSource;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Manages entities that provide light while in darkness mode
|
||||
//-----------------------------------------------------------------------------
|
||||
class CDarknessLightSourcesSystem : public CAutoGameSystem
|
||||
{
|
||||
public:
|
||||
CDarknessLightSourcesSystem() : CAutoGameSystem( "CDarknessLightSourcesSystem" )
|
||||
{
|
||||
}
|
||||
|
||||
void LevelInitPreEntity();
|
||||
|
||||
void AddLightSource( CInfoDarknessLightSource *pEntity, float flRadius );
|
||||
void RemoveLightSource( CInfoDarknessLightSource *pEntity );
|
||||
bool IsEntityVisibleToTarget( CBaseEntity *pLooker, CBaseEntity *pTarget );
|
||||
bool AreThereLightSourcesWithinRadius( CBaseEntity *pLooker, float flRadius );
|
||||
void SetDebug( bool bDebug );
|
||||
|
||||
private:
|
||||
struct lightsource_t
|
||||
{
|
||||
float flLightRadiusSqr;
|
||||
CHandle<CInfoDarknessLightSource> hEntity;
|
||||
};
|
||||
|
||||
CUtlVector<lightsource_t> m_LightSources;
|
||||
};
|
||||
|
||||
CDarknessLightSourcesSystem *DarknessLightSourcesSystem();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Darkness mode light source entity
|
||||
//-----------------------------------------------------------------------------
|
||||
class CInfoDarknessLightSource : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CInfoDarknessLightSource, CBaseEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void Activate()
|
||||
{
|
||||
if ( m_bDisabled == false )
|
||||
{
|
||||
DarknessLightSourcesSystem()->AddLightSource( this, m_flLightRadius );
|
||||
|
||||
if ( g_debug_darkness.GetBool() )
|
||||
{
|
||||
SetThink( &CInfoDarknessLightSource::DebugThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::Activate();
|
||||
}
|
||||
virtual void UpdateOnRemove()
|
||||
{
|
||||
DarknessLightSourcesSystem()->RemoveLightSource( this );
|
||||
BaseClass::UpdateOnRemove();
|
||||
}
|
||||
void SetLightRadius( float flRadius )
|
||||
{
|
||||
m_flLightRadius = flRadius;
|
||||
}
|
||||
|
||||
void InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
DarknessLightSourcesSystem()->AddLightSource( this, m_flLightRadius );
|
||||
m_bDisabled = false;
|
||||
}
|
||||
|
||||
void InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
DarknessLightSourcesSystem()->RemoveLightSource( this );
|
||||
m_bDisabled = true;
|
||||
}
|
||||
|
||||
void DebugThink( void )
|
||||
{
|
||||
Vector vecRadius( m_flLightRadius, m_flLightRadius, m_flLightRadius );
|
||||
NDebugOverlay::Box( GetAbsOrigin(), -vecRadius, vecRadius, 255,255,255, 8, 0.1 );
|
||||
NDebugOverlay::Box( GetAbsOrigin(), -Vector(5,5,5), Vector(5,5,5), 255,0,0, 8, 0.1 );
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
|
||||
int textoffset = 0;
|
||||
EntityText( textoffset, UTIL_VarArgs("Org: %.2f %.2f %.2f", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ), 0.1 );
|
||||
textoffset++;
|
||||
EntityText( textoffset, UTIL_VarArgs("Radius %.2f", m_flLightRadius), 0.1 );
|
||||
textoffset++;
|
||||
if ( m_bIgnoreLOS )
|
||||
{
|
||||
EntityText( textoffset, "Ignoring LOS", 0.1 );
|
||||
textoffset++;
|
||||
}
|
||||
if ( m_bDisabled )
|
||||
{
|
||||
EntityText( textoffset, "DISABLED", 0.1 );
|
||||
textoffset++;
|
||||
}
|
||||
}
|
||||
|
||||
void IgnoreLOS( void )
|
||||
{
|
||||
m_bIgnoreLOS = true;
|
||||
}
|
||||
|
||||
bool ShouldIgnoreLOS( void )
|
||||
{
|
||||
return m_bIgnoreLOS;
|
||||
}
|
||||
|
||||
private:
|
||||
float m_flLightRadius;
|
||||
bool m_bDisabled;
|
||||
bool m_bIgnoreLOS;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( info_darknessmode_lightsource, CInfoDarknessLightSource );
|
||||
|
||||
BEGIN_DATADESC( CInfoDarknessLightSource )
|
||||
DEFINE_KEYFIELD( m_flLightRadius, FIELD_FLOAT, "LightRadius" ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
|
||||
DEFINE_FIELD( m_bIgnoreLOS, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_THINKFUNC( DebugThink ),
|
||||
END_DATADESC()
|
||||
|
||||
CDarknessLightSourcesSystem g_DarknessLightSourcesSystem;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CDarknessLightSourcesSystem *DarknessLightSourcesSystem()
|
||||
{
|
||||
return &g_DarknessLightSourcesSystem;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDarknessLightSourcesSystem::LevelInitPreEntity()
|
||||
{
|
||||
m_LightSources.Purge();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDarknessLightSourcesSystem::AddLightSource( CInfoDarknessLightSource *pEntity, float flRadius )
|
||||
{
|
||||
lightsource_t sNewSource;
|
||||
sNewSource.hEntity = pEntity;
|
||||
sNewSource.flLightRadiusSqr = flRadius * flRadius;
|
||||
m_LightSources.AddToTail( sNewSource );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDarknessLightSourcesSystem::RemoveLightSource( CInfoDarknessLightSource *pEntity )
|
||||
{
|
||||
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||||
{
|
||||
if ( m_LightSources[i].hEntity == pEntity )
|
||||
{
|
||||
m_LightSources.Remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CDarknessLightSourcesSystem::IsEntityVisibleToTarget( CBaseEntity *pLooker, CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->IsEffectActive( EF_BRIGHTLIGHT ) || pTarget->IsEffectActive( EF_DIMLIGHT ) )
|
||||
return true;
|
||||
|
||||
bool bDebug = g_debug_darkness.GetBool();
|
||||
if ( bDebug && pLooker )
|
||||
{
|
||||
bDebug = (pLooker->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) != 0;
|
||||
}
|
||||
|
||||
trace_t tr;
|
||||
|
||||
// Loop through all the light sources. Do it backwards, so we can remove dead ones.
|
||||
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||||
{
|
||||
// Removed?
|
||||
if ( m_LightSources[i].hEntity == NULL || m_LightSources[i].hEntity->IsMarkedForDeletion() )
|
||||
{
|
||||
m_LightSources.FastRemove( i );
|
||||
continue;
|
||||
}
|
||||
|
||||
CInfoDarknessLightSource *pLightSource = m_LightSources[i].hEntity;
|
||||
|
||||
// Close enough to a light source?
|
||||
float flDistanceSqr = (pTarget->WorldSpaceCenter() - pLightSource->GetAbsOrigin()).LengthSqr();
|
||||
if ( flDistanceSqr < m_LightSources[i].flLightRadiusSqr )
|
||||
{
|
||||
if ( pLightSource->ShouldIgnoreLOS() )
|
||||
{
|
||||
if ( bDebug )
|
||||
{
|
||||
NDebugOverlay::Line( pTarget->WorldSpaceCenter(), pLightSource->GetAbsOrigin(), 0,255,0,true, 0.1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check LOS from the light to the target
|
||||
CTraceFilterSkipTwoEntities filter( pTarget, pLooker, COLLISION_GROUP_NONE );
|
||||
AI_TraceLine( pTarget->WorldSpaceCenter(), pLightSource->GetAbsOrigin(), MASK_BLOCKLOS, &filter, &tr );
|
||||
if ( tr.fraction == 1.0 )
|
||||
{
|
||||
if ( bDebug )
|
||||
{
|
||||
NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0,true, 0.1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( bDebug )
|
||||
{
|
||||
NDebugOverlay::Line( tr.startpos, tr.endpos, 255,0,0,true, 0.1);
|
||||
NDebugOverlay::Line( tr.endpos, pLightSource->GetAbsOrigin(), 128,0,0,true, 0.1);
|
||||
}
|
||||
|
||||
// If the target is within the radius of the light, don't do sillhouette checks
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !pLooker )
|
||||
continue;
|
||||
|
||||
// Between a light source and the looker?
|
||||
Vector vecLookerToLight = (pLightSource->GetAbsOrigin() - pLooker->WorldSpaceCenter());
|
||||
Vector vecLookerToTarget = (pTarget->WorldSpaceCenter() - pLooker->WorldSpaceCenter());
|
||||
float flDistToSource = VectorNormalize( vecLookerToLight );
|
||||
float flDistToTarget = VectorNormalize( vecLookerToTarget );
|
||||
float flDot = DotProduct( vecLookerToLight, vecLookerToTarget );
|
||||
if ( flDot > 0 )
|
||||
{
|
||||
// Make sure the target is in front of the lightsource
|
||||
if ( flDistToTarget < flDistToSource )
|
||||
{
|
||||
if ( bDebug )
|
||||
{
|
||||
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), pLooker->WorldSpaceCenter() + (vecLookerToLight * 128), 255,255,255,true, 0.1);
|
||||
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), pLooker->WorldSpaceCenter() + (vecLookerToTarget * 128), 255,0,0,true, 0.1);
|
||||
}
|
||||
|
||||
// Now, we need to find out if the light source is obscured by anything.
|
||||
// To do this, we want to calculate the point of intersection between the light source
|
||||
// sphere and the line from the looker through the target.
|
||||
float flASqr = (flDistToSource * flDistToSource);
|
||||
float flB = -2 * flDistToSource * flDot;
|
||||
float flCSqr = m_LightSources[i].flLightRadiusSqr;
|
||||
float flDesc = (flB * flB) - (4 * (flASqr - flCSqr));
|
||||
if ( flDesc >= 0 )
|
||||
{
|
||||
float flLength = (-flB - sqrt(flDesc)) / 2;
|
||||
Vector vecSpherePoint = pLooker->WorldSpaceCenter() + (vecLookerToTarget * flLength);
|
||||
|
||||
// We've got the point of intersection. See if we can see it.
|
||||
CTraceFilterSkipTwoEntities filter( pTarget, pLooker, COLLISION_GROUP_NONE );
|
||||
AI_TraceLine( pLooker->EyePosition(), vecSpherePoint, MASK_SOLID_BRUSHONLY, &filter, &tr );
|
||||
|
||||
if ( bDebug )
|
||||
{
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), vecSpherePoint, 255,0,0,true, 0.1);
|
||||
}
|
||||
else
|
||||
{
|
||||
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), vecSpherePoint, 0,255,0,true, 0.1);
|
||||
NDebugOverlay::Line( pLightSource->GetAbsOrigin(), vecSpherePoint, 255,0,0,true, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( tr.fraction == 1.0 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CDarknessLightSourcesSystem::AreThereLightSourcesWithinRadius( CBaseEntity *pLooker, float flRadius )
|
||||
{
|
||||
float flRadiusSqr = (flRadius * flRadius);
|
||||
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||||
{
|
||||
// Removed?
|
||||
if ( m_LightSources[i].hEntity == NULL || m_LightSources[i].hEntity->IsMarkedForDeletion() )
|
||||
{
|
||||
m_LightSources.FastRemove( i );
|
||||
continue;
|
||||
}
|
||||
|
||||
CBaseEntity *pLightSource = m_LightSources[i].hEntity;
|
||||
|
||||
// Close enough to a light source?
|
||||
float flDistanceSqr = (pLooker->WorldSpaceCenter() - pLightSource->GetAbsOrigin()).LengthSqr();
|
||||
if ( flDistanceSqr < flRadiusSqr )
|
||||
{
|
||||
trace_t tr;
|
||||
AI_TraceLine( pLooker->EyePosition(), pLightSource->GetAbsOrigin(), MASK_SOLID_BRUSHONLY, pLooker, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( g_debug_darkness.GetBool() )
|
||||
{
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), tr.endpos, 255,0,0,true, 0.1);
|
||||
}
|
||||
else
|
||||
{
|
||||
NDebugOverlay::Line( pLooker->WorldSpaceCenter(), tr.endpos, 0,255,0,true, 0.1);
|
||||
NDebugOverlay::Line( pLightSource->GetAbsOrigin(), tr.endpos, 255,0,0,true, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( tr.fraction == 1.0 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDarknessLightSourcesSystem::SetDebug( bool bDebug )
|
||||
{
|
||||
for ( int i = m_LightSources.Count() - 1; i >= 0; i-- )
|
||||
{
|
||||
CInfoDarknessLightSource *pLightSource = dynamic_cast<CInfoDarknessLightSource*>(m_LightSources[i].hEntity.Get());
|
||||
if ( pLightSource )
|
||||
{
|
||||
if ( bDebug )
|
||||
{
|
||||
pLightSource->SetThink( &CInfoDarknessLightSource::DebugThink );
|
||||
pLightSource->SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
else
|
||||
{
|
||||
pLightSource->SetThink( NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CV_Debug_Darkness( IConVar *pConVar, const char *pOldString, float flOldValue )
|
||||
{
|
||||
ConVarRef var( pConVar );
|
||||
DarknessLightSourcesSystem()->SetDebug( var.GetBool() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pEntity -
|
||||
//-----------------------------------------------------------------------------
|
||||
void AddEntityToDarknessCheck( CBaseEntity *pEntity, float flLightRadius /*=DARKNESS_LIGHTSOURCE_SIZE*/ )
|
||||
{
|
||||
// Create a light source, and attach it to the entity
|
||||
CInfoDarknessLightSource *pLightSource = (CInfoDarknessLightSource *) CreateEntityByName( "info_darknessmode_lightsource" );
|
||||
if ( pLightSource )
|
||||
{
|
||||
pLightSource->SetLightRadius( flLightRadius );
|
||||
DispatchSpawn( pLightSource );
|
||||
pLightSource->SetAbsOrigin( pEntity->WorldSpaceCenter() );
|
||||
pLightSource->SetParent( pEntity );
|
||||
pLightSource->Activate();
|
||||
|
||||
// Dynamically created darkness sources can ignore LOS
|
||||
// to match the (broken) visual representation of our dynamic lights.
|
||||
if ( darkness_ignore_LOS_to_sources.GetBool() )
|
||||
{
|
||||
pLightSource->IgnoreLOS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pEntity -
|
||||
//-----------------------------------------------------------------------------
|
||||
void RemoveEntityFromDarknessCheck( CBaseEntity *pEntity )
|
||||
{
|
||||
// Find any light sources parented to this entity, and remove them
|
||||
CBaseEntity *pChild = pEntity->FirstMoveChild();
|
||||
while ( pChild )
|
||||
{
|
||||
CBaseEntity *pPrevChild = pChild;
|
||||
pChild = pChild->NextMovePeer();
|
||||
|
||||
if ( dynamic_cast<CInfoDarknessLightSource*>(pPrevChild) )
|
||||
{
|
||||
UTIL_Remove( pPrevChild );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pEntity -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool LookerCouldSeeTargetInDarkness( CBaseEntity *pLooker, CBaseEntity *pTarget )
|
||||
{
|
||||
if ( DarknessLightSourcesSystem()->IsEntityVisibleToTarget( pLooker, pTarget ) )
|
||||
{
|
||||
//NDebugOverlay::Line( pTarget->WorldSpaceCenter(), pLooker->WorldSpaceCenter(), 0,255,0,true, 0.1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return true if there is at least 1 darkness light source within
|
||||
// the specified radius of the looker.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool DarknessLightSourceWithinRadius( CBaseEntity *pLooker, float flRadius )
|
||||
{
|
||||
return DarknessLightSourcesSystem()->AreThereLightSourcesWithinRadius( pLooker, flRadius );
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef INFO_DARKNESSMODE_LIGHTSOURCE_H
|
||||
#define INFO_DARKNESSMODE_LIGHTSOURCE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// Default distance from lightsources that entities are considered visible
|
||||
// NOTE!!! This is bigger by a factor of to deal with fixing a bug from HL2. See dlight_t.h
|
||||
#define DARKNESS_LIGHTSOURCE_SIZE (256.0f*1.2f)
|
||||
|
||||
void AddEntityToDarknessCheck( CBaseEntity *pEntity, float flLightRadius = DARKNESS_LIGHTSOURCE_SIZE );
|
||||
void RemoveEntityFromDarknessCheck( CBaseEntity *pEntity );
|
||||
bool LookerCouldSeeTargetInDarkness( CBaseEntity *pLooker, CBaseEntity *pTarget );
|
||||
bool DarknessLightSourceWithinRadius( CBaseEntity *pLooker, float flRadius );
|
||||
|
||||
#endif // INFO_DARKNESSMODE_LIGHTSOURCE_H
|
||||
@@ -0,0 +1,125 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Amount of time before breen teleports away
|
||||
//-----------------------------------------------------------------------------
|
||||
class CInfoTeleporterCountdown : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CInfoTeleporterCountdown, CPointEntity );
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
virtual int UpdateTransmitState();
|
||||
|
||||
private:
|
||||
void InputDisable(inputdata_t &inputdata);
|
||||
void InputEnable(inputdata_t &inputdata);
|
||||
void InputStartCountdown(inputdata_t &inputdata);
|
||||
void InputStopCountdown(inputdata_t &inputdata);
|
||||
|
||||
CNetworkVar( bool, m_bCountdownStarted );
|
||||
CNetworkVar( bool, m_bDisabled );
|
||||
CNetworkVar( float, m_flStartTime );
|
||||
CNetworkVar( float, m_flTimeRemaining );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Save/load
|
||||
//-----------------------------------------------------------------------------
|
||||
BEGIN_DATADESC( CInfoTeleporterCountdown )
|
||||
|
||||
DEFINE_FIELD( m_bCountdownStarted, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flTimeRemaining, FIELD_FLOAT ),
|
||||
|
||||
// Outputs
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "StartCountdown", InputStartCountdown ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "StopCountdown", InputStopCountdown ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( info_teleporter_countdown, CInfoTeleporterCountdown );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Networking
|
||||
//-----------------------------------------------------------------------------
|
||||
IMPLEMENT_SERVERCLASS_ST( CInfoTeleporterCountdown, DT_InfoTeleporterCountdown )
|
||||
SendPropInt( SENDINFO( m_bCountdownStarted ), 1, SPROP_UNSIGNED ),
|
||||
SendPropInt( SENDINFO( m_bDisabled ), 1, SPROP_UNSIGNED ),
|
||||
SendPropTime( SENDINFO( m_flStartTime ) ),
|
||||
SendPropFloat( SENDINFO( m_flTimeRemaining ), 0, SPROP_NOSCALE ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Starts/stops countdown
|
||||
//-----------------------------------------------------------------------------
|
||||
void CInfoTeleporterCountdown::InputStartCountdown(inputdata_t &inputdata)
|
||||
{
|
||||
if (!m_bCountdownStarted)
|
||||
{
|
||||
m_bCountdownStarted = true;
|
||||
m_bDisabled = false;
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
m_flTimeRemaining = inputdata.value.Float();
|
||||
}
|
||||
}
|
||||
|
||||
void CInfoTeleporterCountdown::InputStopCountdown(inputdata_t &inputdata)
|
||||
{
|
||||
m_bCountdownStarted = false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Disables/reenables an active countdown
|
||||
//-----------------------------------------------------------------------------
|
||||
void CInfoTeleporterCountdown::InputDisable(inputdata_t &inputdata)
|
||||
{
|
||||
if ( !m_bDisabled )
|
||||
{
|
||||
m_bDisabled = true;
|
||||
if ( m_bCountdownStarted )
|
||||
{
|
||||
m_flTimeRemaining -= gpGlobals->curtime - m_flStartTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CInfoTeleporterCountdown::InputEnable(inputdata_t &inputdata)
|
||||
{
|
||||
if ( m_bDisabled )
|
||||
{
|
||||
m_bDisabled = false;
|
||||
if ( m_bCountdownStarted )
|
||||
{
|
||||
m_flStartTime = gpGlobals->curtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Always send the teleporter countdown
|
||||
//-----------------------------------------------------------------------------
|
||||
int CInfoTeleporterCountdown::UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
@@ -0,0 +1,984 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: The various ammo types for HL2
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "ammodef.h"
|
||||
#include "eventlist.h"
|
||||
#include "npcevent.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Applies ammo quantity scale.
|
||||
//---------------------------------------------------------
|
||||
int ITEM_GiveAmmo( CBasePlayer *pPlayer, float flCount, const char *pszAmmoName, bool bSuppressSound = false )
|
||||
{
|
||||
int iAmmoType = GetAmmoDef()->Index(pszAmmoName);
|
||||
if (iAmmoType == -1)
|
||||
{
|
||||
Msg("ERROR: Attempting to give unknown ammo type (%s)\n",pszAmmoName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
flCount *= g_pGameRules->GetAmmoQuantityScale(iAmmoType);
|
||||
|
||||
// Don't give out less than 1 of anything.
|
||||
flCount = MAX( 1.0f, flCount );
|
||||
|
||||
return pPlayer->GiveAmmo( flCount, iAmmoType, bSuppressSound );
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// >> BoxSRounds
|
||||
// ========================================================================
|
||||
class CItem_BoxSRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxSRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxsrounds.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxsrounds.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_PISTOL, "Pistol"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_box_srounds, CItem_BoxSRounds);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_pistol, CItem_BoxSRounds);
|
||||
|
||||
// ========================================================================
|
||||
// >> LargeBoxSRounds
|
||||
// ========================================================================
|
||||
class CItem_LargeBoxSRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_LargeBoxSRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxsrounds.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxsrounds.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_PISTOL_LARGE, "Pistol"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_large_box_srounds, CItem_LargeBoxSRounds);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_pistol_large, CItem_LargeBoxSRounds);
|
||||
|
||||
// ========================================================================
|
||||
// >> BoxMRounds
|
||||
// ========================================================================
|
||||
class CItem_BoxMRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxMRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxmrounds.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxmrounds.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_SMG1, "SMG1"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_box_mrounds, CItem_BoxMRounds);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_smg1, CItem_BoxMRounds);
|
||||
|
||||
// ========================================================================
|
||||
// >> LargeBoxMRounds
|
||||
// ========================================================================
|
||||
class CItem_LargeBoxMRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_LargeBoxMRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxmrounds.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxmrounds.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_SMG1_LARGE, "SMG1"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_large_box_mrounds, CItem_LargeBoxMRounds);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_smg1_large, CItem_LargeBoxMRounds);
|
||||
|
||||
// ========================================================================
|
||||
// >> BoxLRounds
|
||||
// ========================================================================
|
||||
class CItem_BoxLRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxLRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/combine_rifle_cartridge01.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/combine_rifle_cartridge01.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_AR2, "AR2"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_box_lrounds, CItem_BoxLRounds);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_ar2, CItem_BoxLRounds);
|
||||
|
||||
// ========================================================================
|
||||
// >> LargeBoxLRounds
|
||||
// ========================================================================
|
||||
class CItem_LargeBoxLRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_LargeBoxLRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/combine_rifle_cartridge01.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/combine_rifle_cartridge01.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_AR2_LARGE, "AR2"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_large_box_lrounds, CItem_LargeBoxLRounds);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_ar2_large, CItem_LargeBoxLRounds);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> CItem_Box357Rounds
|
||||
// ========================================================================
|
||||
class CItem_Box357Rounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_Box357Rounds, CItem );
|
||||
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/357ammo.mdl");
|
||||
}
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/357ammo.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_357, "357"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_357, CItem_Box357Rounds);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> CItem_LargeBox357Rounds
|
||||
// ========================================================================
|
||||
class CItem_LargeBox357Rounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_LargeBox357Rounds, CItem );
|
||||
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/357ammobox.mdl");
|
||||
}
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/357ammobox.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_357_LARGE, "357"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_357_large, CItem_LargeBox357Rounds);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> CItem_BoxXBowRounds
|
||||
// ========================================================================
|
||||
class CItem_BoxXBowRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxXBowRounds, CItem );
|
||||
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/crossbowrounds.mdl");
|
||||
}
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/crossbowrounds.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_CROSSBOW, "XBowBolt" ))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_crossbow, CItem_BoxXBowRounds);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> FlareRound
|
||||
// ========================================================================
|
||||
class CItem_FlareRound : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_FlareRound, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/flare.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/flare.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, 1, "FlareRound"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_flare_round, CItem_FlareRound);
|
||||
|
||||
// ========================================================================
|
||||
// >> BoxFlareRounds
|
||||
// ========================================================================
|
||||
#define SIZE_BOX_FLARE_ROUNDS 5
|
||||
|
||||
class CItem_BoxFlareRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxFlareRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxflares.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxflares.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_BOX_FLARE_ROUNDS, "FlareRound"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_box_flare_rounds, CItem_BoxFlareRounds);
|
||||
|
||||
// ========================================================================
|
||||
// RPG Round
|
||||
// ========================================================================
|
||||
class CItem_RPG_Round : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_RPG_Round, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/weapons/w_missile_closed.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/weapons/w_missile_closed.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_RPG_ROUND, "RPG_Round"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( item_ml_grenade, CItem_RPG_Round );
|
||||
LINK_ENTITY_TO_CLASS( item_rpg_round, CItem_RPG_Round );
|
||||
|
||||
// ========================================================================
|
||||
// >> AR2_Grenade
|
||||
// ========================================================================
|
||||
class CItem_AR2_Grenade : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_AR2_Grenade, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/ar2_grenade.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/ar2_grenade.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_SMG1_GRENADE, "SMG1_Grenade"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_ar2_grenade, CItem_AR2_Grenade);
|
||||
LINK_ENTITY_TO_CLASS(item_ammo_smg1_grenade, CItem_AR2_Grenade);
|
||||
|
||||
// ========================================================================
|
||||
// >> BoxSniperRounds
|
||||
// ========================================================================
|
||||
#define SIZE_BOX_SNIPER_ROUNDS 10
|
||||
|
||||
class CItem_BoxSniperRounds : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxSniperRounds, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxsniperrounds.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxsniperrounds.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_BOX_SNIPER_ROUNDS, "SniperRound"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_box_sniper_rounds, CItem_BoxSniperRounds);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> BoxBuckshot
|
||||
// ========================================================================
|
||||
class CItem_BoxBuckshot : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_BoxBuckshot, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/boxbuckshot.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/boxbuckshot.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_BUCKSHOT, "Buckshot"))
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(item_box_buckshot, CItem_BoxBuckshot);
|
||||
|
||||
// ========================================================================
|
||||
// >> CItem_AR2AltFireRound
|
||||
// ========================================================================
|
||||
class CItem_AR2AltFireRound : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_AR2AltFireRound, CItem );
|
||||
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheParticleSystem( "combineball" );
|
||||
PrecacheModel ("models/items/combine_rifle_ammo01.mdl");
|
||||
}
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/combine_rifle_ammo01.mdl");
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (ITEM_GiveAmmo( pPlayer, SIZE_AMMO_AR2_ALTFIRE, "AR2AltFire" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_ammo_ar2_altfire, CItem_AR2AltFireRound );
|
||||
|
||||
// ==================================================================
|
||||
// Ammo crate which will supply infinite ammo of the specified type
|
||||
// ==================================================================
|
||||
|
||||
// Ammo types
|
||||
enum
|
||||
{
|
||||
AMMOCRATE_SMALL_ROUNDS,
|
||||
AMMOCRATE_MEDIUM_ROUNDS,
|
||||
AMMOCRATE_LARGE_ROUNDS,
|
||||
AMMOCRATE_RPG_ROUNDS,
|
||||
AMMOCRATE_BUCKSHOT,
|
||||
AMMOCRATE_GRENADES,
|
||||
AMMOCRATE_357,
|
||||
AMMOCRATE_CROSSBOW,
|
||||
AMMOCRATE_AR2_ALTFIRE,
|
||||
AMMOCRATE_SMG_ALTFIRE,
|
||||
NUM_AMMO_CRATE_TYPES,
|
||||
};
|
||||
|
||||
// Ammo crate
|
||||
|
||||
class CItem_AmmoCrate : public CBaseAnimating
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_AmmoCrate, CBaseAnimating );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool CreateVPhysics( void );
|
||||
|
||||
virtual void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
void SetupCrate( void );
|
||||
void OnRestore( void );
|
||||
|
||||
//FIXME: May not want to have this used in a radius
|
||||
int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | (FCAP_IMPULSE_USE|FCAP_USE_IN_RADIUS)); };
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
void InputKill( inputdata_t &data );
|
||||
void CrateThink( void );
|
||||
|
||||
virtual int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
|
||||
protected:
|
||||
|
||||
int m_nAmmoType;
|
||||
int m_nAmmoIndex;
|
||||
|
||||
static const char *m_lpzModelNames[NUM_AMMO_CRATE_TYPES];
|
||||
static const char *m_lpzAmmoNames[NUM_AMMO_CRATE_TYPES];
|
||||
static int m_nAmmoAmounts[NUM_AMMO_CRATE_TYPES];
|
||||
static const char *m_pGiveWeapon[NUM_AMMO_CRATE_TYPES];
|
||||
|
||||
float m_flCloseTime;
|
||||
COutputEvent m_OnUsed;
|
||||
CHandle< CBasePlayer > m_hActivator;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_ammo_crate, CItem_AmmoCrate );
|
||||
|
||||
BEGIN_DATADESC( CItem_AmmoCrate )
|
||||
|
||||
DEFINE_KEYFIELD( m_nAmmoType, FIELD_INTEGER, "AmmoType" ),
|
||||
|
||||
DEFINE_FIELD( m_flCloseTime, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
|
||||
|
||||
// These can be recreated
|
||||
//DEFINE_FIELD( m_nAmmoIndex, FIELD_INTEGER ),
|
||||
//DEFINE_FIELD( m_lpzModelNames, FIELD_ ),
|
||||
//DEFINE_FIELD( m_lpzAmmoNames, FIELD_ ),
|
||||
//DEFINE_FIELD( m_nAmmoAmounts, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnUsed, "OnUsed" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ),
|
||||
|
||||
DEFINE_THINKFUNC( CrateThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Animation events.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Models names
|
||||
const char *CItem_AmmoCrate::m_lpzModelNames[NUM_AMMO_CRATE_TYPES] =
|
||||
{
|
||||
"models/items/ammocrate_pistol.mdl", // Small rounds
|
||||
"models/items/ammocrate_smg1.mdl", // Medium rounds
|
||||
"models/items/ammocrate_ar2.mdl", // Large rounds
|
||||
"models/items/ammocrate_rockets.mdl", // RPG rounds
|
||||
"models/items/ammocrate_buckshot.mdl", // Buckshot
|
||||
"models/items/ammocrate_grenade.mdl", // Grenades
|
||||
"models/items/ammocrate_smg1.mdl", // 357
|
||||
"models/items/ammocrate_smg1.mdl", // Crossbow
|
||||
|
||||
//FIXME: This model is incorrect!
|
||||
"models/items/ammocrate_ar2.mdl", // Combine Ball
|
||||
"models/items/ammocrate_smg2.mdl", // smg grenade
|
||||
};
|
||||
|
||||
// Ammo type names
|
||||
const char *CItem_AmmoCrate::m_lpzAmmoNames[NUM_AMMO_CRATE_TYPES] =
|
||||
{
|
||||
"Pistol",
|
||||
"SMG1",
|
||||
"AR2",
|
||||
"RPG_Round",
|
||||
"Buckshot",
|
||||
"Grenade",
|
||||
"357",
|
||||
"XBowBolt",
|
||||
"AR2AltFire",
|
||||
"SMG1_Grenade",
|
||||
};
|
||||
|
||||
// Ammo amount given per +use
|
||||
int CItem_AmmoCrate::m_nAmmoAmounts[NUM_AMMO_CRATE_TYPES] =
|
||||
{
|
||||
300, // Pistol
|
||||
300, // SMG1
|
||||
300, // AR2
|
||||
3, // RPG rounds
|
||||
120, // Buckshot
|
||||
5, // Grenades
|
||||
50, // 357
|
||||
50, // Crossbow
|
||||
3, // AR2 alt-fire
|
||||
5,
|
||||
};
|
||||
|
||||
const char *CItem_AmmoCrate::m_pGiveWeapon[NUM_AMMO_CRATE_TYPES] =
|
||||
{
|
||||
NULL, // Pistol
|
||||
NULL, // SMG1
|
||||
NULL, // AR2
|
||||
NULL, // RPG rounds
|
||||
NULL, // Buckshot
|
||||
"weapon_frag", // Grenades
|
||||
NULL, // 357
|
||||
NULL, // Crossbow
|
||||
NULL, // AR2 alt-fire
|
||||
NULL, // SMG alt-fire
|
||||
};
|
||||
|
||||
#define AMMO_CRATE_CLOSE_DELAY 1.5f
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetSolid( SOLID_VPHYSICS );
|
||||
CreateVPhysics();
|
||||
|
||||
ResetSequence( LookupSequence( "Idle" ) );
|
||||
SetBodygroup( 1, true );
|
||||
|
||||
m_flCloseTime = gpGlobals->curtime;
|
||||
m_flAnimTime = gpGlobals->curtime;
|
||||
m_flPlaybackRate = 0.0;
|
||||
SetCycle( 0 );
|
||||
|
||||
m_takedamage = DAMAGE_EVENTS_ONLY;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//------------------------------------------------------------------------------
|
||||
bool CItem_AmmoCrate::CreateVPhysics( void )
|
||||
{
|
||||
return ( VPhysicsInitStatic() != NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::Precache( void )
|
||||
{
|
||||
SetupCrate();
|
||||
PrecacheModel( STRING( GetModelName() ) );
|
||||
|
||||
PrecacheScriptSound( "AmmoCrate.Open" );
|
||||
PrecacheScriptSound( "AmmoCrate.Close" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::SetupCrate( void )
|
||||
{
|
||||
SetModelName( AllocPooledString( m_lpzModelNames[m_nAmmoType] ) );
|
||||
|
||||
m_nAmmoIndex = GetAmmoDef()->Index( m_lpzAmmoNames[m_nAmmoType] );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::OnRestore( void )
|
||||
{
|
||||
BaseClass::OnRestore();
|
||||
|
||||
// Restore our internal state
|
||||
SetupCrate();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pActivator -
|
||||
// *pCaller -
|
||||
// useType -
|
||||
// value -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( pActivator );
|
||||
|
||||
if ( pPlayer == NULL )
|
||||
return;
|
||||
|
||||
m_OnUsed.FireOutput( pActivator, this );
|
||||
|
||||
int iSequence = LookupSequence( "Open" );
|
||||
|
||||
// See if we're not opening already
|
||||
if ( GetSequence() != iSequence )
|
||||
{
|
||||
Vector mins, maxs;
|
||||
trace_t tr;
|
||||
|
||||
CollisionProp()->WorldSpaceAABB( &mins, &maxs );
|
||||
|
||||
Vector vOrigin = GetAbsOrigin();
|
||||
vOrigin.z += ( maxs.z - mins.z );
|
||||
mins = (mins - GetAbsOrigin()) * 0.2f;
|
||||
maxs = (maxs - GetAbsOrigin()) * 0.2f;
|
||||
mins.z = ( GetAbsOrigin().z - vOrigin.z );
|
||||
|
||||
UTIL_TraceHull( vOrigin, vOrigin, mins, maxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.startsolid || tr.allsolid )
|
||||
return;
|
||||
|
||||
m_hActivator = pPlayer;
|
||||
|
||||
// Animate!
|
||||
ResetSequence( iSequence );
|
||||
|
||||
// Make sound
|
||||
CPASAttenuationFilter sndFilter( this, "AmmoCrate.Open" );
|
||||
EmitSound( sndFilter, entindex(), "AmmoCrate.Open" );
|
||||
|
||||
// Start thinking to make it return
|
||||
SetThink( &CItem_AmmoCrate::CrateThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
// Don't close again for two seconds
|
||||
m_flCloseTime = gpGlobals->curtime + AMMO_CRATE_CLOSE_DELAY;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: allows the crate to open up when hit by a crowbar
|
||||
//-----------------------------------------------------------------------------
|
||||
int CItem_AmmoCrate::OnTakeDamage( const CTakeDamageInfo &info )
|
||||
{
|
||||
// if it's the player hitting us with a crowbar, open up
|
||||
CBasePlayer *player = ToBasePlayer(info.GetAttacker());
|
||||
if (player)
|
||||
{
|
||||
CBaseCombatWeapon *weapon = player->GetActiveWeapon();
|
||||
|
||||
if (weapon && !stricmp(weapon->GetName(), "weapon_crowbar"))
|
||||
{
|
||||
// play the normal use sound
|
||||
player->EmitSound( "HL2Player.Use" );
|
||||
// open the crate
|
||||
Use(info.GetAttacker(), info.GetAttacker(), USE_TOGGLE, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// don't actually take any damage
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches the monster-specific messages that occur when tagged
|
||||
// animation frames are played.
|
||||
// Input : *pEvent -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
if ( pEvent->event == AE_AMMOCRATE_PICKUP_AMMO )
|
||||
{
|
||||
if ( m_hActivator )
|
||||
{
|
||||
if ( m_pGiveWeapon[m_nAmmoType] && !m_hActivator->Weapon_OwnsThisType( m_pGiveWeapon[m_nAmmoType] ) )
|
||||
{
|
||||
CBaseEntity *pEntity = CreateEntityByName( m_pGiveWeapon[m_nAmmoType] );
|
||||
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>(pEntity);
|
||||
if ( pWeapon )
|
||||
{
|
||||
pWeapon->SetAbsOrigin( m_hActivator->GetAbsOrigin() );
|
||||
pWeapon->m_iPrimaryAmmoType = 0;
|
||||
pWeapon->m_iSecondaryAmmoType = 0;
|
||||
pWeapon->Spawn();
|
||||
if ( !m_hActivator->BumpWeapon( pWeapon ) )
|
||||
{
|
||||
UTIL_Remove( pEntity );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetBodygroup( 1, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_hActivator->GiveAmmo( m_nAmmoAmounts[m_nAmmoType], m_nAmmoIndex ) != 0 )
|
||||
{
|
||||
SetBodygroup( 1, false );
|
||||
}
|
||||
m_hActivator = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::CrateThink( void )
|
||||
{
|
||||
StudioFrameAdvance();
|
||||
DispatchAnimEvents( this );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
// Start closing if we're not already
|
||||
if ( GetSequence() != LookupSequence( "Close" ) )
|
||||
{
|
||||
// Not ready to close?
|
||||
if ( m_flCloseTime <= gpGlobals->curtime )
|
||||
{
|
||||
m_hActivator = NULL;
|
||||
|
||||
ResetSequence( LookupSequence( "Close" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if we're fully closed
|
||||
if ( IsSequenceFinished() )
|
||||
{
|
||||
// Stop thinking
|
||||
SetThink( NULL );
|
||||
CPASAttenuationFilter sndFilter( this, "AmmoCrate.Close" );
|
||||
EmitSound( sndFilter, entindex(), "AmmoCrate.Close" );
|
||||
|
||||
// FIXME: We're resetting the sequence here
|
||||
// but setting Think to NULL will cause this to never have
|
||||
// StudioFrameAdvance called. What are the consequences of that?
|
||||
ResetSequence( LookupSequence( "Idle" ) );
|
||||
SetBodygroup( 1, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_AmmoCrate::InputKill( inputdata_t &data )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== item_antidote.cpp ========================================================
|
||||
|
||||
handling for the antidote object
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
|
||||
class CItemAntidote : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemAntidote, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/w_antidote.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/w_antidote.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
pPlayer->SetSuitUpdate("!HEV_DET4", FALSE, SUIT_NEXT_IN_1MIN);
|
||||
|
||||
pPlayer->m_rgItems[ITEM_ANTIDOTE] += 1;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_antidote, CItemAntidote);
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Handling for the suit batteries.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl2_player.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CItemBattery : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemBattery, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/battery.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/battery.mdl");
|
||||
|
||||
PrecacheScriptSound( "ItemBattery.Touch" );
|
||||
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player *>( pPlayer );
|
||||
return ( pHL2Player && pHL2Player->ApplyBattery() );
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_battery, CItemBattery);
|
||||
PRECACHE_REGISTER(item_battery);
|
||||
|
||||
@@ -0,0 +1,657 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "item_dynamic_resupply.h"
|
||||
#include "props.h"
|
||||
#include "items.h"
|
||||
#include "ammodef.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar sk_dynamic_resupply_modifier( "sk_dynamic_resupply_modifier","1.0" );
|
||||
extern ConVar sk_battery;
|
||||
extern ConVar sk_healthkit;
|
||||
|
||||
ConVar g_debug_dynamicresupplies( "g_debug_dynamicresupplies", "0", FCVAR_NONE, "Debug item_dynamic_resupply spawning. Set to 1 to see text printouts of the spawning. Set to 2 to see lines drawn to other items factored into the spawning." );
|
||||
|
||||
struct DynamicResupplyItems_t
|
||||
{
|
||||
const char *sEntityName;
|
||||
const char *sAmmoDef;
|
||||
int iAmmoCount;
|
||||
float flFullProbability; // Probability of spawning if the player meeds all goals
|
||||
};
|
||||
|
||||
struct SpawnInfo_t
|
||||
{
|
||||
float m_flDesiredRatio;
|
||||
float m_flCurrentRatio;
|
||||
float m_flDelta;
|
||||
int m_iPotentialItems;
|
||||
};
|
||||
|
||||
|
||||
// Health types
|
||||
static DynamicResupplyItems_t g_DynamicResupplyHealthItems[] =
|
||||
{
|
||||
{ "item_healthkit", "Health", 0, 0.0f, },
|
||||
{ "item_battery", "Armor", 0, 0.0f },
|
||||
};
|
||||
|
||||
// Ammo types
|
||||
static DynamicResupplyItems_t g_DynamicResupplyAmmoItems[] =
|
||||
{
|
||||
{ "item_ammo_pistol", "Pistol", SIZE_AMMO_PISTOL, 0.5f },
|
||||
{ "item_ammo_smg1", "SMG1", SIZE_AMMO_SMG1, 0.4f },
|
||||
{ "item_ammo_smg1_grenade", "SMG1_Grenade", SIZE_AMMO_SMG1_GRENADE, 0.0f },
|
||||
{ "item_ammo_ar2", "AR2", SIZE_AMMO_AR2, 0.0f },
|
||||
{ "item_box_buckshot", "Buckshot", SIZE_AMMO_BUCKSHOT, 0.0f },
|
||||
{ "item_rpg_round", "RPG_Round", SIZE_AMMO_RPG_ROUND, 0.0f },
|
||||
{ "weapon_frag", "Grenade", 1, 0.1f },
|
||||
{ "item_ammo_357", "357", SIZE_AMMO_357, 0.0f },
|
||||
{ "item_ammo_crossbow", "XBowBolt", SIZE_AMMO_CROSSBOW, 0.0f },
|
||||
{ "item_ammo_ar2_altfire", "AR2AltFire", SIZE_AMMO_AR2_ALTFIRE, 0.0f },
|
||||
};
|
||||
|
||||
#define DS_HEALTH_INDEX 0
|
||||
#define DS_ARMOR_INDEX 1
|
||||
#define DS_GRENADE_INDEX 6
|
||||
|
||||
#define NUM_HEALTH_ITEMS (ARRAYSIZE(g_DynamicResupplyHealthItems))
|
||||
#define NUM_AMMO_ITEMS (ARRAYSIZE(g_DynamicResupplyAmmoItems))
|
||||
|
||||
#define DYNAMIC_ITEM_THINK 1.0
|
||||
|
||||
#define POTENTIAL_ITEM_RADIUS 1024
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: An item that dynamically decides what the player needs most and spawns that.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CItem_DynamicResupply : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CItem_DynamicResupply, CPointEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CItem_DynamicResupply();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Activate( void );
|
||||
void CheckPVSThink( void );
|
||||
|
||||
// Inputs
|
||||
void InputKill( inputdata_t &data );
|
||||
void InputCalculateType( inputdata_t &data );
|
||||
void InputBecomeMaster( inputdata_t &data );
|
||||
|
||||
float GetDesiredHealthPercentage( void ) const { return m_flDesiredHealth[0]; }
|
||||
|
||||
private:
|
||||
friend void DynamicResupply_InitFromAlternateMaster( CBaseEntity *pTargetEnt, string_t iszMaster );
|
||||
void FindPotentialItems( int nCount, DynamicResupplyItems_t *pItems, int iDebug, SpawnInfo_t *pSpawnInfo );
|
||||
void ComputeHealthRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo );
|
||||
void ComputeAmmoRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo );
|
||||
bool SpawnItemFromRatio( int nCount, DynamicResupplyItems_t *pItems, int iDebug, SpawnInfo_t *pSpawnInfo, Vector *pVecSpawnOrigin );
|
||||
|
||||
// Spawns an item when the player is full
|
||||
void SpawnFullItem( CItem_DynamicResupply *pMaster, CBasePlayer *pPlayer, int iDebug );
|
||||
void SpawnDynamicItem( CBasePlayer *pPlayer );
|
||||
|
||||
enum Versions
|
||||
{
|
||||
VERSION_0,
|
||||
VERSION_1_PERSISTENT_MASTER,
|
||||
|
||||
VERSION_CURRENT = VERSION_1_PERSISTENT_MASTER,
|
||||
};
|
||||
|
||||
int m_version;
|
||||
float m_flDesiredHealth[ NUM_HEALTH_ITEMS ];
|
||||
float m_flDesiredAmmo[ NUM_AMMO_ITEMS ];
|
||||
|
||||
bool m_bIsMaster;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_dynamic_resupply, CItem_DynamicResupply);
|
||||
|
||||
// Master
|
||||
typedef CHandle<CItem_DynamicResupply> DynamicResupplyHandle_t;
|
||||
|
||||
static DynamicResupplyHandle_t g_MasterResupply;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Save/load:
|
||||
//-----------------------------------------------------------------------------
|
||||
BEGIN_DATADESC( CItem_DynamicResupply )
|
||||
|
||||
DEFINE_THINKFUNC( CheckPVSThink ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "CalculateType", InputCalculateType ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "BecomeMaster", InputBecomeMaster ),
|
||||
|
||||
DEFINE_KEYFIELD( m_flDesiredHealth[0], FIELD_FLOAT, "DesiredHealth" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredHealth[1], FIELD_FLOAT, "DesiredArmor" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[0], FIELD_FLOAT, "DesiredAmmoPistol" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[1], FIELD_FLOAT, "DesiredAmmoSMG1" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[2], FIELD_FLOAT, "DesiredAmmoSMG1_Grenade" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[3], FIELD_FLOAT, "DesiredAmmoAR2" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[4], FIELD_FLOAT, "DesiredAmmoBuckshot" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[5], FIELD_FLOAT, "DesiredAmmoRPG_Round" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[6], FIELD_FLOAT, "DesiredAmmoGrenade" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[7], FIELD_FLOAT, "DesiredAmmo357" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[8], FIELD_FLOAT, "DesiredAmmoCrossbow" ),
|
||||
DEFINE_KEYFIELD( m_flDesiredAmmo[9], FIELD_FLOAT, "DesiredAmmoAR2_AltFire" ),
|
||||
|
||||
DEFINE_FIELD( m_version, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_bIsMaster, FIELD_BOOLEAN ),
|
||||
|
||||
// Silence, Classcheck!
|
||||
// DEFINE_ARRAY( m_flDesiredHealth, FIELD_FLOAT, NUM_HEALTH_ITEMS ),
|
||||
// DEFINE_ARRAY( m_flDesiredAmmo, FIELD_FLOAT, NUM_AMMO_ITEMS ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CItem_DynamicResupply::CItem_DynamicResupply( void )
|
||||
{
|
||||
AddSpawnFlags( SF_DYNAMICRESUPPLY_USE_MASTER );
|
||||
m_version = VERSION_CURRENT;
|
||||
|
||||
// Setup default values
|
||||
m_flDesiredHealth[0] = 1.0; // Health
|
||||
m_flDesiredHealth[1] = 0.3; // Armor
|
||||
m_flDesiredAmmo[0] = 0.5; // Pistol
|
||||
m_flDesiredAmmo[1] = 0.5; // SMG1
|
||||
m_flDesiredAmmo[2] = 0.1; // SMG1 Grenade
|
||||
m_flDesiredAmmo[3] = 0.4; // AR2
|
||||
m_flDesiredAmmo[4] = 0.5; // Shotgun
|
||||
m_flDesiredAmmo[5] = 0.0; // RPG Round
|
||||
m_flDesiredAmmo[6] = 0.1; // Grenade
|
||||
m_flDesiredAmmo[7] = 0; // 357
|
||||
m_flDesiredAmmo[8] = 0; // Crossbow
|
||||
m_flDesiredAmmo[9] = 0; // AR2 alt-fire
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::Spawn( void )
|
||||
{
|
||||
if ( g_pGameRules->IsAllowedToSpawn( this ) == false )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't callback to spawn
|
||||
Precache();
|
||||
|
||||
m_bIsMaster = HasSpawnFlags( SF_DYNAMICRESUPPLY_IS_MASTER );
|
||||
|
||||
// Am I the master?
|
||||
if ( !HasSpawnFlags( SF_DYNAMICRESUPPLY_IS_MASTER | SF_DYNAMICRESUPPLY_ALTERNATE_MASTER ) )
|
||||
{
|
||||
// Stagger the thinks a bit so they don't all think at the same time
|
||||
SetNextThink( gpGlobals->curtime + RandomFloat(0.2f, 0.4f) );
|
||||
SetThink( &CItem_DynamicResupply::CheckPVSThink );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::Activate( void )
|
||||
{
|
||||
BaseClass::Activate();
|
||||
|
||||
if ( HasSpawnFlags( SF_DYNAMICRESUPPLY_IS_MASTER ) )
|
||||
{
|
||||
if ( !g_MasterResupply && ( m_bIsMaster || m_version < VERSION_1_PERSISTENT_MASTER ) )
|
||||
{
|
||||
g_MasterResupply = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bIsMaster = false;
|
||||
}
|
||||
}
|
||||
if ( !HasSpawnFlags( SF_DYNAMICRESUPPLY_ALTERNATE_MASTER ) && HasSpawnFlags( SF_DYNAMICRESUPPLY_USE_MASTER ) && gpGlobals->curtime < 1.0 )
|
||||
{
|
||||
if ( !g_MasterResupply )
|
||||
{
|
||||
Warning( "item_dynamic_resupply set to 'Use Master', but no item_dynamic_resupply master exists.\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::Precache( void )
|
||||
{
|
||||
// Precache all the items potentially spawned
|
||||
int i;
|
||||
for ( i = 0; i < NUM_HEALTH_ITEMS; i++ )
|
||||
{
|
||||
UTIL_PrecacheOther( g_DynamicResupplyHealthItems[i].sEntityName );
|
||||
}
|
||||
|
||||
for ( i = 0; i < NUM_AMMO_ITEMS; i++ )
|
||||
{
|
||||
UTIL_PrecacheOther( g_DynamicResupplyAmmoItems[i].sEntityName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::CheckPVSThink( void )
|
||||
{
|
||||
edict_t *pentPlayer = UTIL_FindClientInPVS( edict() );
|
||||
if ( pentPlayer )
|
||||
{
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pentPlayer );
|
||||
if ( pPlayer )
|
||||
{
|
||||
SpawnDynamicItem( pPlayer );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + DYNAMIC_ITEM_THINK );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::InputKill( inputdata_t &data )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::InputCalculateType( inputdata_t &data )
|
||||
{
|
||||
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
||||
SpawnDynamicItem( pPlayer );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::InputBecomeMaster( inputdata_t &data )
|
||||
{
|
||||
if ( g_MasterResupply )
|
||||
g_MasterResupply->m_bIsMaster = false;
|
||||
|
||||
g_MasterResupply = this;
|
||||
m_bIsMaster = true;
|
||||
|
||||
// Stop thinking now that I am the master.
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Chooses an item when the player is full
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBasePlayer *pPlayer, int iDebug )
|
||||
{
|
||||
// Can we not actually spawn the item?
|
||||
if ( !HasSpawnFlags(SF_DYNAMICRESUPPLY_ALWAYS_SPAWN) )
|
||||
return;
|
||||
|
||||
float flRatio[NUM_AMMO_ITEMS];
|
||||
int i;
|
||||
float flTotalProb = 0.0f;
|
||||
for ( i = 0; i < NUM_AMMO_ITEMS; ++i )
|
||||
{
|
||||
int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef );
|
||||
bool bCanSpawn = pPlayer->Weapon_GetWpnForAmmo( iAmmoType ) != NULL;
|
||||
|
||||
if ( bCanSpawn && ( g_DynamicResupplyAmmoItems[i].flFullProbability != 0 ) && ( pMaster->m_flDesiredAmmo[i] != 0.0f ) )
|
||||
{
|
||||
flTotalProb += g_DynamicResupplyAmmoItems[i].flFullProbability;
|
||||
flRatio[i] = flTotalProb;
|
||||
}
|
||||
else
|
||||
{
|
||||
flRatio[i] = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if ( flTotalProb == 0.0f )
|
||||
{
|
||||
// If we're supposed to fallback to just a health vial, do that and finish.
|
||||
if ( pMaster->HasSpawnFlags(SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL) )
|
||||
{
|
||||
CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles(), this );
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Player is full, spawning item_healthvial due to spawnflag.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, spawn the first ammo item in the list
|
||||
flRatio[0] = 1.0f;
|
||||
flTotalProb = 1.0f;
|
||||
}
|
||||
|
||||
float flChoice = random->RandomFloat( 0.0f, flTotalProb );
|
||||
for ( i = 0; i < NUM_AMMO_ITEMS; ++i )
|
||||
{
|
||||
if ( flChoice <= flRatio[i] )
|
||||
{
|
||||
CBaseEntity::Create( g_DynamicResupplyAmmoItems[i].sEntityName, GetAbsOrigin(), GetAbsAngles(), this );
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Player is full, spawning %s \n", g_DynamicResupplyAmmoItems[i].sEntityName );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Player is full on all health + ammo, is not spawning.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::FindPotentialItems( int nCount, DynamicResupplyItems_t *pItems, int iDebug, SpawnInfo_t *pSpawnInfo )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < nCount; ++i )
|
||||
{
|
||||
pSpawnInfo[i].m_iPotentialItems = 0;
|
||||
}
|
||||
|
||||
// Count the potential addition of items in the PVS
|
||||
CBaseEntity *pEntity = NULL;
|
||||
while ( (pEntity = UTIL_EntitiesInPVS( this, pEntity )) != NULL )
|
||||
{
|
||||
if ( pEntity->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() ) > (POTENTIAL_ITEM_RADIUS * POTENTIAL_ITEM_RADIUS) )
|
||||
continue;
|
||||
|
||||
for ( i = 0; i < nCount; ++i )
|
||||
{
|
||||
if ( !FClassnameIs( pEntity, pItems[i].sEntityName ) )
|
||||
continue;
|
||||
|
||||
if ( iDebug == 2 )
|
||||
{
|
||||
NDebugOverlay::Line( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), 0,255,0, true, 20.0 );
|
||||
}
|
||||
|
||||
++pSpawnInfo[i].m_iPotentialItems;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Searching the PVS:\n");
|
||||
for ( int i = 0; i < nCount; i++ )
|
||||
{
|
||||
Msg(" Found %d '%s' in the PVS.\n", pSpawnInfo[i].m_iPotentialItems, pItems[i].sEntityName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::ComputeHealthRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo )
|
||||
{
|
||||
for ( int i = 0; i < NUM_HEALTH_ITEMS; i++ )
|
||||
{
|
||||
// Figure out the current level of this resupply type
|
||||
float flMax;
|
||||
if ( i == DS_HEALTH_INDEX )
|
||||
{
|
||||
// Health
|
||||
flMax = pPlayer->GetMaxHealth();
|
||||
|
||||
float flCurrentHealth = pPlayer->GetHealth() + (pSpawnInfo[i].m_iPotentialItems * sk_healthkit.GetFloat());
|
||||
pSpawnInfo[i].m_flCurrentRatio = (flCurrentHealth / flMax);
|
||||
}
|
||||
else if ( i == DS_ARMOR_INDEX )
|
||||
{
|
||||
// Armor
|
||||
// Ignore armor if we don't have the suit
|
||||
if ( !pPlayer->IsSuitEquipped() )
|
||||
{
|
||||
pSpawnInfo[i].m_flCurrentRatio = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flMax = MAX_NORMAL_BATTERY;
|
||||
float flCurrentArmor = pPlayer->ArmorValue() + (pSpawnInfo[i].m_iPotentialItems * sk_battery.GetFloat());
|
||||
pSpawnInfo[i].m_flCurrentRatio = (flCurrentArmor / flMax);
|
||||
}
|
||||
}
|
||||
|
||||
pSpawnInfo[i].m_flDesiredRatio = pMaster->m_flDesiredHealth[i] * sk_dynamic_resupply_modifier.GetFloat();
|
||||
pSpawnInfo[i].m_flDelta = pSpawnInfo[i].m_flDesiredRatio - pSpawnInfo[i].m_flCurrentRatio;
|
||||
pSpawnInfo[i].m_flDelta = clamp( pSpawnInfo[i].m_flDelta, 0, 1 );
|
||||
}
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Calculating desired health ratios & deltas:\n");
|
||||
for ( int i = 0; i < NUM_HEALTH_ITEMS; i++ )
|
||||
{
|
||||
Msg(" %s Desired Ratio: %.2f, Current Ratio: %.2f = Delta of %.2f\n",
|
||||
g_DynamicResupplyHealthItems[i].sEntityName, pSpawnInfo[i].m_flDesiredRatio, pSpawnInfo[i].m_flCurrentRatio, pSpawnInfo[i].m_flDelta );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::ComputeAmmoRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo )
|
||||
{
|
||||
for ( int i = 0; i < NUM_AMMO_ITEMS; i++ )
|
||||
{
|
||||
// Get the ammodef's
|
||||
int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef );
|
||||
Assert( iAmmoType != -1 );
|
||||
|
||||
// Ignore ammo types if we don't have a weapon that uses it (except for the grenade)
|
||||
if ( (i != DS_GRENADE_INDEX) && !pPlayer->Weapon_GetWpnForAmmo( iAmmoType ) )
|
||||
{
|
||||
pSpawnInfo[i].m_flCurrentRatio = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float flMax = GetAmmoDef()->MaxCarry( iAmmoType );
|
||||
float flCurrentAmmo = pPlayer->GetAmmoCount( iAmmoType );
|
||||
flCurrentAmmo += (pSpawnInfo[i].m_iPotentialItems * g_DynamicResupplyAmmoItems[i].iAmmoCount);
|
||||
pSpawnInfo[i].m_flCurrentRatio = (flCurrentAmmo / flMax);
|
||||
}
|
||||
|
||||
// Use the master if we're supposed to
|
||||
pSpawnInfo[i].m_flDesiredRatio = pMaster->m_flDesiredAmmo[i] * sk_dynamic_resupply_modifier.GetFloat();
|
||||
pSpawnInfo[i].m_flDelta = pSpawnInfo[i].m_flDesiredRatio - pSpawnInfo[i].m_flCurrentRatio;
|
||||
pSpawnInfo[i].m_flDelta = clamp( pSpawnInfo[i].m_flDelta, 0, 1 );
|
||||
}
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Calculating desired ammo ratios & deltas:\n");
|
||||
for ( int i = 0; i < NUM_AMMO_ITEMS; i++ )
|
||||
{
|
||||
Msg(" %s Desired Ratio: %.2f, Current Ratio: %.2f = Delta of %.2f\n",
|
||||
g_DynamicResupplyAmmoItems[i].sEntityName, pSpawnInfo[i].m_flDesiredRatio, pSpawnInfo[i].m_flCurrentRatio, pSpawnInfo[i].m_flDelta );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CItem_DynamicResupply::SpawnItemFromRatio( int nCount, DynamicResupplyItems_t *pItems, int iDebug, SpawnInfo_t *pSpawnInfo, Vector *pVecSpawnOrigin )
|
||||
{
|
||||
// Now find the one we're farthest from
|
||||
float flFarthest = 0;
|
||||
int iSelectedIndex = -1;
|
||||
for ( int i = 0; i < nCount; ++i )
|
||||
{
|
||||
if ( pSpawnInfo[i].m_flDelta > flFarthest )
|
||||
{
|
||||
flFarthest = pSpawnInfo[i].m_flDelta;
|
||||
iSelectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ( iSelectedIndex < 0 )
|
||||
return false;
|
||||
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Chosen item: %s (had farthest delta, %.2f)\n", pItems[iSelectedIndex].sEntityName, pSpawnInfo[iSelectedIndex].m_flDelta );
|
||||
}
|
||||
|
||||
CBaseEntity *pEnt = CBaseEntity::Create( pItems[iSelectedIndex].sEntityName, *pVecSpawnOrigin, GetAbsAngles(), this );
|
||||
pEnt->SetAbsVelocity( GetAbsVelocity() );
|
||||
pEnt->SetLocalAngularVelocity( GetLocalAngularVelocity() );
|
||||
|
||||
// Move the entity up so that it doesn't go below the spawn origin
|
||||
Vector vecWorldMins, vecWorldMaxs;
|
||||
pEnt->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs );
|
||||
if ( vecWorldMins.z < pVecSpawnOrigin->z )
|
||||
{
|
||||
float dz = pVecSpawnOrigin->z - vecWorldMins.z;
|
||||
pVecSpawnOrigin->z += dz;
|
||||
vecWorldMaxs.z += dz;
|
||||
pEnt->SetAbsOrigin( *pVecSpawnOrigin );
|
||||
}
|
||||
|
||||
// Update the spawn position to spawn them on top of each other
|
||||
pVecSpawnOrigin->z = vecWorldMaxs.z + 6.0f;
|
||||
|
||||
pVecSpawnOrigin->x += random->RandomFloat( -6, 6 );
|
||||
pVecSpawnOrigin->y += random->RandomFloat( -6, 6 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_DynamicResupply::SpawnDynamicItem( CBasePlayer *pPlayer )
|
||||
{
|
||||
Assert( pPlayer );
|
||||
|
||||
// If we're the master, we never want to spawn
|
||||
if ( g_MasterResupply == this )
|
||||
return;
|
||||
|
||||
int iDebug = g_debug_dynamicresupplies.GetInt();
|
||||
if ( iDebug )
|
||||
{
|
||||
Msg("Spawning item_dynamic_resupply:\n");
|
||||
}
|
||||
|
||||
SpawnInfo_t pAmmoInfo[ NUM_AMMO_ITEMS ];
|
||||
SpawnInfo_t pHealthInfo[ NUM_HEALTH_ITEMS ];
|
||||
|
||||
// Count the potential addition of items in the PVS
|
||||
FindPotentialItems( NUM_HEALTH_ITEMS, g_DynamicResupplyHealthItems, iDebug, pHealthInfo );
|
||||
FindPotentialItems( NUM_AMMO_ITEMS, g_DynamicResupplyAmmoItems, iDebug, pAmmoInfo );
|
||||
|
||||
// Use the master if we're supposed to
|
||||
CItem_DynamicResupply *pMaster = this;
|
||||
if ( HasSpawnFlags( SF_DYNAMICRESUPPLY_USE_MASTER ) && g_MasterResupply )
|
||||
{
|
||||
pMaster = g_MasterResupply;
|
||||
}
|
||||
|
||||
// Compute desired ratios for health and ammo
|
||||
ComputeHealthRatios( pMaster, pPlayer, iDebug, pHealthInfo );
|
||||
ComputeAmmoRatios( pMaster, pPlayer, iDebug, pAmmoInfo );
|
||||
|
||||
Vector vecSpawnOrigin = GetAbsOrigin();
|
||||
bool bHealthSpawned = SpawnItemFromRatio( NUM_HEALTH_ITEMS, g_DynamicResupplyHealthItems, iDebug, pHealthInfo, &vecSpawnOrigin );
|
||||
bool bAmmoSpawned = SpawnItemFromRatio( NUM_AMMO_ITEMS, g_DynamicResupplyAmmoItems, iDebug, pAmmoInfo, &vecSpawnOrigin );
|
||||
if ( !bHealthSpawned && !bAmmoSpawned )
|
||||
{
|
||||
SpawnFullItem( pMaster, pPlayer, iDebug );
|
||||
}
|
||||
|
||||
SetThink( NULL );
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : float
|
||||
//-----------------------------------------------------------------------------
|
||||
float DynamicResupply_GetDesiredHealthPercentage( void )
|
||||
{
|
||||
// Return what the master supply dictates
|
||||
if ( g_MasterResupply != NULL )
|
||||
return g_MasterResupply->GetDesiredHealthPercentage();
|
||||
|
||||
// Full health if they haven't specified otherwise
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void DynamicResupply_InitFromAlternateMaster( CBaseEntity *pTargetEnt, string_t iszMaster )
|
||||
{
|
||||
if ( iszMaster== NULL_STRING )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CItem_DynamicResupply *pTargetResupply = assert_cast<CItem_DynamicResupply *>( pTargetEnt );
|
||||
CBaseEntity *pMasterEnt = gEntList.FindEntityByName( NULL, iszMaster );
|
||||
|
||||
if ( !pMasterEnt || !pMasterEnt->ClassMatches( pTargetResupply->GetClassname() ) )
|
||||
{
|
||||
DevWarning( "Invalid item_dynamic_resupply name %s\n", STRING( iszMaster ) );
|
||||
return;
|
||||
}
|
||||
|
||||
CItem_DynamicResupply *pMasterResupply = assert_cast<CItem_DynamicResupply *>( pMasterEnt );
|
||||
|
||||
pTargetResupply->RemoveSpawnFlags( SF_DYNAMICRESUPPLY_USE_MASTER );
|
||||
memcpy( pTargetResupply->m_flDesiredHealth, pMasterResupply->m_flDesiredHealth, sizeof( pMasterResupply->m_flDesiredHealth ) );
|
||||
memcpy( pTargetResupply->m_flDesiredAmmo, pMasterResupply->m_flDesiredAmmo, sizeof( pMasterResupply->m_flDesiredAmmo ) );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ITEM_DYNAMIC_RESUPPLY_H
|
||||
#define ITEM_DYNAMIC_RESUPPLY_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Spawnflags
|
||||
#define SF_DYNAMICRESUPPLY_USE_MASTER 1
|
||||
#define SF_DYNAMICRESUPPLY_IS_MASTER 2
|
||||
#define SF_DYNAMICRESUPPLY_ALWAYS_SPAWN 4 // even if the player has met his target
|
||||
#define SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL 8 // If we fail to spawn anything, spawn a health vial
|
||||
#define SF_DYNAMICRESUPPLY_ALTERNATE_MASTER 16 // Don't assume role as master on activate, but don't think either
|
||||
|
||||
float DynamicResupply_GetDesiredHealthPercentage( void );
|
||||
void DynamicResupply_InitFromAlternateMaster( CBaseEntity *pResupply, string_t iszMaster );
|
||||
|
||||
#endif // ITEM_DYNAMIC_RESUPPLY_H
|
||||
@@ -0,0 +1,743 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements health kits and wall mounted health chargers.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gamerules.h"
|
||||
#include "player.h"
|
||||
#include "items.h"
|
||||
#include "in_buttons.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar sk_healthkit( "sk_healthkit","0" );
|
||||
ConVar sk_healthvial( "sk_healthvial","0" );
|
||||
ConVar sk_healthcharger( "sk_healthcharger","0" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small health kit. Heals the player when picked up.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CHealthKit : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHealthKit, CItem );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool MyTouch( CBasePlayer *pPlayer );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthkit, CHealthKit );
|
||||
PRECACHE_REGISTER(item_healthkit);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHealthKit::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( "models/items/healthkit.mdl" );
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHealthKit::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/items/healthkit.mdl");
|
||||
|
||||
PrecacheScriptSound( "HealthKit.Touch" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pPlayer -
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHealthKit::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) )
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( GetClassname() );
|
||||
MessageEnd();
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, "HealthKit.Touch" );
|
||||
EmitSound( filter, pPlayer->entindex(), "HealthKit.Touch" );
|
||||
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small dynamically dropped health kit
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CHealthVial : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHealthVial, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( "models/healthvial.mdl" );
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel("models/healthvial.mdl");
|
||||
|
||||
PrecacheScriptSound( "HealthVial.Touch" );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) )
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( GetClassname() );
|
||||
MessageEnd();
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, "HealthVial.Touch" );
|
||||
EmitSound( filter, pPlayer->entindex(), "HealthVial.Touch" );
|
||||
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthvial, CHealthVial );
|
||||
PRECACHE_REGISTER( item_healthvial );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wall mounted health kit. Heals the player when used.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWallHealth : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CWallHealth, CBaseToggle );
|
||||
|
||||
void Spawn( );
|
||||
void Precache( void );
|
||||
int DrawDebugTextOverlays(void);
|
||||
bool CreateVPhysics(void);
|
||||
void Off(void);
|
||||
void Recharge(void);
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | m_iCaps; }
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
|
||||
int m_nState;
|
||||
int m_iCaps;
|
||||
|
||||
COutputFloat m_OutRemainingHealth;
|
||||
COutputEvent m_OnPlayerUse;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(func_healthcharger, CWallHealth);
|
||||
|
||||
|
||||
BEGIN_DATADESC( CWallHealth )
|
||||
|
||||
DEFINE_FIELD( m_flNextCharge, FIELD_TIME),
|
||||
DEFINE_FIELD( m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_flSoundTime, FIELD_TIME),
|
||||
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iCaps, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Off ),
|
||||
DEFINE_FUNCTION( Recharge ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ),
|
||||
DEFINE_OUTPUT( m_OutRemainingHealth, "OutRemainingHealth"),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pkvd -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWallHealth::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if (FStrEq(szKeyName, "style") ||
|
||||
FStrEq(szKeyName, "height") ||
|
||||
FStrEq(szKeyName, "value1") ||
|
||||
FStrEq(szKeyName, "value2") ||
|
||||
FStrEq(szKeyName, "value3"))
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else if (FStrEq(szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(szValue);
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(BaseClass::KeyValue( szKeyName, szValue ));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Spawn(void)
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetSolid( SOLID_BSP );
|
||||
SetMoveType( MOVETYPE_PUSH );
|
||||
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
|
||||
m_iJuice = sk_healthcharger.GetFloat();
|
||||
|
||||
m_nState = 0;
|
||||
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
CreateVPhysics();
|
||||
}
|
||||
|
||||
int CWallHealth::DrawDebugTextOverlays(void)
|
||||
{
|
||||
int text_offset = BaseClass::DrawDebugTextOverlays();
|
||||
|
||||
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
||||
{
|
||||
char tempstr[512];
|
||||
Q_snprintf(tempstr,sizeof(tempstr),"Charge left: %i", m_iJuice );
|
||||
EntityText(text_offset,tempstr,0);
|
||||
text_offset++;
|
||||
}
|
||||
return text_offset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool CWallHealth::CreateVPhysics(void)
|
||||
{
|
||||
VPhysicsInitStatic();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Precache(void)
|
||||
{
|
||||
PrecacheScriptSound( "WallHealth.Deny" );
|
||||
PrecacheScriptSound( "WallHealth.Start" );
|
||||
PrecacheScriptSound( "WallHealth.LoopingContinueCharge" );
|
||||
PrecacheScriptSound( "WallHealth.Recharge" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pActivator -
|
||||
// *pCaller -
|
||||
// useType -
|
||||
// value -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(pActivator);
|
||||
|
||||
// Reset to a state of continuous use.
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
m_nState = 1;
|
||||
Off();
|
||||
}
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise.
|
||||
// disabled HEV suit dependency for now.
|
||||
//if ((m_iJuice <= 0) || (!(pActivator->m_bWearingSuit)))
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "WallHealth.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( pActivator->GetHealth() >= pActivator->GetMaxHealth() )
|
||||
{
|
||||
if( pPlayer )
|
||||
{
|
||||
pPlayer->m_afButtonPressed &= ~IN_USE;
|
||||
}
|
||||
|
||||
// Make the user re-use me to get started drawing health.
|
||||
m_iCaps = FCAP_IMPULSE_USE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.25f );
|
||||
SetThink(&CWallHealth::Off);
|
||||
|
||||
// Time to recharge yet?
|
||||
|
||||
if (m_flNextCharge >= gpGlobals->curtime)
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EmitSound( "WallHealth.Start" );
|
||||
m_flSoundTime = 0.56 + gpGlobals->curtime;
|
||||
|
||||
m_OnPlayerUse.FireOutput( pActivator, this );
|
||||
}
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->curtime))
|
||||
{
|
||||
m_iOn++;
|
||||
CPASAttenuationFilter filter( this, "WallHealth.LoopingContinueCharge" );
|
||||
filter.MakeReliable();
|
||||
EmitSound( filter, entindex(), "WallHealth.LoopingContinueCharge" );
|
||||
}
|
||||
|
||||
// charge the player
|
||||
if ( pActivator->TakeHealth( 1, DMG_GENERIC ) )
|
||||
{
|
||||
m_iJuice--;
|
||||
}
|
||||
|
||||
// Send the output.
|
||||
float flRemaining = m_iJuice / sk_healthcharger.GetFloat();
|
||||
m_OutRemainingHealth.Set(flRemaining, pActivator, this);
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Recharge(void)
|
||||
{
|
||||
EmitSound( "WallHealth.Recharge" );
|
||||
m_iJuice = sk_healthcharger.GetFloat();
|
||||
m_nState = 0;
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
StopSound( "WallHealth.LoopingContinueCharge" );
|
||||
|
||||
m_iOn = 0;
|
||||
|
||||
if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_iReactivate );
|
||||
SetThink(&CWallHealth::Recharge);
|
||||
}
|
||||
else
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wall mounted health kit. Heals the player when used.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CNewWallHealth : public CBaseAnimating
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNewWallHealth, CBaseAnimating );
|
||||
|
||||
void Spawn( );
|
||||
void Precache( void );
|
||||
int DrawDebugTextOverlays(void);
|
||||
bool CreateVPhysics(void);
|
||||
void Off(void);
|
||||
void Recharge(void);
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | m_iCaps; }
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
|
||||
int m_nState;
|
||||
int m_iCaps;
|
||||
|
||||
COutputFloat m_OutRemainingHealth;
|
||||
COutputEvent m_OnPlayerUse;
|
||||
|
||||
void StudioFrameAdvance ( void );
|
||||
|
||||
float m_flJuice;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthcharger, CNewWallHealth);
|
||||
|
||||
|
||||
BEGIN_DATADESC( CNewWallHealth )
|
||||
|
||||
DEFINE_FIELD( m_flNextCharge, FIELD_TIME),
|
||||
DEFINE_FIELD( m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_flSoundTime, FIELD_TIME),
|
||||
DEFINE_FIELD( m_nState, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iCaps, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flJuice, FIELD_FLOAT ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Off ),
|
||||
DEFINE_FUNCTION( Recharge ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ),
|
||||
DEFINE_OUTPUT( m_OutRemainingHealth, "OutRemainingHealth"),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
#define HEALTH_CHARGER_MODEL_NAME "models/props_combine/health_charger001.mdl"
|
||||
#define CHARGE_RATE 0.25f
|
||||
#define CHARGES_PER_SECOND 1.0f / CHARGE_RATE
|
||||
#define CALLS_PER_SECOND 7.0f * CHARGES_PER_SECOND
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pkvd -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNewWallHealth::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if (FStrEq(szKeyName, "style") ||
|
||||
FStrEq(szKeyName, "height") ||
|
||||
FStrEq(szKeyName, "value1") ||
|
||||
FStrEq(szKeyName, "value2") ||
|
||||
FStrEq(szKeyName, "value3"))
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else if (FStrEq(szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(szValue);
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(BaseClass::KeyValue( szKeyName, szValue ));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewWallHealth::Spawn(void)
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetSolid( SOLID_VPHYSICS );
|
||||
CreateVPhysics();
|
||||
|
||||
SetModel( HEALTH_CHARGER_MODEL_NAME );
|
||||
AddEffects( EF_NOSHADOW );
|
||||
|
||||
ResetSequence( LookupSequence( "idle" ) );
|
||||
|
||||
m_iJuice = sk_healthcharger.GetFloat();
|
||||
|
||||
m_nState = 0;
|
||||
|
||||
m_iReactivate = 0;
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
CreateVPhysics();
|
||||
|
||||
m_flJuice = m_iJuice;
|
||||
SetCycle( 1.0f - ( m_flJuice / sk_healthcharger.GetFloat() ) );
|
||||
}
|
||||
|
||||
int CNewWallHealth::DrawDebugTextOverlays(void)
|
||||
{
|
||||
int text_offset = BaseClass::DrawDebugTextOverlays();
|
||||
|
||||
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
||||
{
|
||||
char tempstr[512];
|
||||
Q_snprintf(tempstr,sizeof(tempstr),"Charge left: %i", m_iJuice );
|
||||
EntityText(text_offset,tempstr,0);
|
||||
text_offset++;
|
||||
}
|
||||
return text_offset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool CNewWallHealth::CreateVPhysics(void)
|
||||
{
|
||||
VPhysicsInitStatic();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewWallHealth::Precache(void)
|
||||
{
|
||||
PrecacheModel( HEALTH_CHARGER_MODEL_NAME );
|
||||
|
||||
PrecacheScriptSound( "WallHealth.Deny" );
|
||||
PrecacheScriptSound( "WallHealth.Start" );
|
||||
PrecacheScriptSound( "WallHealth.LoopingContinueCharge" );
|
||||
PrecacheScriptSound( "WallHealth.Recharge" );
|
||||
}
|
||||
|
||||
void CNewWallHealth::StudioFrameAdvance( void )
|
||||
{
|
||||
m_flPlaybackRate = 0;
|
||||
|
||||
float flMaxJuice = sk_healthcharger.GetFloat();
|
||||
|
||||
SetCycle( 1.0f - (float)( m_flJuice / flMaxJuice ) );
|
||||
// Msg( "Cycle: %f - Juice: %d - m_flJuice :%f - Interval: %f\n", (float)GetCycle(), (int)m_iJuice, (float)m_flJuice, GetAnimTimeInterval() );
|
||||
|
||||
if ( !m_flPrevAnimTime )
|
||||
{
|
||||
m_flPrevAnimTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
// Latch prev
|
||||
m_flPrevAnimTime = m_flAnimTime;
|
||||
// Set current
|
||||
m_flAnimTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pActivator -
|
||||
// *pCaller -
|
||||
// useType -
|
||||
// value -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator->IsPlayer() )
|
||||
return;
|
||||
CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(pActivator);
|
||||
|
||||
// Reset to a state of continuous use.
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
if ( m_iOn )
|
||||
{
|
||||
float flCharges = CHARGES_PER_SECOND;
|
||||
float flCalls = CALLS_PER_SECOND;
|
||||
|
||||
m_flJuice -= flCharges / flCalls;
|
||||
StudioFrameAdvance();
|
||||
}
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
ResetSequence( LookupSequence( "emptyclick" ) );
|
||||
m_nState = 1;
|
||||
Off();
|
||||
}
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise.
|
||||
// disabled HEV suit dependency for now.
|
||||
//if ((m_iJuice <= 0) || (!(pActivator->m_bWearingSuit)))
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "WallHealth.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( pActivator->GetHealth() >= pActivator->GetMaxHealth() )
|
||||
{
|
||||
if( pPlayer )
|
||||
{
|
||||
pPlayer->m_afButtonPressed &= ~IN_USE;
|
||||
}
|
||||
|
||||
// Make the user re-use me to get started drawing health.
|
||||
m_iCaps = FCAP_IMPULSE_USE;
|
||||
|
||||
EmitSound( "WallHealth.Deny" );
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + CHARGE_RATE );
|
||||
SetThink( &CNewWallHealth::Off );
|
||||
|
||||
// Time to recharge yet?
|
||||
|
||||
if (m_flNextCharge >= gpGlobals->curtime)
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EmitSound( "WallHealth.Start" );
|
||||
m_flSoundTime = 0.56 + gpGlobals->curtime;
|
||||
|
||||
m_OnPlayerUse.FireOutput( pActivator, this );
|
||||
}
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->curtime))
|
||||
{
|
||||
m_iOn++;
|
||||
CPASAttenuationFilter filter( this, "WallHealth.LoopingContinueCharge" );
|
||||
filter.MakeReliable();
|
||||
EmitSound( filter, entindex(), "WallHealth.LoopingContinueCharge" );
|
||||
}
|
||||
|
||||
// charge the player
|
||||
if ( pActivator->TakeHealth( 1, DMG_GENERIC ) )
|
||||
{
|
||||
m_iJuice--;
|
||||
}
|
||||
|
||||
// Send the output.
|
||||
float flRemaining = m_iJuice / sk_healthcharger.GetFloat();
|
||||
m_OutRemainingHealth.Set(flRemaining, pActivator, this);
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewWallHealth::Recharge(void)
|
||||
{
|
||||
EmitSound( "WallHealth.Recharge" );
|
||||
m_flJuice = m_iJuice = sk_healthcharger.GetFloat();
|
||||
m_nState = 0;
|
||||
|
||||
ResetSequence( LookupSequence( "idle" ) );
|
||||
StudioFrameAdvance();
|
||||
|
||||
m_iReactivate = 0;
|
||||
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewWallHealth::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
StopSound( "WallHealth.LoopingContinueCharge" );
|
||||
|
||||
if ( m_nState == 1 )
|
||||
{
|
||||
SetCycle( 1.0f );
|
||||
}
|
||||
|
||||
m_iOn = 0;
|
||||
m_flJuice = m_iJuice;
|
||||
|
||||
if ( m_iReactivate == 0 )
|
||||
{
|
||||
if ((!m_iJuice) && g_pGameRules->FlHealthChargerRechargeTime() > 0 )
|
||||
{
|
||||
m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime();
|
||||
SetNextThink( gpGlobals->curtime + m_iReactivate );
|
||||
SetThink(&CNewWallHealth::Recharge);
|
||||
}
|
||||
else
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: The various ammo types for HL2
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "props.h"
|
||||
#include "items.h"
|
||||
#include "item_dynamic_resupply.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
const char *pszItemCrateModelName[] =
|
||||
{
|
||||
"models/items/item_item_crate.mdl",
|
||||
"models/items/item_beacon_crate.mdl",
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A breakable crate that drops items
|
||||
//-----------------------------------------------------------------------------
|
||||
class CItem_ItemCrate : public CPhysicsProp
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItem_ItemCrate, CPhysicsProp );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
|
||||
virtual int ObjectCaps() { return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION; };
|
||||
|
||||
virtual int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
|
||||
void InputKill( inputdata_t &data );
|
||||
|
||||
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
|
||||
virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason );
|
||||
|
||||
protected:
|
||||
virtual void OnBreak( const Vector &vecVelocity, const AngularImpulse &angVel, CBaseEntity *pBreaker );
|
||||
|
||||
private:
|
||||
// Crate types. Add more!
|
||||
enum CrateType_t
|
||||
{
|
||||
CRATE_SPECIFIC_ITEM = 0,
|
||||
CRATE_TYPE_COUNT,
|
||||
};
|
||||
|
||||
enum CrateAppearance_t
|
||||
{
|
||||
CRATE_APPEARANCE_DEFAULT = 0,
|
||||
CRATE_APPEARANCE_RADAR_BEACON,
|
||||
};
|
||||
|
||||
private:
|
||||
CrateType_t m_CrateType;
|
||||
string_t m_strItemClass;
|
||||
int m_nItemCount;
|
||||
string_t m_strAlternateMaster;
|
||||
CrateAppearance_t m_CrateAppearance;
|
||||
|
||||
COutputEvent m_OnCacheInteraction;
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_item_crate, CItem_ItemCrate);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Save/load:
|
||||
//-----------------------------------------------------------------------------
|
||||
BEGIN_DATADESC( CItem_ItemCrate )
|
||||
|
||||
DEFINE_KEYFIELD( m_CrateType, FIELD_INTEGER, "CrateType" ),
|
||||
DEFINE_KEYFIELD( m_strItemClass, FIELD_STRING, "ItemClass" ),
|
||||
DEFINE_KEYFIELD( m_nItemCount, FIELD_INTEGER, "ItemCount" ),
|
||||
DEFINE_KEYFIELD( m_strAlternateMaster, FIELD_STRING, "SpecificResupply" ),
|
||||
DEFINE_KEYFIELD( m_CrateAppearance, FIELD_INTEGER, "CrateAppearance" ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ),
|
||||
DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_ItemCrate::Precache( void )
|
||||
{
|
||||
// Set this here to quiet base prop warnings
|
||||
PrecacheModel( pszItemCrateModelName[m_CrateAppearance] );
|
||||
SetModel( pszItemCrateModelName[m_CrateAppearance] );
|
||||
|
||||
BaseClass::Precache();
|
||||
if ( m_CrateType == CRATE_SPECIFIC_ITEM )
|
||||
{
|
||||
if ( NULL_STRING != m_strItemClass )
|
||||
{
|
||||
// Don't precache if this is a null string.
|
||||
UTIL_PrecacheOther( STRING(m_strItemClass) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_ItemCrate::Spawn( void )
|
||||
{
|
||||
if ( g_pGameRules->IsAllowedToSpawn( this ) == false )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
DisableAutoFade();
|
||||
SetModelName( AllocPooledString( pszItemCrateModelName[m_CrateAppearance] ) );
|
||||
|
||||
if ( NULL_STRING == m_strItemClass )
|
||||
{
|
||||
Warning( "CItem_ItemCrate(%i): CRATE_SPECIFIC_ITEM with NULL ItemClass string (deleted)!!!\n", entindex() );
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
Precache( );
|
||||
SetModel( pszItemCrateModelName[m_CrateAppearance] );
|
||||
AddEFlags( EFL_NO_ROTORWASH_PUSH );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &data -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_ItemCrate::InputKill( inputdata_t &data )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Item crates blow up immediately
|
||||
//-----------------------------------------------------------------------------
|
||||
int CItem_ItemCrate::OnTakeDamage( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetDamageType() & DMG_AIRBOAT )
|
||||
{
|
||||
CTakeDamageInfo dmgInfo = info;
|
||||
dmgInfo.ScaleDamage( 10.0 );
|
||||
return BaseClass::OnTakeDamage( dmgInfo );
|
||||
}
|
||||
|
||||
return BaseClass::OnTakeDamage( info );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_ItemCrate::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
|
||||
{
|
||||
float flDamageScale = 1.0f;
|
||||
if ( FClassnameIs( pEvent->pEntities[!index], "prop_vehicle_airboat" ) ||
|
||||
FClassnameIs( pEvent->pEntities[!index], "prop_vehicle_jeep" ) )
|
||||
{
|
||||
flDamageScale = 100.0f;
|
||||
}
|
||||
|
||||
m_impactEnergyScale *= flDamageScale;
|
||||
BaseClass::VPhysicsCollision( index, pEvent );
|
||||
m_impactEnergyScale /= flDamageScale;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse &angImpulse, CBaseEntity *pBreaker )
|
||||
{
|
||||
// FIXME: We could simply store the name of an entity to put into the crate
|
||||
// as a string entered in by worldcraft. Should we? I'd do it for sure
|
||||
// if it was easy to get a dropdown with all entity types in it.
|
||||
|
||||
m_OnCacheInteraction.FireOutput(pBreaker,this);
|
||||
|
||||
for ( int i = 0; i < m_nItemCount; ++i )
|
||||
{
|
||||
CBaseEntity *pSpawn = NULL;
|
||||
switch( m_CrateType )
|
||||
{
|
||||
case CRATE_SPECIFIC_ITEM:
|
||||
pSpawn = CreateEntityByName( STRING(m_strItemClass) );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !pSpawn )
|
||||
return;
|
||||
|
||||
// Give a little randomness...
|
||||
Vector vecOrigin;
|
||||
CollisionProp()->RandomPointInBounds( Vector(0.25, 0.25, 0.25), Vector( 0.75, 0.75, 0.75 ), &vecOrigin );
|
||||
pSpawn->SetAbsOrigin( vecOrigin );
|
||||
|
||||
QAngle vecAngles;
|
||||
vecAngles.x = random->RandomFloat( -20.0f, 20.0f );
|
||||
vecAngles.y = random->RandomFloat( 0.0f, 360.0f );
|
||||
vecAngles.z = random->RandomFloat( -20.0f, 20.0f );
|
||||
pSpawn->SetAbsAngles( vecAngles );
|
||||
|
||||
Vector vecActualVelocity;
|
||||
vecActualVelocity.Random( -10.0f, 10.0f );
|
||||
// vecActualVelocity += vecVelocity;
|
||||
pSpawn->SetAbsVelocity( vecActualVelocity );
|
||||
|
||||
QAngle angVel;
|
||||
AngularImpulseToQAngle( angImpulse, angVel );
|
||||
pSpawn->SetLocalAngularVelocity( angVel );
|
||||
|
||||
// If we're creating an item, it can't be picked up until it comes to rest
|
||||
// But only if it wasn't broken by a vehicle
|
||||
CItem *pItem = dynamic_cast<CItem*>(pSpawn);
|
||||
if ( pItem && !pBreaker->GetServerVehicle())
|
||||
{
|
||||
pItem->ActivateWhenAtRest();
|
||||
}
|
||||
|
||||
pSpawn->Spawn();
|
||||
|
||||
// Avoid missing items drops by a dynamic resupply because they don't think immediately
|
||||
if ( FClassnameIs( pSpawn, "item_dynamic_resupply" ) )
|
||||
{
|
||||
if ( m_strAlternateMaster != NULL_STRING )
|
||||
{
|
||||
DynamicResupply_InitFromAlternateMaster( pSpawn, m_strAlternateMaster );
|
||||
}
|
||||
if ( i == 0 )
|
||||
{
|
||||
pSpawn->AddSpawnFlags( SF_DYNAMICRESUPPLY_ALWAYS_SPAWN );
|
||||
}
|
||||
pSpawn->SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CItem_ItemCrate::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
|
||||
{
|
||||
BaseClass::OnPhysGunPickup( pPhysGunUser, reason );
|
||||
|
||||
m_OnCacheInteraction.FireOutput( pPhysGunUser, this );
|
||||
|
||||
if ( reason == PUNTED_BY_CANNON && m_CrateAppearance != CRATE_APPEARANCE_RADAR_BEACON )
|
||||
{
|
||||
Vector vForward;
|
||||
AngleVectors( pPhysGunUser->EyeAngles(), &vForward, NULL, NULL );
|
||||
Vector vForce = Pickup_PhysGunLaunchVelocity( this, vForward, PHYSGUN_FORCE_PUNTED );
|
||||
AngularImpulse angular = AngularImpulse( 0, 0, 0 );
|
||||
|
||||
IPhysicsObject *pPhysics = VPhysicsGetObject();
|
||||
|
||||
if ( pPhysics )
|
||||
{
|
||||
pPhysics->AddVelocity( &vForce, &angular );
|
||||
}
|
||||
|
||||
TakeDamage( CTakeDamageInfo( pPhysGunUser, pPhysGunUser, GetHealth(), DMG_GENERIC ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== item_longjump.cpp ========================================================
|
||||
|
||||
handling for the longjump module
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
//#include "weapons.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
|
||||
class CItemLongJump : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemLongJump, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/w_longjump.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/w_longjump.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->m_fLongJump )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( pPlayer->IsSuitEquipped() )
|
||||
{
|
||||
pPlayer->m_fLongJump = TRUE;// player now has longjump module
|
||||
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( STRING(pev->classname) );
|
||||
MessageEnd();
|
||||
|
||||
UTIL_EmitSoundSuit( pPlayer->edict(), "!HEV_A1" ); // Play the longjump sound UNDONE: Kelly? correct sound?
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_longjump, CItemLongJump );
|
||||
@@ -0,0 +1,44 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== item_security.cpp ========================================================
|
||||
|
||||
handling for the security item
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
//#include "weapons.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
|
||||
class CItemSecurity : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemSecurity, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/w_security.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/w_security.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
pPlayer->m_rgItems[ITEM_SECURITY] += 1;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_security, CItemSecurity);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== item_suit.cpp ========================================================
|
||||
|
||||
handling for the player's suit.
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define SF_SUIT_SHORTLOGON 0x0001
|
||||
|
||||
class CItemSuit : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemSuit, CItem );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/items/hevsuit.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
|
||||
CollisionProp()->UseTriggerBounds( false, 0 );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/items/hevsuit.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->IsSuitEquipped() )
|
||||
return FALSE;
|
||||
|
||||
if ( m_spawnflags & SF_SUIT_SHORTLOGON )
|
||||
UTIL_EmitSoundSuit(pPlayer->edict(), "!HEV_A0"); // short version of suit logon,
|
||||
else
|
||||
UTIL_EmitSoundSuit(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon
|
||||
|
||||
pPlayer->EquipSuit();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_suit, CItemSuit);
|
||||
@@ -0,0 +1,237 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements doors that move when you look at them.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "basecombatcharacter.h"
|
||||
#include "entitylist.h"
|
||||
#include "func_movelinear.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define SF_LDOOR_THRESHOLD 8192
|
||||
#define SF_LDOOR_INVERT 16384
|
||||
#define SF_LDOOR_FROM_OPEN 32768
|
||||
|
||||
|
||||
class CLookDoor : public CFuncMoveLinear
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CLookDoor, CFuncMoveLinear );
|
||||
|
||||
void Spawn( void );
|
||||
void MoveThink( void );
|
||||
|
||||
// Inputs
|
||||
void InputInvertOn( inputdata_t &inputdata );
|
||||
void InputInvertOff( inputdata_t &inputdata );
|
||||
|
||||
float m_flProximityDistance; // How far before I start reacting
|
||||
float m_flProximityOffset;
|
||||
float m_flFieldOfView;
|
||||
|
||||
EHANDLE m_hLooker; // Who is looking
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
|
||||
class CLookDoorThinker : public CLogicalEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CLookDoorThinker, CLogicalEntity );
|
||||
|
||||
void LookThink( void );
|
||||
EHANDLE m_hLookDoor; // Who owns me
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
|
||||
BEGIN_DATADESC( CLookDoorThinker )
|
||||
|
||||
DEFINE_FIELD( m_hLookDoor, FIELD_EHANDLE ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION(LookThink),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( lookdoorthinker, CLookDoorThinker );
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
//------------------------------------------------------------------------------
|
||||
void CLookDoorThinker::LookThink(void)
|
||||
{
|
||||
if (m_hLookDoor)
|
||||
{
|
||||
((CLookDoor*)(CBaseEntity*)m_hLookDoor)->MoveThink();
|
||||
SetNextThink( gpGlobals->curtime + 0.01f );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BEGIN_DATADESC( CLookDoor )
|
||||
|
||||
DEFINE_KEYFIELD( m_flProximityDistance, FIELD_FLOAT, "ProximityDistance"),
|
||||
DEFINE_KEYFIELD( m_flProximityOffset, FIELD_FLOAT, "ProximityOffset"),
|
||||
DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ),
|
||||
DEFINE_FIELD(m_hLooker, FIELD_EHANDLE),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "InvertOn", InputInvertOn ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "InvertOff", InputInvertOff ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION(MoveThink),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( func_lookdoor, CLookDoor );
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Input handlers.
|
||||
//------------------------------------------------------------------------------
|
||||
void CLookDoor::InputInvertOn( inputdata_t &inputdata )
|
||||
{
|
||||
m_spawnflags |= SF_LDOOR_INVERT;
|
||||
}
|
||||
|
||||
void CLookDoor::InputInvertOff( inputdata_t &inputdata )
|
||||
{
|
||||
m_spawnflags &= ~SF_LDOOR_INVERT;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
//------------------------------------------------------------------------------
|
||||
void CLookDoor::Spawn(void)
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
if (m_target == NULL_STRING)
|
||||
{
|
||||
Warning( "ERROR: DoorLook (%s) given no target. Rejecting spawn.\n",GetDebugName());
|
||||
return;
|
||||
}
|
||||
CLookDoorThinker* pLookThinker = (CLookDoorThinker*)CreateEntityByName("lookdoorthinker");
|
||||
if (pLookThinker)
|
||||
{
|
||||
pLookThinker->SetThink(&CLookDoorThinker::LookThink);
|
||||
pLookThinker->m_hLookDoor = this;
|
||||
pLookThinker->SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
//------------------------------------------------------------------------------
|
||||
void CLookDoor::MoveThink(void)
|
||||
{
|
||||
// --------------------------------
|
||||
// Make sure we have a looker
|
||||
// --------------------------------
|
||||
if (m_hLooker == NULL)
|
||||
{
|
||||
m_hLooker = (CBaseEntity*)gEntList.FindEntityByName( NULL, m_target );
|
||||
|
||||
if (m_hLooker == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Calculate an orgin for the door
|
||||
//--------------------------------------
|
||||
Vector vOrigin = WorldSpaceCenter() - GetAbsOrigin();
|
||||
|
||||
// If FROM_OPEN flag is set, door proximity is measured
|
||||
// from the open and not the closed position
|
||||
if (FBitSet (m_spawnflags, SF_LDOOR_FROM_OPEN))
|
||||
{
|
||||
vOrigin += m_vecPosition2;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// First add movement based on proximity
|
||||
// ------------------------------------------------------
|
||||
float flProxMove = 0;
|
||||
if (m_flProximityDistance > 0)
|
||||
{
|
||||
float flDist = (m_hLooker->GetAbsOrigin() - vOrigin).Length()-m_flProximityOffset;
|
||||
if (flDist < 0) flDist = 0;
|
||||
|
||||
if (flDist < m_flProximityDistance)
|
||||
{
|
||||
if (FBitSet (m_spawnflags, SF_LDOOR_THRESHOLD))
|
||||
{
|
||||
flProxMove = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flProxMove = 1-flDist/m_flProximityDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Then add movement based on view angle
|
||||
// ------------------------------------------------------
|
||||
float flViewMove = 0;
|
||||
if (m_flFieldOfView > 0)
|
||||
{
|
||||
// ----------------------------------------
|
||||
// Check that toucher is facing the target
|
||||
// ----------------------------------------
|
||||
Assert( dynamic_cast< CBaseCombatCharacter* >( m_hLooker.Get() ) );
|
||||
CBaseCombatCharacter* pBCC = (CBaseCombatCharacter*)m_hLooker.Get();
|
||||
Vector vTouchDir = pBCC->EyeDirection3D( );
|
||||
Vector vTargetDir = vOrigin - pBCC->EyePosition();
|
||||
VectorNormalize(vTargetDir);
|
||||
|
||||
float flDotPr = DotProduct(vTouchDir,vTargetDir);
|
||||
if (flDotPr < m_flFieldOfView)
|
||||
{
|
||||
flViewMove = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flViewMove = (flDotPr-m_flFieldOfView)/(1.0 - m_flFieldOfView);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Summate the two moves
|
||||
//---------------------------------------
|
||||
float flMove = flProxMove + flViewMove;
|
||||
if (flMove > 1.0)
|
||||
{
|
||||
flMove = 1.0;
|
||||
}
|
||||
|
||||
// If behavior is inverted do the reverse
|
||||
if (FBitSet (m_spawnflags, SF_LDOOR_INVERT))
|
||||
{
|
||||
flMove = 1-flMove;
|
||||
}
|
||||
|
||||
// Move the door
|
||||
SetPosition( flMove );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// This is a skeleton file for use when creating a new
|
||||
// NPC. Copy and rename this file for the new
|
||||
// NPC and add the copy to the build.
|
||||
//
|
||||
// Leave this file in the build until we ship! Allowing
|
||||
// this file to be rebuilt with the rest of the game ensures
|
||||
// that it stays up to date with the rest of the NPC code.
|
||||
//
|
||||
// Replace occurances of CNewNPC with the new NPC's
|
||||
// classname. Don't forget the lower-case occurance in
|
||||
// LINK_ENTITY_TO_CLASS()
|
||||
//
|
||||
//
|
||||
// ASSUMPTIONS MADE:
|
||||
//
|
||||
// You're making a character based on CAI_BaseNPC. If this
|
||||
// is not true, make sure you replace all occurances
|
||||
// of 'CAI_BaseNPC' in this file with the appropriate
|
||||
// parent class.
|
||||
//
|
||||
// You're making a human-sized NPC that walks.
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_hull.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//=========================================================
|
||||
// Private activities
|
||||
//=========================================================
|
||||
int ACT_MYCUSTOMACTIVITY = -1;
|
||||
|
||||
//=========================================================
|
||||
// Custom schedules
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE,
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Custom tasks
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
TASK_MYCUSTOMTASK = LAST_SHARED_TASK,
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Custom Conditions
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION,
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CNewNPC : public CAI_BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNewNPC, CAI_BaseNPC );
|
||||
|
||||
public:
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
Class_T Classify( void );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
// This is a dummy field. In order to provide save/restore
|
||||
// code in this file, we must have at least one field
|
||||
// for the code to operate on. Delete this field when
|
||||
// you are ready to do your own save/restore for this
|
||||
// character.
|
||||
int m_iDeleteThisField;
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_newnpc, CNewNPC );
|
||||
IMPLEMENT_CUSTOM_AI( npc_citizen,CNewNPC );
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Save/Restore
|
||||
//---------------------------------------------------------
|
||||
BEGIN_DATADESC( CNewNPC )
|
||||
|
||||
DEFINE_FIELD( m_iDeleteThisField, FIELD_INTEGER ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialize the custom schedules
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewNPC::InitCustomSchedules(void)
|
||||
{
|
||||
INIT_CUSTOM_AI(CNewNPC);
|
||||
|
||||
ADD_CUSTOM_TASK(CNewNPC, TASK_MYCUSTOMTASK);
|
||||
|
||||
ADD_CUSTOM_SCHEDULE(CNewNPC, SCHED_MYCUSTOMSCHEDULE);
|
||||
|
||||
ADD_CUSTOM_ACTIVITY(CNewNPC, ACT_MYCUSTOMACTIVITY);
|
||||
|
||||
ADD_CUSTOM_CONDITION(CNewNPC, COND_MYCUSTOMCONDITION);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewNPC::Precache( void )
|
||||
{
|
||||
PrecacheModel( "models/mymodel.mdl" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNewNPC::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetModel( "models/mymodel.mdl" );
|
||||
SetHullType(HULL_HUMAN);
|
||||
SetHullSizeNormal();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
SetBloodColor( BLOOD_COLOR_RED );
|
||||
m_iHealth = 20;
|
||||
m_flFieldOfView = 0.5;
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
CapabilitiesClear();
|
||||
//CapabilitiesAdd( bits_CAP_NONE );
|
||||
|
||||
NPCInit();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
Class_T CNewNPC::Classify( void )
|
||||
{
|
||||
return CLASS_NONE;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user