1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 //===========================================================================
30 //
31 // Name:			ai_cast_script.c
32 // Function:		Wolfenstein AI Character Scripting
33 // Programmer:		Ridah
34 // Tab Size:		4 (real tabs)
35 //===========================================================================
36 
37 #include "g_local.h"
38 #include "../qcommon/q_shared.h"
39 #include "../botlib/botlib.h"      //bot lib interface
40 #include "../botlib/be_aas.h"
41 #include "../botlib/be_ea.h"
42 #include "../botlib/be_ai_gen.h"
43 #include "../botlib/be_ai_goal.h"
44 #include "../botlib/be_ai_move.h"
45 #include "../botlib/botai.h"          //bot ai interface
46 
47 #include "ai_cast.h"
48 
49 /*
50 Scripting that allows the designers to control the behaviour of AI characters
51 according to each different scenario.
52 */
53 
54 // action functions need to be declared here so they can be accessed in the scriptAction table
55 qboolean AICast_ScriptAction_GotoMarker( cast_state_t *cs, char *params );
56 qboolean AICast_ScriptAction_WalkToMarker( cast_state_t *cs, char *params );
57 qboolean AICast_ScriptAction_CrouchToMarker( cast_state_t *cs, char *params );
58 qboolean AICast_ScriptAction_GotoCast( cast_state_t *cs, char *params );
59 qboolean AICast_ScriptAction_WalkToCast( cast_state_t *cs, char *params );
60 qboolean AICast_ScriptAction_CrouchToCast( cast_state_t *cs, char *params );
61 qboolean AICast_ScriptAction_Wait( cast_state_t *cs, char *params );
62 qboolean AICast_ScriptAction_AbortIfLoadgame( cast_state_t *cs, char *params );	//----(SA)	added
63 qboolean AICast_ScriptAction_Trigger( cast_state_t *cs, char *params );
64 qboolean AICast_ScriptAction_FollowCast( cast_state_t *cs, char *params );
65 qboolean AICast_ScriptAction_PlaySound( cast_state_t *cs, char *params );
66 qboolean AICast_ScriptAction_NoAttack( cast_state_t *cs, char *params );
67 qboolean AICast_ScriptAction_Attack( cast_state_t *cs, char *params );
68 qboolean AICast_ScriptAction_PlayAnim( cast_state_t *cs, char *params );
69 qboolean AICast_ScriptAction_ClearAnim( cast_state_t *cs, char *params );
70 qboolean AICast_ScriptAction_SetAmmo( cast_state_t *cs, char *params );
71 qboolean AICast_ScriptAction_SetClip( cast_state_t *cs, char *params );			//----(SA)	added
72 qboolean AICast_ScriptAction_SelectWeapon( cast_state_t *cs, char *params );
73 qboolean AICast_ScriptAction_GiveArmor( cast_state_t *cs, char *params );		//----(SA)	added
74 qboolean AICast_ScriptAction_SetArmor( cast_state_t *cs, char *params );		//----(SA)	added
75 qboolean AICast_ScriptAction_SuggestWeapon( cast_state_t *cs, char *params );	//----(SA)	added
76 qboolean AICast_ScriptAction_GiveWeapon( cast_state_t *cs, char *params );
77 qboolean AICast_ScriptAction_GiveInventory( cast_state_t *cs, char *params );
78 qboolean AICast_ScriptAction_TakeWeapon( cast_state_t *cs, char *params );
79 qboolean AICast_ScriptAction_Movetype( cast_state_t *cs, char *params );
80 qboolean AICast_ScriptAction_AlertEntity( cast_state_t *cs, char *params );
81 qboolean AICast_ScriptAction_SaveGame( cast_state_t *cs, char *params );
82 qboolean AICast_ScriptAction_FireAtTarget( cast_state_t *cs, char *params );
83 qboolean AICast_ScriptAction_GodMode( cast_state_t *cs, char *params );
84 qboolean AICast_ScriptAction_Accum( cast_state_t *cs, char *params );
85 qboolean AICast_ScriptAction_SpawnCast( cast_state_t *cs, char *params );
86 qboolean AICast_ScriptAction_MissionFailed( cast_state_t *cs, char *params );
87 qboolean AICast_ScriptAction_ObjectiveMet( cast_state_t *cs, char *params );
88 qboolean AICast_ScriptAction_ObjectivesNeeded( cast_state_t *cs, char *params );
89 qboolean AICast_ScriptAction_NoAIDamage( cast_state_t *cs, char *params );
90 qboolean AICast_ScriptAction_Print( cast_state_t *cs, char *params );
91 qboolean AICast_ScriptAction_FaceTargetAngles( cast_state_t *cs, char *params );
92 qboolean AICast_ScriptAction_ResetScript( cast_state_t *cs, char *params );
93 qboolean AICast_ScriptAction_Mount( cast_state_t *cs, char *params );
94 qboolean AICast_ScriptAction_Unmount( cast_state_t *cs, char *params );
95 qboolean AICast_ScriptAction_SavePersistant( cast_state_t *cs, char *params );
96 qboolean AICast_ScriptAction_ChangeLevel( cast_state_t *cs, char *params );
97 qboolean AICast_ScriptAction_EndGame( cast_state_t *cs, char *params );			//----(SA)	added
98 qboolean AICast_ScriptAction_Teleport( cast_state_t *cs, char *params );		//----(SA)	added
99 qboolean AICast_ScriptAction_FoundSecret( cast_state_t *cs, char *params );
100 qboolean AICast_ScriptAction_NoSight( cast_state_t *cs, char *params );
101 qboolean AICast_ScriptAction_Sight( cast_state_t *cs, char *params );
102 qboolean AICast_ScriptAction_NoAvoid( cast_state_t *cs, char *params );
103 qboolean AICast_ScriptAction_Avoid( cast_state_t *cs, char *params );
104 qboolean AICast_ScriptAction_Attrib( cast_state_t *cs, char *params );
105 qboolean AICast_ScriptAction_DenyAction( cast_state_t *cs, char *params );
106 qboolean AICast_ScriptAction_LightningDamage( cast_state_t *cs, char *params );
107 qboolean AICast_ScriptAction_Headlook( cast_state_t *cs, char *params );
108 qboolean AICast_ScriptAction_BackupScript( cast_state_t *cs, char *params );
109 qboolean AICast_ScriptAction_RestoreScript( cast_state_t *cs, char *params );
110 qboolean AICast_ScriptAction_StateType( cast_state_t *cs, char *params );
111 qboolean AICast_ScriptAction_KnockBack( cast_state_t *cs, char *params );
112 qboolean AICast_ScriptAction_Zoom( cast_state_t *cs, char *params );
113 qboolean AICast_ScriptAction_Parachute( cast_state_t *cs, char *params );
114 qboolean AICast_ScriptAction_Cigarette( cast_state_t *cs, char *params );		//----(SA)	added
115 qboolean AICast_ScriptAction_StartCam( cast_state_t *cs, char *params );
116 qboolean AICast_ScriptAction_StopCam( cast_state_t *cs, char *params );			//----(SA)	added
117 qboolean AICast_ScriptAction_StartCamBlack( cast_state_t *cs, char *params );
118 qboolean AICast_ScriptAction_EntityScriptName( cast_state_t *cs, char *params );
119 qboolean AICast_ScriptAction_AIScriptName( cast_state_t *cs, char *params );
120 qboolean AICast_ScriptAction_SetHealth( cast_state_t *cs, char *params );
121 qboolean AICast_ScriptAction_NoTarget( cast_state_t *cs, char *params );
122 qboolean AICast_ScriptAction_Cvar( cast_state_t *cs, char *params );
123 
124 qboolean AICast_ScriptAction_MusicStart( cast_state_t *cs, char *params );		//----(SA)
125 qboolean AICast_ScriptAction_MusicPlay( cast_state_t *cs, char *params );		//----(SA)
126 qboolean AICast_ScriptAction_MusicStop( cast_state_t *cs, char *params );		//----(SA)
127 qboolean AICast_ScriptAction_MusicFade( cast_state_t *cs, char *params );		//----(SA)
128 qboolean AICast_ScriptAction_MusicQueue( cast_state_t *cs, char *params );		//----(SA)
129 
130 qboolean AICast_ScriptAction_ExplicitRouting( cast_state_t *cs, char *params );
131 qboolean AICast_ScriptAction_LockPlayer( cast_state_t *cs, char *params );
132 qboolean AICast_ScriptAction_AnimCondition( cast_state_t *cs, char *params );
133 qboolean AICast_ScriptAction_PushAway( cast_state_t *cs, char *params );
134 qboolean AICast_ScriptAction_CatchFire( cast_state_t *cs, char *params );
135 
136 // these are the actions that each event can call
137 cast_script_stack_action_t scriptActions[] =
138 {
139 	{"gotomarker",       AICast_ScriptAction_GotoMarker},
140 	{"runtomarker",      AICast_ScriptAction_GotoMarker},
141 	{"walktomarker", AICast_ScriptAction_WalkToMarker},
142 	{"crouchtomarker",   AICast_ScriptAction_CrouchToMarker},
143 	{"gotocast",     AICast_ScriptAction_GotoCast},
144 	{"runtocast",        AICast_ScriptAction_GotoCast},
145 	{"walktocast",       AICast_ScriptAction_WalkToCast},
146 	{"crouchtocast", AICast_ScriptAction_CrouchToCast},
147 	{"followcast",       AICast_ScriptAction_FollowCast},
148 	{"playsound",        AICast_ScriptAction_PlaySound},
149 	{"playanim",     AICast_ScriptAction_PlayAnim},
150 	{"clearanim",        AICast_ScriptAction_ClearAnim},
151 	{"wait",         AICast_ScriptAction_Wait},
152 	{"abort_if_loadgame",AICast_ScriptAction_AbortIfLoadgame},		//----(SA)	added
153 	{"trigger",          AICast_ScriptAction_Trigger},
154 	{"setammo",          AICast_ScriptAction_SetAmmo},
155 	{"setclip",          AICast_ScriptAction_SetClip},				//----(SA)	added
156 	{"selectweapon", AICast_ScriptAction_SelectWeapon},
157 	{"noattack",     AICast_ScriptAction_NoAttack},
158 	{"suggestweapon",    AICast_ScriptAction_SuggestWeapon},		//----(SA)	added
159 	{"attack",           AICast_ScriptAction_Attack},
160 	{"givearmor",        AICast_ScriptAction_GiveArmor},			//----(SA)	added
161 	{"setarmor",     AICast_ScriptAction_SetArmor},					//----(SA)	added
162 	{"giveinventory",    AICast_ScriptAction_GiveInventory},
163 	{"giveweapon",       AICast_ScriptAction_GiveWeapon},
164 	{"takeweapon",       AICast_ScriptAction_TakeWeapon},
165 	{"movetype",     AICast_ScriptAction_Movetype},
166 	{"alertentity",      AICast_ScriptAction_AlertEntity},
167 	{"savegame",     AICast_ScriptAction_SaveGame},
168 	{"fireattarget", AICast_ScriptAction_FireAtTarget},
169 	{"godmode",          AICast_ScriptAction_GodMode},
170 	{"accum",            AICast_ScriptAction_Accum},
171 	{"spawncast",        AICast_ScriptAction_SpawnCast},
172 	{"missionfailed",    AICast_ScriptAction_MissionFailed},
173 	{"missionsuccess",   AICast_ScriptAction_ObjectiveMet},
174 	{"objectivemet", AICast_ScriptAction_ObjectiveMet},				// dupe of missionsuccess so scripts can changeover to a more logical name
175 	{"objectivesneeded",AICast_ScriptAction_ObjectivesNeeded},
176 	{"noaidamage",       AICast_ScriptAction_NoAIDamage},
177 	{"print",            AICast_ScriptAction_Print},
178 	{"facetargetangles",AICast_ScriptAction_FaceTargetAngles},
179 	{"resetscript",      AICast_ScriptAction_ResetScript},
180 	{"mount",            AICast_ScriptAction_Mount},
181 	{"unmount",          AICast_ScriptAction_Unmount},
182 	{"savepersistant",   AICast_ScriptAction_SavePersistant},
183 	{"changelevel",      AICast_ScriptAction_ChangeLevel},
184 	{"endgame",          AICast_ScriptAction_EndGame},				//----(SA)	added
185 	{"teleport",     AICast_ScriptAction_Teleport},					//----(SA)	added
186 	{"foundsecret",      AICast_ScriptAction_FoundSecret},
187 	{"nosight",          AICast_ScriptAction_NoSight},
188 	{"sight",            AICast_ScriptAction_Sight},
189 	{"noavoid",          AICast_ScriptAction_NoAvoid},
190 	{"avoid",            AICast_ScriptAction_Avoid},
191 	{"attrib",           AICast_ScriptAction_Attrib},
192 	{"denyactivate", AICast_ScriptAction_DenyAction},
193 	{"lightningdamage",  AICast_ScriptAction_LightningDamage},
194 	{"deny",         AICast_ScriptAction_DenyAction},
195 	{"headlook",     AICast_ScriptAction_Headlook},
196 	{"backupscript", AICast_ScriptAction_BackupScript},
197 	{"restorescript",    AICast_ScriptAction_RestoreScript},
198 	{"statetype",        AICast_ScriptAction_StateType},
199 	{"knockback",        AICast_ScriptAction_KnockBack},
200 	{"zoom",         AICast_ScriptAction_Zoom},
201 	{"parachute",        AICast_ScriptAction_Parachute},
202 	{"cigarette",        AICast_ScriptAction_Cigarette},			//----(SA)	added
203 	{"startcam",     AICast_ScriptAction_StartCam},
204 	{"startcamblack",    AICast_ScriptAction_StartCamBlack},
205 	{"stopcam",          AICast_ScriptAction_StopCam},				//----(SA)	added
206 	{"entityscriptname",AICast_ScriptAction_EntityScriptName},
207 	{"aiscriptname", AICast_ScriptAction_AIScriptName},
208 	{"sethealth",        AICast_ScriptAction_SetHealth},
209 	{"notarget",     AICast_ScriptAction_NoTarget},
210 	{"cvar",         AICast_ScriptAction_Cvar},
211 
212 //----(SA)	added some music interface
213 	{"mu_start",     AICast_ScriptAction_MusicStart},			// (char *new_music, int time)	// time to fade in
214 	{"mu_play",          AICast_ScriptAction_MusicPlay},		// (char *new_music)
215 	{"mu_stop",          AICast_ScriptAction_MusicStop},		// (int time)	// time to fadeout
216 	{"mu_fade",          AICast_ScriptAction_MusicFade},		// (float target_volume, int time)	// time to fade to target
217 	{"mu_queue",     AICast_ScriptAction_MusicQueue},			// (char *new_music)	// music that will start when previous fades to 0
218 //----(SA)	end
219 
220 	{"explicit_routing", AICast_ScriptAction_ExplicitRouting},
221 	{"lockplayer",       AICast_ScriptAction_LockPlayer},
222 	{"anim_condition",   AICast_ScriptAction_AnimCondition},
223 	{"pushaway",     AICast_ScriptAction_PushAway},
224 	{"catchfire",        AICast_ScriptAction_CatchFire},
225 
226 	{NULL,              0}
227 };
228 
229 qboolean AICast_EventMatch_StringEqual( cast_script_event_t *event, char *eventParm );
230 qboolean AICast_EventMatch_IntInRange( cast_script_event_t *event, char *eventParm );
231 
232 // the list of events that can start an action sequence
233 // NOTE!!: only append to this list, DO NOT INSERT!!
234 cast_script_event_define_t scriptEvents[] =
235 {
236 	{"spawn",            0},          // called as each character is spawned into the game
237 	{"playerstart",      0},          // called when player hits 'start' button
238 	{"enemysight",       AICast_EventMatch_StringEqual}, // enemy has been sighted for the first time (once only)
239 	{"sight",            AICast_EventMatch_StringEqual}, // non-enemy has been sighted for the first time (once only)
240 	{"enemydead",        AICast_EventMatch_StringEqual}, // our enemy is now dead
241 	{"trigger",          AICast_EventMatch_StringEqual}, // something has triggered us (always followed by an identifier)
242 	{"pain",         AICast_EventMatch_IntInRange},  // we've been hurt
243 	{"death",            AICast_EventMatch_StringEqual},	// RIP
244 	{"activate",     AICast_EventMatch_StringEqual}, // "param" has just activated us
245 	{"enemysightcorpse",AICast_EventMatch_StringEqual},  // sighted the given enemy as a corpse, for the first time
246 	{"friendlysightcorpse", 0},                       // sighted a friendly as a corpse for the first time
247 	{"avoiddanger",      AICast_EventMatch_StringEqual}, // we are avoiding something dangerous
248 	{"blocked",          AICast_EventMatch_StringEqual}, // blocked by someone else
249 	{"statechange",      AICast_EventMatch_StringEqual}, // changing aistates
250 	{"bulletimpact", 0},
251 	{"inspectbodystart", AICast_EventMatch_StringEqual}, // starting to travel to body for inspection
252 	{"inspectbodyend",   AICast_EventMatch_StringEqual}, // reached body for inspection
253 	{"inspectsoundstart",    AICast_EventMatch_StringEqual}, // reached sound for inspection
254 	{"inspectsoundend",  AICast_EventMatch_StringEqual}, // reached sound for inspection
255 	{"attacksound",      AICast_EventMatch_StringEqual}, // play a custom attack sound, and/or deny playing the default sound
256 	{"fakedeath",        0},
257 	{"bulletimpactsound",    0},
258 	{"inspectfriendlycombatstart", 0},
259 	{"painenemy",        AICast_EventMatch_StringEqual},
260 	{"forced_mg42_unmount",  0},
261 
262 	{NULL,              0}
263 };
264 
265 
266 /*
267 ===============
268 AICast_EventMatch_StringEqual
269 ===============
270 */
AICast_EventMatch_StringEqual(cast_script_event_t * event,char * eventParm)271 qboolean AICast_EventMatch_StringEqual( cast_script_event_t *event, char *eventParm ) {
272 	if ( !event->params || !event->params[0] || ( eventParm && !Q_strcasecmp( event->params, eventParm ) ) ) {
273 		return qtrue;
274 	} else {
275 		return qfalse;
276 	}
277 }
278 
279 /*
280 ===============
281 AICast_EventMatch_IntInRange
282 ===============
283 */
AICast_EventMatch_IntInRange(cast_script_event_t * event,char * eventParm)284 qboolean AICast_EventMatch_IntInRange( cast_script_event_t *event, char *eventParm ) {
285 	char *pString, *token;
286 	int int1, int2, eInt;
287 
288 	// get the cast name
289 	pString = eventParm;
290 	token = COM_ParseExt( &pString, qfalse );
291 	int1 = atoi( token );
292 	token = COM_ParseExt( &pString, qfalse );
293 	int2 = atoi( token );
294 
295 	eInt = atoi( event->params );
296 
297 	if ( eventParm && eInt > int1 && eInt <= int2 ) {
298 		return qtrue;
299 	} else {
300 		return qfalse;
301 	}
302 }
303 
304 /*
305 ===============
306 AICast_EventForString
307 ===============
308 */
AICast_EventForString(char * string)309 int AICast_EventForString( char *string ) {
310 	int i;
311 
312 	for ( i = 0; scriptEvents[i].eventStr; i++ )
313 	{
314 		if ( !Q_strcasecmp( string, scriptEvents[i].eventStr ) ) {
315 			return i;
316 		}
317 	}
318 
319 	return -1;
320 }
321 
322 /*
323 ===============
324 AICast_ActionForString
325 ===============
326 */
AICast_ActionForString(cast_state_t * cs,char * string)327 cast_script_stack_action_t *AICast_ActionForString( cast_state_t *cs, char *string ) {
328 	int i;
329 
330 	for ( i = 0; scriptActions[i].actionString; i++ )
331 	{
332 		if ( !Q_strcasecmp( string, scriptActions[i].actionString ) ) {
333 			if ( !Q_strcasecmp( string, "foundsecret" ) ) {
334 				level.numSecrets++;
335 				G_SendMissionStats();
336 			}
337 			return &scriptActions[i];
338 		}
339 	}
340 
341 	return NULL;
342 }
343 
344 /*
345 =============
346 AICast_ScriptLoad
347 
348   Loads the script for the current level into the buffer
349 =============
350 */
AICast_ScriptLoad(void)351 void AICast_ScriptLoad( void ) {
352 	char filename[MAX_QPATH];
353 	vmCvar_t mapname;
354 	fileHandle_t f;
355 	int len;
356 
357 	level.scriptAI = NULL;
358 
359 	trap_Cvar_VariableStringBuffer( "ai_scriptName", filename, sizeof( filename ) );
360 	if ( strlen( filename ) > 0 ) {
361 		trap_Cvar_Register( &mapname, "ai_scriptName", "", CVAR_ROM );
362 	} else {
363 		trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
364 	}
365 	Q_strncpyz( filename, "maps/", sizeof( filename ) );
366 	Q_strcat( filename, sizeof( filename ), mapname.string );
367 	Q_strcat( filename, sizeof( filename ), ".ai" );
368 
369 	len = trap_FS_FOpenFile( filename, &f, FS_READ );
370 
371 	// make sure we clear out the temporary scriptname
372 	trap_Cvar_Set( "ai_scriptName", "" );
373 
374 	if ( len < 0 ) {
375 		return;
376 	}
377 
378 	level.scriptAI = G_Alloc( len );
379 	trap_FS_Read( level.scriptAI, len, f );
380 
381 	trap_FS_FCloseFile( f );
382 
383 	return;
384 }
385 
386 /*
387 ==============
388 AICast_ScriptParse
389 
390   Parses the script for the given character
391 ==============
392 */
393 #define MAX_SCRIPT_EVENTS   64
394 cast_script_event_t cast_temp_events[MAX_SCRIPT_EVENTS];
AICast_ScriptParse(cast_state_t * cs)395 void AICast_ScriptParse( cast_state_t *cs ) {
396 	gentity_t   *ent;
397 	char        *pScript;
398 	char        *token;
399 	qboolean wantName;
400 	qboolean inScript;
401 	int eventNum;
402 	int numEventItems;
403 	cast_script_event_t *curEvent;
404 	char params[MAX_QPATH];
405 	cast_script_stack_action_t  *action;
406 	int i;
407 	int bracketLevel;
408 	qboolean buildScript;       //----(SA)	added
409 
410 	if ( !level.scriptAI ) {
411 		return;
412 	}
413 
414 	ent = &g_entities[cs->entityNum];
415 	if ( !ent->aiName ) {
416 		return;
417 	}
418 
419 	buildScript = qtrue;
420 
421 	pScript = level.scriptAI;
422 	wantName = qtrue;
423 	inScript = qfalse;
424 	COM_BeginParseSession( "AICast_ScriptParse" );
425 	bracketLevel = 0;
426 	numEventItems = 0;
427 
428 	memset( cast_temp_events, 0, sizeof( cast_temp_events ) );
429 
430 	while ( 1 )
431 	{
432 		token = COM_Parse( &pScript );
433 
434 		if ( !token[0] ) {
435 			if ( !wantName ) {
436 				G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
437 			}
438 			break;
439 		}
440 
441 		// end of script
442 		if ( token[0] == '}' ) {
443 			if ( inScript ) {
444 				break;
445 			}
446 			if ( wantName ) {
447 				G_Error( "AICast_ScriptParse(), Error (line %d): '}' found, but not expected.\n", COM_GetCurrentParseLine() );
448 			}
449 			wantName = qtrue;
450 		} else if ( token[0] == '{' )    {
451 			if ( wantName ) {
452 				G_Error( "AICast_ScriptParse(), Error (line %d): '{' found, NAME expected.\n", COM_GetCurrentParseLine() );
453 			}
454 		} else if ( wantName )   {
455 			if ( !Q_strcasecmp( ent->aiName, token ) ) {
456 				inScript = qtrue;
457 				numEventItems = 0;
458 			}
459 			wantName = qfalse;
460 		} else if ( inScript )   {
461 			if ( !Q_strcasecmp( token, "attributes" ) ) {
462 				// read in all the attributes
463 				AICast_CheckLevelAttributes( cs, ent, &pScript );
464 				continue;
465 			}
466 			eventNum = AICast_EventForString( token );
467 			if ( eventNum < 0 ) {
468 				G_Error( "AICast_ScriptParse(), Error (line %d): unknown event: %s.\n", COM_GetCurrentParseLine(), token );
469 			}
470 			if ( numEventItems >= MAX_SCRIPT_EVENTS ) {
471 				G_Error( "AICast_ScriptParse(), Error (line %d): MAX_SCRIPT_EVENTS reached (%d)\n", COM_GetCurrentParseLine(), MAX_SCRIPT_EVENTS );
472 			}
473 
474 			// if this is a "friendlysightcorpse" event, then disable corpse vis sharing
475 			if ( !Q_stricmp( token, "friendlysightcorpse" ) ) {
476 				cs->aiFlags &= ~AIFL_CORPSESIGHTING;
477 			}
478 
479 			curEvent = &cast_temp_events[numEventItems];
480 			curEvent->eventNum = eventNum;
481 			memset( params, 0, sizeof( params ) );
482 
483 			// parse any event params before the start of this event's actions
484 			while ( ( token = COM_Parse( &pScript ) ) && ( token[0] != '{' ) )
485 			{
486 				if ( !token[0] ) {
487 					G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
488 				}
489 
490 				if ( eventNum == 13 ) {   // statechange event, check params
491 					if ( strlen( token ) > 1 ) {
492 						if ( BG_IndexForString( token, animStateStr, qtrue ) < 0 ) {
493 							G_Error( "AICast_ScriptParse(), Error (line %d): unknown state type '%s'.\n", COM_GetCurrentParseLine(), token );
494 						}
495 					}
496 				}
497 
498 				if ( strlen( params ) ) { // add a space between each param
499 					Q_strcat( params, sizeof( params ), " " );
500 				}
501 				Q_strcat( params, sizeof( params ), token );
502 			}
503 
504 			if ( strlen( params ) ) { // copy the params into the event
505 				curEvent->params = G_Alloc( strlen( params ) + 1 );
506 				Q_strncpyz( curEvent->params, params, strlen( params ) + 1 );
507 			}
508 
509 			// parse the actions for this event
510 			while ( ( token = COM_Parse( &pScript ) ) && ( token[0] != '}' ) )
511 			{
512 				if ( !token[0] ) {
513 					G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
514 				}
515 
516 				action = AICast_ActionForString( cs, token );
517 				if ( !action ) {
518 					G_Error( "AICast_ScriptParse(), Error (line %d): unknown action: %s.\n", COM_GetCurrentParseLine(), token );
519 				}
520 
521 				curEvent->stack.items[curEvent->stack.numItems].action = action;
522 
523 				memset( params, 0, sizeof( params ) );
524 				token = COM_ParseExt( &pScript, qfalse );
525 				for ( i = 0; token[0]; i++ )
526 				{
527 					if ( strlen( params ) ) { // add a space between each param
528 						Q_strcat( params, sizeof( params ), " " );
529 					}
530 
531 					if ( i == 0 ) {
532 						// Special case: playsound's need to be cached on startup to prevent in-game pauses
533 						if ( !Q_stricmp( action->actionString, "playsound" ) ) {
534 							G_SoundIndex( token );
535 						}
536 
537 //----(SA)	added a bit more
538 						if (    buildScript && (
539 									!Q_stricmp( action->actionString, "mu_start" ) ||
540 									!Q_stricmp( action->actionString, "mu_play" ) ||
541 									!Q_stricmp( action->actionString, "mu_queue" ) ||
542 									!Q_stricmp( action->actionString, "startcam" ) ||
543 									!Q_stricmp( action->actionString, "startcamblack" ) )
544 								) {
545 							if ( strlen( token ) ) { // we know there's a [0], but don't know if it's '0'
546 								trap_SendServerCommand( cs->entityNum, va( "addToBuild %s\n", token ) );
547 							}
548 						}
549 
550 						if ( !Q_stricmp( action->actionString, "giveweapon" ) ) { // register weapon for client pre-loading
551 							gitem_t *weap = BG_FindItem2( token );    // (SA) FIXME: rats, need to fix this for weapon names with spaces: 'mauser rifle'
552 //							if(weap)
553 							RegisterItem( weap );   // don't be nice, just do it.  if it can't find it, you'll bomb out to the error menu
554 						}
555 //----(SA)	end
556 					}
557 
558 					if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word
559 						Q_strcat( params, sizeof( params ), "\"" );
560 					}
561 
562 					Q_strcat( params, sizeof( params ), token );
563 
564 					if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word
565 						Q_strcat( params, sizeof( params ), "\"" );
566 					}
567 
568 					token = COM_ParseExt( &pScript, qfalse );
569 				}
570 
571 				if ( strlen( params ) ) { // copy the params into the event
572 					curEvent->stack.items[curEvent->stack.numItems].params = G_Alloc( strlen( params ) + 1 );
573 					Q_strncpyz( curEvent->stack.items[curEvent->stack.numItems].params, params, strlen( params ) + 1 );
574 				}
575 
576 				curEvent->stack.numItems++;
577 
578 				if ( curEvent->stack.numItems >= AICAST_MAX_SCRIPT_STACK_ITEMS ) {
579 					G_Error( "AICast_ScriptParse(): script exceeded MAX_SCRIPT_ITEMS (%d), line %d\n", AICAST_MAX_SCRIPT_STACK_ITEMS, COM_GetCurrentParseLine() );
580 				}
581 			}
582 
583 			numEventItems++;
584 		} else    // skip this character completely
585 		{
586 
587 			while ( ( token = COM_Parse( &pScript ) ) )
588 			{
589 				if ( !token[0] ) {
590 					G_Error( "AICast_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
591 				} else if ( token[0] == '{' ) {
592 					bracketLevel++;
593 				} else if ( token[0] == '}' ) {
594 					if ( !--bracketLevel ) {
595 						break;
596 					}
597 				}
598 			}
599 		}
600 	}
601 
602 	// alloc and copy the events into the cast_state_t for this cast
603 	if ( numEventItems > 0 ) {
604 		cs->castScriptEvents = G_Alloc( sizeof( cast_script_event_t ) * numEventItems );
605 		memcpy( cs->castScriptEvents, cast_temp_events, sizeof( cast_script_event_t ) * numEventItems );
606 		cs->numCastScriptEvents = numEventItems;
607 
608 		cs->castScriptStatus.castScriptEventIndex = -1;
609 	}
610 }
611 
612 /*
613 ================
614 AICast_ScriptChange
615 ================
616 */
AICast_ScriptChange(cast_state_t * cs,int newScriptNum)617 void AICast_ScriptChange( cast_state_t *cs, int newScriptNum ) {
618 	cast_script_status_t scriptStatusBackup;
619 
620 	cs->scriptCallIndex++;
621 
622 	// backup the current scripting
623 	scriptStatusBackup = cs->castScriptStatus;
624 
625 	// set the new script to this cast, and reset script status
626 	cs->castScriptStatus.castScriptStackHead = 0;
627 	cs->castScriptStatus.castScriptStackChangeTime = level.time;
628 	cs->castScriptStatus.castScriptEventIndex = newScriptNum;
629 	cs->castScriptStatus.scriptId = scriptStatusBackup.scriptId + 1;
630 	cs->castScriptStatus.scriptGotoId = -1;
631 	cs->castScriptStatus.scriptGotoEnt = -1;
632 	cs->castScriptStatus.scriptFlags |= SFL_FIRST_CALL;
633 
634 	// try and run the script, if it doesn't finish, then abort the current script (discard backup)
635 	if ( AICast_ScriptRun( cs, qtrue ) ) {
636 		// completed successfully
637 		cs->castScriptStatus.castScriptStackHead = scriptStatusBackup.castScriptStackHead;
638 		cs->castScriptStatus.castScriptStackChangeTime = scriptStatusBackup.castScriptStackChangeTime;
639 		cs->castScriptStatus.castScriptEventIndex = scriptStatusBackup.castScriptEventIndex;
640 		cs->castScriptStatus.scriptId = scriptStatusBackup.scriptId;
641 		cs->castScriptStatus.scriptFlags = scriptStatusBackup.scriptFlags;
642 	}
643 }
644 
645 /*
646 ================
647 AICast_ScriptEvent
648 
649   An event has occured, for which a script may exist
650 ================
651 */
AICast_ScriptEvent(struct cast_state_s * cs,char * eventStr,char * params)652 void AICast_ScriptEvent( struct cast_state_s *cs, char *eventStr, char *params ) {
653 	int i, eventNum;
654 
655 	eventNum = -1;
656 
657 	// find out which event this is
658 	for ( i = 0; scriptEvents[i].eventStr; i++ )
659 	{
660 		if ( !Q_strcasecmp( eventStr, scriptEvents[i].eventStr ) ) { // match found
661 			eventNum = i;
662 			break;
663 		}
664 	}
665 
666 	if ( eventNum < 0 ) {
667 		if ( g_cheats.integer ) { // dev mode
668 			G_Printf( "devmode-> AICast_ScriptEvent(), unknown event: %s\n", eventStr );
669 		}
670 	}
671 
672 	// show debugging info
673 	if (    (   ( aicast_debug.integer == 1 ) ||
674 				(   ( aicast_debug.integer == 2 ) &&
675 					( ( strlen( aicast_debugname.string ) < 1 ) || ( g_entities[cs->entityNum].aiName && !strcmp( aicast_debugname.string, g_entities[cs->entityNum].aiName ) ) ) ) ) ) {
676 		G_Printf( "(%s) AIScript event: %s %s ", g_entities[cs->entityNum].aiName, eventStr, params );
677 	}
678 
679 	cs->aiFlags &= ~AIFL_DENYACTION;
680 
681 	// see if this cast has this event
682 	for ( i = 0; i < cs->numCastScriptEvents; i++ )
683 	{
684 		if ( cs->castScriptEvents[i].eventNum == eventNum ) {
685 			if (    ( !cs->castScriptEvents[i].params )
686 					||  ( !scriptEvents[eventNum].eventMatch || scriptEvents[eventNum].eventMatch( &cs->castScriptEvents[i], params ) ) ) {
687 
688 				// show debugging info
689 				if (    (   ( aicast_debug.integer == 1 ) ||
690 							(   ( aicast_debug.integer == 2 ) &&
691 								( ( strlen( aicast_debugname.string ) < 1 ) || ( g_entities[cs->entityNum].aiName && !strcmp( aicast_debugname.string, g_entities[cs->entityNum].aiName ) ) ) ) ) ) {
692 					G_Printf( "found, calling script: (%s) %s %s\n", g_entities[cs->entityNum].aiName, eventStr, params );
693 				}
694 
695 				AICast_ScriptChange( cs, i );
696 				break;
697 			}
698 		}
699 	}
700 
701 	// show debugging info
702 	if (    (   ( aicast_debug.integer == 1 ) ||
703 				(   ( aicast_debug.integer == 2 ) &&
704 					( ( strlen( aicast_debugname.string ) < 1 ) || ( g_entities[cs->entityNum].aiName && !strcmp( aicast_debugname.string, g_entities[cs->entityNum].aiName ) ) ) ) ) ) {
705 		if ( i == cs->numCastScriptEvents ) {
706 			G_Printf( "not found\n" );
707 		}
708 	}
709 
710 }
711 
712 /*
713 ================
714 AICast_ForceScriptEvent
715 
716   Definately run this event now, overriding any paised state
717 ================
718 */
AICast_ForceScriptEvent(struct cast_state_s * cs,char * eventStr,char * params)719 void AICast_ForceScriptEvent( struct cast_state_s *cs, char *eventStr, char *params ) {
720 	int oldPauseTime;
721 
722 	oldPauseTime = cs->scriptPauseTime;
723 	cs->scriptPauseTime = 0;
724 
725 	AICast_ScriptEvent( cs, eventStr, params );
726 
727 	cs->scriptPauseTime = oldPauseTime;
728 }
729 
730 /*
731 =============
732 AICast_ScriptRun
733 
734   returns qtrue if the script completed
735 =============
736 */
AICast_ScriptRun(cast_state_t * cs,qboolean force)737 qboolean AICast_ScriptRun( cast_state_t *cs, qboolean force ) {
738 	cast_script_stack_t *stack;
739 
740 	if ( !aicast_scripts.integer ) {
741 		return qtrue;
742 	}
743 
744 	if ( cs->castScriptStatus.castScriptEventIndex < 0 ) {
745 		return qtrue;
746 	}
747 
748 	if ( !cs->castScriptEvents ) {
749 		cs->castScriptStatus.castScriptEventIndex = -1;
750 		return qtrue;
751 	}
752 
753 	// only allow the PLAYER'S spawn function through if we're NOT still waiting on everything to finish loading in
754 	if ( !cs->entityNum && saveGamePending && Q_stricmp( "spawn", scriptEvents[cs->castScriptEvents[cs->castScriptStatus.castScriptEventIndex].eventNum].eventStr ) ) {
755 		//char loading[4];
756 		//trap_Cvar_VariableStringBuffer( "savegame_loading", loading, sizeof(loading) );
757 		//if (strlen( loading ) > 0 && atoi(loading) != 0)	// we're loading a savegame
758 		return qfalse;
759 	}
760 
761 	if ( !force && ( cs->scriptPauseTime >= level.time ) ) {
762 		return qtrue;
763 	}
764 
765 	stack = &cs->castScriptEvents[cs->castScriptStatus.castScriptEventIndex].stack;
766 
767 	if ( !stack->numItems ) {
768 		cs->castScriptStatus.castScriptEventIndex = -1;
769 		return qtrue;
770 	}
771 
772 	while ( cs->castScriptStatus.castScriptStackHead < stack->numItems )
773 	{
774 		//
775 		// show debugging info
776 		if (    ( cs->castScriptStatus.castScriptStackChangeTime == level.time ) &&
777 				(   ( aicast_debug.integer == 1 ) ||
778 					(   ( aicast_debug.integer == 2 ) &&
779 						( ( strlen( aicast_debugname.string ) < 1 ) || ( g_entities[cs->entityNum].aiName && !strcmp( aicast_debugname.string, g_entities[cs->entityNum].aiName ) ) ) ) ) ) {
780 			G_Printf( "(%s) AIScript command: %s %s\n", g_entities[cs->entityNum].aiName, stack->items[cs->castScriptStatus.castScriptStackHead].action->actionString, ( stack->items[cs->castScriptStatus.castScriptStackHead].params ? stack->items[cs->castScriptStatus.castScriptStackHead].params : "" ) );
781 		}
782 		//
783 		if ( !stack->items[cs->castScriptStatus.castScriptStackHead].action->actionFunc( cs, stack->items[cs->castScriptStatus.castScriptStackHead].params ) ) {
784 			// check that we are still running the same script that we were when we call the action
785 			if ( cs->castScriptStatus.castScriptEventIndex >= 0 && stack == &cs->castScriptEvents[cs->castScriptStatus.castScriptEventIndex].stack ) {
786 				cs->castScriptStatus.scriptFlags &= ~SFL_FIRST_CALL;
787 			}
788 			return qfalse;
789 		}
790 		// move to the next action in the script
791 		cs->castScriptStatus.castScriptStackHead++;
792 		// record the time that this new item became active
793 		cs->castScriptStatus.castScriptStackChangeTime = level.time;
794 		// reset misc stuff
795 		cs->castScriptStatus.scriptGotoId = -1;
796 		cs->castScriptStatus.scriptGotoEnt = -1;
797 		cs->castScriptStatus.scriptFlags |= SFL_FIRST_CALL;
798 	}
799 
800 	cs->castScriptStatus.castScriptEventIndex = -1;
801 
802 	return qtrue;
803 }
804