aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/scripted.h
blob: 574fcbd564c25e9a2763a04750088077395181fb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#ifndef SCRIPTED_H
#define SCRIPTED_H
#ifdef _WIN32
#pragma once
#endif

#ifndef SCRIPTEVENT_H
#include "scriptevent.h"
#endif

#include "ai_basenpc.h"


//
// The number of unique outputs that a script can fire from animation events.
// These are fired via SCRIPT_EVENT_FIREEVENT in CAI_BaseNPC::HandleAnimEvent.
//
#define MAX_SCRIPT_EVENTS				8


#define SF_SCRIPT_WAITTILLSEEN			1
#define SF_SCRIPT_EXITAGITATED			2
#define SF_SCRIPT_REPEATABLE			4		// Whether the script can be played more than once.
#define SF_SCRIPT_LEAVECORPSE			8
#define SF_SCRIPT_START_ON_SPAWN		16
#define SF_SCRIPT_NOINTERRUPT			32
#define SF_SCRIPT_OVERRIDESTATE			64
#define SF_SCRIPT_DONT_TELEPORT_AT_END	128		// Don't fixup end position with a teleport when the SS is finished
#define SF_SCRIPT_LOOP_IN_POST_IDLE		256		// Loop in the post idle animation after playing the action animation.
#define SF_SCRIPT_HIGH_PRIORITY			512		// If set, we don't allow other scripts to steal our spot in the queue.
#define SF_SCRIPT_SEARCH_CYCLICALLY		1024	// Start search from last entity found.
#define SF_SCRIPT_NO_COMPLAINTS			2048	// doesn't bitch if it can't find anything
#define SF_SCRIPT_ALLOW_DEATH			4096	// the actor using this scripted sequence may die without interrupting the scene (used for scripted deaths)


enum script_moveto_t
{
	CINE_MOVETO_WAIT = 0,
	CINE_MOVETO_WALK = 1,
	CINE_MOVETO_RUN = 2,
	CINE_MOVETO_CUSTOM = 3,
	CINE_MOVETO_TELEPORT = 4,
	CINE_MOVETO_WAIT_FACING = 5,
};

enum SCRIPT_PLAYER_DEATH
{
	SCRIPT_DO_NOTHING = 0,
	SCRIPT_CANCEL = 1,
};


//
// Interrupt levels for grabbing NPCs to act out scripted events. These indicate
// how important it is to get a specific NPC, and can affect how they respond.
//
enum SS_INTERRUPT
{
	SS_INTERRUPT_BY_CLASS = 0,		// Indicates that we are asking for this NPC by class
	SS_INTERRUPT_BY_NAME,			// Indicates that we are asking for this NPC by name
};


// when a NPC finishes an AI scripted sequence, we can choose
// a schedule to place them in. These defines are the aliases to
// resolve worldcraft input to real schedules (sjb)
#define SCRIPT_FINISHSCHED_DEFAULT	0
#define SCRIPT_FINISHSCHED_AMBUSH	1

class CAI_ScriptedSequence : public CBaseEntity
{
	DECLARE_CLASS( CAI_ScriptedSequence, CBaseEntity );
public:
	void Spawn( void );
	virtual void Blocked( CBaseEntity *pOther );
	virtual void Touch( CBaseEntity *pOther );
	virtual int	 ObjectCaps( void ) { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
	virtual void Activate( void );
	virtual void UpdateOnRemove( void );
	void StartThink();
	void ScriptThink( void );
	void StopThink();

	DECLARE_DATADESC();

	void Pain( void );
	void Die( void );
	void DelayStart( bool bDelay );
	bool FindEntity( void );
	void StartScript( void );
	void FireScriptEvent( int nEvent );
	void OnBeginSequence( void );

	void SetTarget( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; };
	CBaseEntity *GetTarget( void ) { return m_hTargetEnt; };

	// Input handlers
	void InputBeginSequence( inputdata_t &inputdata );
	void InputCancelSequence( inputdata_t &inputdata );
	void InputMoveToPosition( inputdata_t &inputdata );

	bool IsTimeToStart( void );
	bool IsWaitingForBegin( void );
	void ReleaseEntity( CAI_BaseNPC *pEntity );
	void CancelScript( void );
	bool StartSequence( CAI_BaseNPC *pTarget, string_t iszSeq, bool completeOnEmpty );
	void SynchronizeSequence( CAI_BaseNPC *pNPC );
	bool FCanOverrideState ( void );
	void SequenceDone( CAI_BaseNPC *pNPC );
	void PostIdleDone( CAI_BaseNPC *pNPC );
	void FixScriptNPCSchedule( CAI_BaseNPC *pNPC, int iSavedCineFlags );
	void FixFlyFlag( CAI_BaseNPC *pNPC, int iSavedCineFlags );
	bool CanInterrupt( void );
	void AllowInterrupt( bool fAllow );
	void RemoveIgnoredConditions( void );
	bool PlayedSequence( void ) { return m_sequenceStarted; }
	bool CanEnqueueAfter( void );

	// Entry & Action loops
	bool IsPlayingEntry( void ) { return m_bIsPlayingEntry; }
	bool IsPlayingAction( void ) { return ( m_sequenceStarted && !m_bIsPlayingEntry ); }
	bool FinishedActionSequence( CAI_BaseNPC *pNPC );
	void SetLoopActionSequence( bool bLoop ) { m_bLoopActionSequence = bLoop; }
	bool ShouldLoopActionSequence( void ) { return m_bLoopActionSequence; }
	void StopActionLoop( bool bStopSynchronizedScenes );
	void SetSynchPostIdles( bool bSynch ) { m_bSynchPostIdles = bSynch; }
	void SynchNewSequence( CAI_BaseNPC::SCRIPTSTATE newState, string_t iszSequence, bool bSynchOtherScenes );

	// Dynamic scripted sequence spawning
	void ForceSetTargetEntity( CAI_BaseNPC *pTarget, bool bDontCancelOtherSequences );

	// Dynamic interactions
	void SetupInteractionPosition( CBaseEntity *pRelativeEntity, VMatrix &matDesiredLocalToWorld );
	void ModifyScriptedAutoMovement( Vector *vecNewPos );

	bool IsTeleportingDueToMoveTo( void ) { return m_bIsTeleportingDueToMoveTo; }

	// Debug
	virtual int DrawDebugTextOverlays( void );
	virtual void DrawDebugGeometryOverlays( void );

	void InputScriptPlayerDeath( inputdata_t &inputdata );

private:
	friend class CAI_BaseNPC;	// should probably try to eliminate this relationship

	string_t m_iszEntry;		// String index for animation that must be played before entering the main action anim
	string_t m_iszPreIdle;		// String index for idle animation to play before playing the action anim (only played while waiting for the script to begin)
	string_t m_iszPlay;			// String index for scripted action animation
	string_t m_iszPostIdle;		// String index for idle animation to play before playing the action anim
	string_t m_iszCustomMove;	// String index for custom movement animation
	string_t m_iszNextScript;	// Name of the script to run immediately after this one.
	string_t m_iszEntity;		// Entity that is wanted for this script

	int m_fMoveTo;
	bool m_bIsPlayingEntry;
	bool m_bLoopActionSequence;
	bool m_bSynchPostIdles;
	bool m_bIgnoreGravity;
	bool m_bDisableNPCCollisions;	// Used when characters must interpenetrate while riding on elevators, trains, etc.

	float m_flRadius;			// Range to search for an NPC to possess.
	float m_flRepeat;			// Repeat rate

	int m_iDelay;					// A counter indicating how many scripts are NOT ready to start.

	bool m_bDelayed;				// This moderately hacky hack ensures that we don't calls to DelayStart(true) or DelayStart(false)
									// twice in succession. This is necessary because we didn't want to remove the call to DelayStart(true)
									// from StartScript, even though DelayStart(true) is called from TASK_PRE_SCRIPT.
									// All of this is necessary in case the NPCs schedule gets cleared during the script and then they
									// reselect the schedule to play the script. Without this you can get NPCs stuck with m_iDelay = -1

	float m_startTime;				// Time when script actually started, used for synchronization
	bool m_bWaitForBeginSequence;	// Set to true when we are told to MoveToPosition. Holds the actor in the pre-action idle until BeginSequence is called.

	int m_saved_effects;
	int m_savedFlags;
	int m_savedCollisionGroup;

	bool m_interruptable;
	bool m_sequenceStarted;

	EHANDLE	m_hTargetEnt;

	EHANDLE m_hNextCine;		// The script to hand the NPC off to when we finish with them.
	
	bool	m_bThinking;
	bool 	m_bInitiatedSelfDelete;

	bool	m_bIsTeleportingDueToMoveTo;

	CAI_BaseNPC *FindScriptEntity( void );
	EHANDLE m_hLastFoundEntity;

	// Code forced us to use a specific NPC
	EHANDLE m_hForcedTarget;
	bool	m_bDontCancelOtherSequences;
	bool	m_bForceSynch;

	bool	m_bTargetWasAsleep;

	COutputEvent m_OnBeginSequence;
	COutputEvent m_OnEndSequence;
	COutputEvent m_OnPostIdleEndSequence;
	COutputEvent m_OnCancelSequence;
	COutputEvent m_OnCancelFailedSequence;	// Fired when a scene is cancelled before it's ever run
	COutputEvent m_OnScriptEvent[MAX_SCRIPT_EVENTS];

	static void ScriptEntityCancel( CBaseEntity *pentCine, bool bPretendSuccess = false );

	static const char *GetSpawnPreIdleSequenceForScript( CBaseEntity *pTargetEntity );

	// Dynamic interactions
	// For now, store just a single one of these. To synchronize positions
	// with multiple other NPCs, this needs to be an array of NPCs & desired position matrices.
	VMatrix		m_matInteractionPosition;
	EHANDLE		m_hInteractionRelativeEntity;

	int			m_iPlayerDeathBehavior;
};


#endif // SCRIPTED_H