This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+109
View File
@@ -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() );
}
+263
View File
@@ -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
+318
View File
@@ -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
+776
View File
@@ -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()
+120
View File
@@ -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
+132
View File
@@ -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()
+76
View File
@@ -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
+478
View File
@@ -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()
+144
View File
@@ -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
+783
View File
@@ -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()
+106
View File
@@ -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
+170
View File
@@ -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;
}
+50
View File
@@ -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
+80
View File
@@ -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
+411
View File
@@ -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();
}
+83
View File
@@ -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
+44
View File
@@ -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 );
}
+32
View File
@@ -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
+186
View File
@@ -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
+112
View File
@@ -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));
}
}
}
+43
View File
@@ -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
+44
View File
@@ -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;
}
+28
View File
@@ -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
+522
View File
@@ -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;
}
+97
View File
@@ -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
+294
View File
@@ -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
+106
View File
@@ -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()
{
}
+51
View File
@@ -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
+158
View File
@@ -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
+128
View File
@@ -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
+37
View File
@@ -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
+272
View File
@@ -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
+217
View File
@@ -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 );
}
}
+54
View File
@@ -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
+107
View File
@@ -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();
}
+202
View File
@@ -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 );
}
}
+55
View File
@@ -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
+101
View File
@@ -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;
}
+68
View File
@@ -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
+777
View File
@@ -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
+353
View File
@@ -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
+253
View File
@@ -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;
}
+47
View File
@@ -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
+443
View File
@@ -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 );
}
+74
View File
@@ -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
+274
View File
@@ -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);
+41
View File
@@ -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
+326
View File
@@ -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;
}
+135
View File
@@ -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
+157
View File
@@ -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");
}
+42
View File
@@ -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
+453
View File
@@ -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;
}
+19
View File
@@ -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
+696
View File
@@ -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;
}
}
+83
View File
@@ -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
+244
View File
@@ -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" );
}
+38
View File
@@ -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
+330
View File
@@ -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;
}
+52
View File
@@ -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
+365
View File
@@ -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 );
}
+57
View File
@@ -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
+284
View File
@@ -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" );
}
+58
View File
@@ -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
+266
View File
@@ -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 );
}
+55
View File
@@ -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
+397
View File
@@ -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 );
}
}
+80
View File
@@ -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
+26
View File
@@ -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);
+177
View File
@@ -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" );
}
}
+61
View File
@@ -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;
}
+16
View File
@@ -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;
}
+23
View File
@@ -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
+384
View File
@@ -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 &params );
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
+69
View File
@@ -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
}
+56
View File
@@ -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
+875
View File
@@ -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 );
}
+178
View File
@@ -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 );
}
+984
View File
@@ -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 );
}
+45
View File
@@ -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);
+45
View File
@@ -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);
+657
View File
@@ -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 ) );
}
+25
View File
@@ -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
+743
View File
@@ -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 );
}
}
+276
View File
@@ -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 ) );
}
}
+61
View File
@@ -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 );
+44
View File
@@ -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);
+58
View File
@@ -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);
+237
View File
@@ -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 );
}
+176
View File
@@ -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