//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: The downtrodden citizens of City 17. Timid when unarmed, they will // rise up against their Combine oppressors when given a //weapon. // //=============================================================================// #ifndef NPC_CITIZEN_H #define NPC_CITIZEN_H #include "npc_playercompanion.h" #include "ai_behavior_functank.h" struct SquadCandidate_t; //----------------------------------------------------------------------------- // // CLASS: CNPC_Citizen // //----------------------------------------------------------------------------- //------------------------------------- // Spawnflags //------------------------------------- #define SF_CITIZEN_FOLLOW \ (1 << 16) // 65536 follow the player as soon as I spawn. #define SF_CITIZEN_MEDIC (1 << 17) // 131072 #define SF_CITIZEN_RANDOM_HEAD (1 << 18) // 262144 #define SF_CITIZEN_AMMORESUPPLIER (1 << 19) // 524288 #define SF_CITIZEN_NOT_COMMANDABLE (1 << 20) // 1048576 #define SF_CITIZEN_IGNORE_SEMAPHORE \ (1 << 21) // 2097152 Work outside the speech semaphore system #define SF_CITIZEN_RANDOM_HEAD_MALE (1 << 22) // 4194304 #define SF_CITIZEN_RANDOM_HEAD_FEMALE (1 << 23) // 8388608 #define SF_CITIZEN_USE_RENDER_BOUNDS (1 << 24) // 16777216 //------------------------------------- // Animation events //------------------------------------- enum CitizenType_t { CT_DEFAULT, CT_DOWNTRODDEN, CT_REFUGEE, CT_REBEL, CT_UNIQUE }; //----------------------------------------------------------------------------- // Citizen expression types //----------------------------------------------------------------------------- enum CitizenExpressionTypes_t { CIT_EXP_UNASSIGNED, // Defaults to this, selects other in spawn. CIT_EXP_SCARED, CIT_EXP_NORMAL, CIT_EXP_ANGRY, CIT_EXP_LAST_TYPE, }; //------------------------------------- class CNPC_Citizen : public CNPC_PlayerCompanion { DECLARE_CLASS(CNPC_Citizen, CNPC_PlayerCompanion); public: CNPC_Citizen() : m_iHead(-1) {} //--------------------------------- bool CreateBehaviors(); void Precache(); void PrecacheAllOfType(CitizenType_t); void Spawn(); void PostNPCInit(); virtual void SelectModel(); void SelectExpressionType(); void Activate(); virtual void OnGivenWeapon(CBaseCombatWeapon *pNewWeapon); void FixupMattWeapon(); #ifdef HL2_EPISODIC virtual float GetJumpGravity() const { return 1.8f; } #endif // HL2_EPISODIC void OnRestore(); //--------------------------------- string_t GetModelName() const; Class_T Classify(); bool ShouldAlwaysThink(); //--------------------------------- // Behavior //--------------------------------- bool ShouldBehaviorSelectSchedule(CAI_BehaviorBase *pBehavior); void GatherConditions(); void PredictPlayerPush(); void PrescheduleThink(); void BuildScheduleTestBits(); bool FInViewCone(CBaseEntity *pEntity); int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); int SelectSchedule(); int SelectSchedulePriorityAction(); int SelectScheduleHeal(); int SelectScheduleRetrieveItem(); int SelectScheduleNonCombat(); int SelectScheduleManhackCombat(); int SelectScheduleCombat(); bool ShouldDeferToFollowBehavior(); int TranslateSchedule(int scheduleType); bool ShouldAcceptGoal(CAI_BehaviorBase *pBehavior, CAI_GoalEntity *pGoal); void OnClearGoal(CAI_BehaviorBase *pBehavior, CAI_GoalEntity *pGoal); void StartTask(const Task_t *pTask); void RunTask(const Task_t *pTask); Activity NPC_TranslateActivity(Activity eNewActivity); void HandleAnimEvent(animevent_t *pEvent); void TaskFail(AI_TaskFailureCode_t code); void PickupItem(CBaseEntity *pItem); void SimpleUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); bool IgnorePlayerPushing(void); int DrawDebugTextOverlays(void); virtual const char *SelectRandomExpressionForState(NPC_STATE state); //--------------------------------- // Combat //--------------------------------- bool OnBeginMoveAndShoot(); void OnEndMoveAndShoot(); virtual bool UseAttackSquadSlots() { return false; } void LocateEnemySound(); bool IsManhackMeleeCombatant(); Vector GetActualShootPosition(const Vector &shootOrigin); void OnChangeActiveWeapon(CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon); bool ShouldLookForBetterWeapon(); //--------------------------------- // Damage handling //--------------------------------- int OnTakeDamage_Alive(const CTakeDamageInfo &info); //--------------------------------- // Commander mode //--------------------------------- bool IsCommandable(); bool IsPlayerAlly(CBasePlayer *pPlayer = NULL); bool CanJoinPlayerSquad(); bool WasInPlayerSquad(); bool HaveCommandGoal() const; bool IsCommandMoving(); bool ShouldAutoSummon(); bool IsValidCommandTarget(CBaseEntity *pTarget); bool NearCommandGoal(); bool VeryFarFromCommandGoal(); bool TargetOrder(CBaseEntity *pTarget, CAI_BaseNPC **Allies, int numAllies); void MoveOrder(const Vector &vecDest, CAI_BaseNPC **Allies, int numAllies); void OnMoveOrder(); void CommanderUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); bool ShouldSpeakRadio(CBaseEntity *pListener); void OnMoveToCommandGoalFailed(); void AddToPlayerSquad(); void RemoveFromPlayerSquad(); void TogglePlayerSquadState(); void UpdatePlayerSquad(); static int __cdecl PlayerSquadCandidateSortFunc(const SquadCandidate_t *, const SquadCandidate_t *); void FixupPlayerSquad(); void ClearFollowTarget(); void UpdateFollowCommandPoint(); bool IsFollowingCommandPoint(); CAI_BaseNPC *GetSquadCommandRepresentative(); void SetSquad(CAI_Squad *pSquad); void AddInsignia(); void RemoveInsignia(); bool SpeakCommandResponse(AIConcept_t concept, const char *modifiers = NULL); //--------------------------------- // Scanner interaction //--------------------------------- float GetNextScannerInspectTime() { return m_fNextInspectTime; } void SetNextScannerInspectTime(float flTime) { m_fNextInspectTime = flTime; } bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter *sourceEnt); //--------------------------------- // Hints //--------------------------------- bool FValidateHintType(CAI_Hint *pHint); //--------------------------------- // Special abilities //--------------------------------- bool IsMedic() { return HasSpawnFlags(SF_CITIZEN_MEDIC); } bool IsAmmoResupplier() { return HasSpawnFlags(SF_CITIZEN_AMMORESUPPLIER); } bool CanHeal(); bool ShouldHealTarget(CBaseEntity *pTarget, bool bActiveUse = false); #if HL2_EPISODIC bool ShouldHealTossTarget(CBaseEntity *pTarget, bool bActiveUse = false); #endif void Heal(); bool ShouldLookForHealthItem(); #if HL2_EPISODIC void TossHealthKit( CBaseCombatCharacter *pThrowAt, const Vector &offset); // create a healthkit and throw it at someone void InputForceHealthKitToss(inputdata_t &inputdata); #endif //--------------------------------- // Inputs //--------------------------------- void InputRemoveFromPlayerSquad(inputdata_t &inputdata) { RemoveFromPlayerSquad(); } void InputStartPatrolling(inputdata_t &inputdata); void InputStopPatrolling(inputdata_t &inputdata); void InputSetCommandable(inputdata_t &inputdata); void InputSetMedicOn(inputdata_t &inputdata); void InputSetMedicOff(inputdata_t &inputdata); void InputSetAmmoResupplierOn(inputdata_t &inputdata); void InputSetAmmoResupplierOff(inputdata_t &inputdata); void InputSpeakIdleResponse(inputdata_t &inputdata); //--------------------------------- // Sounds & speech //--------------------------------- void FearSound(void); void DeathSound(const CTakeDamageInfo &info); bool UseSemaphore(void); virtual void OnChangeRunningBehavior(CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior); private: //----------------------------------------------------- // Conditions, Schedules, Tasks //----------------------------------------------------- enum { COND_CIT_PLAYERHEALREQUEST = BaseClass::NEXT_CONDITION, COND_CIT_COMMANDHEAL, COND_CIT_HURTBYFIRE, COND_CIT_START_INSPECTION, SCHED_CITIZEN_PLAY_INSPECT_ACTIVITY = BaseClass::NEXT_SCHEDULE, SCHED_CITIZEN_HEAL, SCHED_CITIZEN_RANGE_ATTACK1_RPG, SCHED_CITIZEN_PATROL, SCHED_CITIZEN_MOURN_PLAYER, SCHED_CITIZEN_SIT_ON_TRAIN, SCHED_CITIZEN_STRIDER_RANGE_ATTACK1_RPG, #ifdef HL2_EPISODIC SCHED_CITIZEN_HEAL_TOSS, #endif TASK_CIT_HEAL = BaseClass::NEXT_TASK, TASK_CIT_RPG_AUGER, TASK_CIT_PLAY_INSPECT_SEQUENCE, TASK_CIT_SIT_ON_TRAIN, TASK_CIT_LEAVE_TRAIN, TASK_CIT_SPEAK_MOURNING, #ifdef HL2_EPISODIC TASK_CIT_HEAL_TOSS, #endif }; //----------------------------------------------------- int m_nInspectActivity; float m_flNextFearSoundTime; float m_flStopManhackFlinch; float m_fNextInspectTime; // Next time I'm allowed to get inspected by a // scanner float m_flPlayerHealTime; float m_flNextHealthSearchTime; // Next time I'm allowed to look for a // healthkit float m_flAllyHealTime; float m_flPlayerGiveAmmoTime; string_t m_iszAmmoSupply; int m_iAmmoAmount; bool m_bRPGAvoidPlayer; bool m_bShouldPatrol; string_t m_iszOriginalSquad; float m_flTimeJoinedPlayerSquad; bool m_bWasInPlayerSquad; float m_flTimeLastCloseToPlayer; string_t m_iszDenyCommandConcept; CSimpleSimTimer m_AutoSummonTimer; Vector m_vAutoSummonAnchor; CitizenType_t m_Type; CitizenExpressionTypes_t m_ExpressionType; int m_iHead; static CSimpleSimTimer gm_PlayerSquadEvaluateTimer; float m_flTimePlayerStare; // The game time at which the player started // staring at me. float m_flTimeNextHealStare; // Next time I'm allowed to heal a player who // is staring at me. //----------------------------------------------------- // Outputs //----------------------------------------------------- COutputEvent m_OnJoinedPlayerSquad; COutputEvent m_OnLeftPlayerSquad; COutputEvent m_OnFollowOrder; COutputEvent m_OnStationOrder; COutputEvent m_OnPlayerUse; COutputEvent m_OnNavFailBlocked; //----------------------------------------------------- CAI_FuncTankBehavior m_FuncTankBehavior; CHandle m_hSavedFollowGoalEnt; bool m_bNotifyNavFailBlocked; bool m_bNeverLeavePlayerSquad; // Don't leave the player squad unless // killed, or removed via Entity I/O. //----------------------------------------------------- DECLARE_DATADESC(); #ifdef _XBOX protected: #endif DEFINE_CUSTOM_AI; }; //--------------------------------------------------------- //--------------------------------------------------------- inline bool CNPC_Citizen::NearCommandGoal() { const float flDistSqr = COMMAND_GOAL_TOLERANCE * COMMAND_GOAL_TOLERANCE; return ((GetAbsOrigin() - GetCommandGoal()).LengthSqr() <= flDistSqr); } //--------------------------------------------------------- //--------------------------------------------------------- inline bool CNPC_Citizen::VeryFarFromCommandGoal() { const float flDistSqr = (12 * 50) * (12 * 50); return ((GetAbsOrigin() - GetCommandGoal()).LengthSqr() > flDistSqr); } //============================================================================== // CITIZEN PLAYER-RESPONSE SYSTEM // // NOTE: This system is obsolete, and left here for legacy support. // It has been superseded by the ai_eventresponse system. // //============================================================================== #define CITIZEN_RESPONSE_DISTANCE \ 768 // Maximum distance for responding citizens #define CITIZEN_RESPONSE_REFIRE_TIME \ 15.0 // Time after giving a response before giving any more #define CITIZEN_RESPONSE_GIVEUP_TIME \ 4.0 // Time after a response trigger was fired before discarding it without // responding enum citizenresponses_t { CR_PLAYER_SHOT_GUNSHIP, // Player has shot the gunship with a bullet weapon CR_PLAYER_KILLED_GUNSHIP, // Player has destroyed the gunship CR_VITALNPC_DIED, // Mapmaker specified that an NPC that was vital has died // Add new responses here MAX_CITIZEN_RESPONSES, }; //------------------------------------- class CCitizenResponseSystem : public CBaseEntity { DECLARE_CLASS(CCitizenResponseSystem, CBaseEntity); public: DECLARE_DATADESC(); void Spawn(); void OnRestore(); void AddResponseTrigger(citizenresponses_t iTrigger); void ResponseThink(); //--------------------------------- // Inputs //--------------------------------- void InputResponseVitalNPC(inputdata_t &inputdata); private: float m_flResponseAddedTime [MAX_CITIZEN_RESPONSES]; // Time at which the response was added. 0 if // we have no response. float m_flNextResponseTime; }; //------------------------------------- class CSquadInsignia : public CBaseAnimating { DECLARE_CLASS(CSquadInsignia, CBaseAnimating); void Spawn(); }; //------------------------------------- CCitizenResponseSystem *GetCitizenResponse(); //----------------------------------------------------------------------------- #endif // NPC_CITIZEN_H