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