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: g_script.c
32 // Function: Wolfenstein Entity Scripting
33 // Programmer: Ridah
34 // Tab Size: 4 (real tabs)
35 //===========================================================================
36
37 #include "g_local.h"
38 #include "../qcommon/q_shared.h"
39
40 /*
41 Scripting that allows the designers to control the behaviour of entities
42 according to each different scenario.
43 */
44
45 vmCvar_t g_scriptDebug;
46
47 //
48 //====================================================================
49 //
50 // action functions need to be declared here so they can be accessed in the scriptAction table
51 qboolean G_ScriptAction_GotoMarker( gentity_t *ent, char *params );
52 qboolean G_ScriptAction_Wait( gentity_t *ent, char *params );
53 qboolean G_ScriptAction_Trigger( gentity_t *ent, char *params );
54 qboolean G_ScriptAction_PlaySound( gentity_t *ent, char *params );
55 qboolean G_ScriptAction_PlayAnim( gentity_t *ent, char *params );
56 qboolean G_ScriptAction_AlertEntity( gentity_t *ent, char *params );
57 qboolean G_ScriptAction_Accum( gentity_t *ent, char *params );
58 qboolean G_ScriptAction_MissionFailed( gentity_t *ent, char *params );
59 qboolean G_ScriptAction_MissionSuccess( gentity_t *ent, char *params );
60 qboolean G_ScriptAction_Print( gentity_t *ent, char *params );
61 qboolean G_ScriptAction_FaceAngles( gentity_t *ent, char *params );
62 qboolean G_ScriptAction_ResetScript( gentity_t *ent, char *params );
63 qboolean G_ScriptAction_TagConnect( gentity_t *ent, char *params );
64 qboolean G_ScriptAction_Halt( gentity_t *ent, char *params );
65 qboolean G_ScriptAction_StopSound( gentity_t *ent, char *params );
66 qboolean G_ScriptAction_StartCam( gentity_t *ent, char *params );
67 qboolean G_ScriptAction_StartCamBlack( gentity_t *ent, char *params );
68 qboolean G_ScriptAction_EntityScriptName( gentity_t *ent, char *params );
69 qboolean G_ScriptAction_AIScriptName( gentity_t *ent, char *params );
70 // DHM - Nerve :: Multiplayer scripting commands
71 qboolean G_ScriptAction_MapDescription( gentity_t *ent, char *params );
72 qboolean G_ScriptAction_AxisRespawntime( gentity_t *ent, char *params );
73 qboolean G_ScriptAction_AlliedRespawntime( gentity_t *ent, char *params );
74 qboolean G_ScriptAction_NumberofObjectives( gentity_t *ent, char *params );
75 qboolean G_ScriptAction_ObjectiveAxisDesc( gentity_t *ent, char *params );
76 qboolean G_ScriptAction_ObjectiveAlliedDesc( gentity_t *ent, char *params );
77 qboolean G_ScriptAction_SetWinner( gentity_t *ent, char *params );
78 qboolean G_ScriptAction_SetObjectiveStatus( gentity_t *ent, char *params );
79 qboolean G_ScriptAction_Announce( gentity_t *ent, char *params );
80 qboolean G_ScriptAction_EndRound( gentity_t *ent, char *params );
81 qboolean G_ScriptAction_SetRoundTimelimit( gentity_t *ent, char *params );
82 // dhm
83 qboolean G_ScriptAction_BackupScript( gentity_t *ent, char *params );
84 qboolean G_ScriptAction_RestoreScript( gentity_t *ent, char *params );
85 qboolean G_ScriptAction_SetHealth( gentity_t *ent, char *params );
86
87 //----(SA) added
88 qboolean G_ScriptAction_MusicStart( gentity_t *ent, char *params );
89 qboolean G_ScriptAction_MusicPlay( gentity_t *ent, char *params );
90 qboolean G_ScriptAction_MusicStop( gentity_t *ent, char *params );
91 qboolean G_ScriptAction_MusicFade( gentity_t *ent, char *params );
92 qboolean G_ScriptAction_MusicQueue( gentity_t *ent, char *params );
93 //----(SA) end
94
95 // these are the actions that each event can call
96 g_script_stack_action_t gScriptActions[] =
97 {
98 {"gotomarker", G_ScriptAction_GotoMarker},
99 {"playsound", G_ScriptAction_PlaySound},
100 {"playanim", G_ScriptAction_PlayAnim},
101 {"wait", G_ScriptAction_Wait},
102 {"trigger", G_ScriptAction_Trigger},
103 {"alertentity", G_ScriptAction_AlertEntity},
104 {"accum", G_ScriptAction_Accum},
105 {"missionfailed", G_ScriptAction_MissionFailed},
106 {"missionsuccess", G_ScriptAction_MissionSuccess},
107 {"print", G_ScriptAction_Print},
108 {"faceangles", G_ScriptAction_FaceAngles},
109 {"resetscript", G_ScriptAction_ResetScript},
110 {"attachtotag", G_ScriptAction_TagConnect},
111 {"halt", G_ScriptAction_Halt},
112 {"stopsound", G_ScriptAction_StopSound},
113 {"startcam", G_ScriptAction_StartCam},
114 {"startcamblack", G_ScriptAction_StartCamBlack},
115 {"entityscriptname", G_ScriptAction_EntityScriptName},
116 {"aiscriptname", G_ScriptAction_AIScriptName},
117 // DHM - Nerve :: multiplayer scripting commands start with "wm_" (Wolf Multiplayer)
118 {"wm_mapdescription", G_ScriptAction_MapDescription},
119 {"wm_axis_respawntime", G_ScriptAction_AxisRespawntime},
120 {"wm_allied_respawntime", G_ScriptAction_AlliedRespawntime},
121 {"wm_number_of_objectives", G_ScriptAction_NumberofObjectives},
122 {"wm_objective_axis_desc", G_ScriptAction_ObjectiveAxisDesc},
123 {"wm_objective_allied_desc",G_ScriptAction_ObjectiveAlliedDesc},
124 {"wm_setwinner", G_ScriptAction_SetWinner},
125 {"wm_set_objective_status", G_ScriptAction_SetObjectiveStatus},
126 {"wm_announce", G_ScriptAction_Announce},
127 {"wm_endround", G_ScriptAction_EndRound},
128 {"wm_set_round_timelimit", G_ScriptAction_SetRoundTimelimit},
129 // dhm
130 {"backupscript", G_ScriptAction_BackupScript},
131 {"restorescript", G_ScriptAction_RestoreScript},
132 {"sethealth", G_ScriptAction_SetHealth},
133
134 {"mu_start", G_ScriptAction_MusicStart}, // (char *new_music, int time) // time to fadeup
135 {"mu_play", G_ScriptAction_MusicPlay}, // (char *music)
136 {"mu_stop", G_ScriptAction_MusicStop}, // (int time) // time to fadeout
137 {"mu_fade", G_ScriptAction_MusicFade}, // (float target_volume, int time) // time to fade to target
138 {"mu_queue", G_ScriptAction_MusicQueue}, // (char *new_music) // music that will start when previous fades to 0
139
140 {NULL, 0}
141 };
142
143 qboolean G_Script_EventMatch_StringEqual( g_script_event_t *event, char *eventParm );
144 qboolean G_Script_EventMatch_IntInRange( g_script_event_t *event, char *eventParm );
145
146 // the list of events that can start an action sequence
147 g_script_event_define_t gScriptEvents[] =
148 {
149 {"spawn", 0}, // called as each character is spawned into the game
150 {"trigger", G_Script_EventMatch_StringEqual}, // something has triggered us (always followed by an identifier)
151 {"pain", G_Script_EventMatch_IntInRange}, // we've been hurt
152 {"death", 0}, // RIP
153 {"activate", G_Script_EventMatch_StringEqual}, // something has triggered us (always followed by an identifier)
154 {"stopcam", 0},
155
156 {NULL, 0}
157 };
158
159
160 /*
161 ===============
162 G_Script_EventMatch_StringEqual
163 ===============
164 */
G_Script_EventMatch_StringEqual(g_script_event_t * event,char * eventParm)165 qboolean G_Script_EventMatch_StringEqual( g_script_event_t *event, char *eventParm ) {
166 if ( eventParm && !Q_strcasecmp( event->params, eventParm ) ) {
167 return qtrue;
168 } else {
169 return qfalse;
170 }
171 }
172
173 /*
174 ===============
175 G_Script_EventMatch_IntInRange
176 ===============
177 */
G_Script_EventMatch_IntInRange(g_script_event_t * event,char * eventParm)178 qboolean G_Script_EventMatch_IntInRange( g_script_event_t *event, char *eventParm ) {
179 char *pString, *token;
180 int int1, int2, eInt;
181
182 // get the cast name
183 pString = eventParm;
184 token = COM_ParseExt( &pString, qfalse );
185 int1 = atoi( token );
186 token = COM_ParseExt( &pString, qfalse );
187 int2 = atoi( token );
188
189 eInt = atoi( event->params );
190
191 if ( eventParm && eInt > int1 && eInt <= int2 ) {
192 return qtrue;
193 } else {
194 return qfalse;
195 }
196 }
197
198 /*
199 ===============
200 G_Script_EventForString
201 ===============
202 */
G_Script_EventForString(char * string)203 int G_Script_EventForString( char *string ) {
204 int i;
205
206 for ( i = 0; gScriptEvents[i].eventStr; i++ )
207 {
208 if ( !Q_strcasecmp( string, gScriptEvents[i].eventStr ) ) {
209 return i;
210 }
211 }
212
213 return -1;
214 }
215
216 /*
217 ===============
218 G_Script_ActionForString
219 ===============
220 */
G_Script_ActionForString(char * string)221 g_script_stack_action_t *G_Script_ActionForString( char *string ) {
222 int i;
223
224 for ( i = 0; gScriptActions[i].actionString; i++ )
225 {
226 if ( !Q_strcasecmp( string, gScriptActions[i].actionString ) ) {
227 if ( !Q_strcasecmp( string, "foundsecret" ) ) {
228 level.numSecrets++;
229 G_SendMissionStats();
230 }
231 return &gScriptActions[i];
232 }
233 }
234
235 return NULL;
236 }
237
238 /*
239 =============
240 G_Script_ScriptLoad
241
242 Loads the script for the current level into the buffer
243 =============
244 */
G_Script_ScriptLoad(void)245 void G_Script_ScriptLoad( void ) {
246 char filename[MAX_QPATH];
247 vmCvar_t mapname;
248 fileHandle_t f;
249 int len;
250
251 trap_Cvar_Register( &g_scriptDebug, "g_scriptDebug", "0", 0 );
252
253 level.scriptEntity = NULL;
254
255 trap_Cvar_VariableStringBuffer( "g_scriptName", filename, sizeof( filename ) );
256 if ( strlen( filename ) > 0 ) {
257 trap_Cvar_Register( &mapname, "g_scriptName", "", CVAR_ROM );
258 } else {
259 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
260 }
261 Q_strncpyz( filename, "maps/", sizeof( filename ) );
262 Q_strcat( filename, sizeof( filename ), mapname.string );
263 Q_strcat( filename, sizeof( filename ), ".script" );
264
265 len = trap_FS_FOpenFile( filename, &f, FS_READ );
266
267 // make sure we clear out the temporary scriptname
268 trap_Cvar_Set( "g_scriptName", "" );
269
270 if ( len < 0 ) {
271 return;
272 }
273
274 level.scriptEntity = G_Alloc( len );
275 trap_FS_Read( level.scriptEntity, len, f );
276
277 trap_FS_FCloseFile( f );
278 }
279
280 /*
281 ==============
282 G_Script_ScriptParse
283
284 Parses the script for the given entity
285 ==============
286 */
287 #define MAX_SCRIPT_EVENTS 64
288 g_script_event_t g_temp_events[MAX_SCRIPT_EVENTS];
G_Script_ScriptParse(gentity_t * ent)289 void G_Script_ScriptParse( gentity_t *ent ) {
290 char *pScript;
291 char *token;
292 qboolean wantName;
293 qboolean inScript;
294 int eventNum;
295 int numEventItems;
296 g_script_event_t *curEvent;
297 // DHM - Nerve :: Some of our multiplayer script commands have longer parameters
298 //char params[MAX_QPATH];
299 char params[MAX_INFO_STRING];
300 // dhm - end
301 g_script_stack_action_t *action;
302 int i;
303 int bracketLevel;
304 qboolean buildScript; //----(SA) added
305
306 if ( !ent->scriptName ) {
307 return;
308 }
309 if ( !level.scriptEntity ) {
310 return;
311 }
312
313 buildScript = qtrue;
314
315 pScript = level.scriptEntity;
316 wantName = qtrue;
317 inScript = qfalse;
318 COM_BeginParseSession( "G_Script_ScriptParse" );
319 bracketLevel = 0;
320 numEventItems = 0;
321
322 memset( g_temp_events, 0, sizeof( g_temp_events ) );
323
324 while ( 1 )
325 {
326 token = COM_Parse( &pScript );
327
328 if ( !token[0] ) {
329 if ( !wantName ) {
330 G_Error( "G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
331 }
332 break;
333 }
334
335 // end of script
336 if ( token[0] == '}' ) {
337 if ( inScript ) {
338 break;
339 }
340 if ( wantName ) {
341 G_Error( "G_Script_ScriptParse(), Error (line %d): '}' found, but not expected.\n", COM_GetCurrentParseLine() );
342 }
343 wantName = qtrue;
344 } else if ( token[0] == '{' ) {
345 if ( wantName ) {
346 G_Error( "G_Script_ScriptParse(), Error (line %d): '{' found, NAME expected.\n", COM_GetCurrentParseLine() );
347 }
348 } else if ( wantName ) {
349 if ( !Q_strcasecmp( ent->scriptName, token ) ) {
350 inScript = qtrue;
351 numEventItems = 0;
352 }
353 wantName = qfalse;
354 } else if ( inScript ) {
355 //if ( !Q_strcasecmp( token, "attributes" ) ) {
356 // // read in all the attributes
357 // G_Script_CheckLevelAttributes( cs, ent, &pScript );
358 // continue;
359 //}
360 eventNum = G_Script_EventForString( token );
361 if ( eventNum < 0 ) {
362 G_Error( "G_Script_ScriptParse(), Error (line %d): unknown event: %s.\n", COM_GetCurrentParseLine(), token );
363 }
364 if ( numEventItems >= MAX_SCRIPT_EVENTS ) {
365 G_Error( "G_Script_ScriptParse(), Error (line %d): MAX_SCRIPT_EVENTS reached (%d)\n", COM_GetCurrentParseLine(), MAX_SCRIPT_EVENTS );
366 }
367
368 curEvent = &g_temp_events[numEventItems];
369 curEvent->eventNum = eventNum;
370 memset( params, 0, sizeof( params ) );
371
372 // parse any event params before the start of this event's actions
373 while ( ( token = COM_Parse( &pScript ) ) && ( token[0] != '{' ) )
374 {
375 if ( !token[0] ) {
376 G_Error( "G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
377 }
378
379 if ( strlen( params ) ) { // add a space between each param
380 Q_strcat( params, sizeof( params ), " " );
381 }
382 Q_strcat( params, sizeof( params ), token );
383 }
384
385 if ( strlen( params ) ) { // copy the params into the event
386 curEvent->params = G_Alloc( strlen( params ) + 1 );
387 Q_strncpyz( curEvent->params, params, strlen( params ) + 1 );
388 }
389
390 // parse the actions for this event
391 while ( ( token = COM_Parse( &pScript ) ) && ( token[0] != '}' ) )
392 {
393 if ( !token[0] ) {
394 G_Error( "G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
395 }
396
397 action = G_Script_ActionForString( token );
398 if ( !action ) {
399 G_Error( "G_Script_ScriptParse(), Error (line %d): unknown action: %s.\n", COM_GetCurrentParseLine(), token );
400 }
401
402 curEvent->stack.items[curEvent->stack.numItems].action = action;
403
404 memset( params, 0, sizeof( params ) );
405 token = COM_ParseExt( &pScript, qfalse );
406 for ( i = 0; token[0]; i++ )
407 {
408 if ( strlen( params ) ) { // add a space between each param
409 Q_strcat( params, sizeof( params ), " " );
410 }
411
412 if ( i == 0 ) {
413 // Special case: playsound's need to be cached on startup to prevent in-game pauses
414 if ( !Q_stricmp( action->actionString, "playsound" ) ) {
415 G_SoundIndex( token );
416 }
417
418 //----(SA) added a bit more
419 if ( buildScript && (
420 !Q_stricmp( action->actionString, "mu_start" ) ||
421 !Q_stricmp( action->actionString, "mu_play" ) ||
422 !Q_stricmp( action->actionString, "mu_queue" ) ||
423 !Q_stricmp( action->actionString, "startcam" ) ||
424 !Q_stricmp( action->actionString, "startcamblack" ) )
425 ) {
426 if ( strlen( token ) ) { // we know there's a [0], but don't know if it's '0'
427 trap_SendServerCommand( ent->s.number, va( "addToBuild %s\n", token ) );
428 }
429 }
430 }
431 //----(SA) end
432
433 if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word
434 Q_strcat( params, sizeof( params ), "\"" );
435 }
436
437 Q_strcat( params, sizeof( params ), token );
438
439 if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word
440 Q_strcat( params, sizeof( params ), "\"" );
441 }
442
443 token = COM_ParseExt( &pScript, qfalse );
444 }
445
446 if ( strlen( params ) ) { // copy the params into the event
447 curEvent->stack.items[curEvent->stack.numItems].params = G_Alloc( strlen( params ) + 1 );
448 Q_strncpyz( curEvent->stack.items[curEvent->stack.numItems].params, params, strlen( params ) + 1 );
449 }
450
451 curEvent->stack.numItems++;
452
453 if ( curEvent->stack.numItems >= G_MAX_SCRIPT_STACK_ITEMS ) {
454 G_Error( "G_Script_ScriptParse(): script exceeded MAX_SCRIPT_ITEMS (%d), line %d\n", G_MAX_SCRIPT_STACK_ITEMS, COM_GetCurrentParseLine() );
455 }
456 }
457
458 numEventItems++;
459 } else // skip this character completely
460 {
461 // TTimo: gcc: suggest parentheses around assignment used as truth value
462 while ( ( token = COM_Parse( &pScript ) ) )
463 {
464 if ( !token[0] ) {
465 G_Error( "G_Script_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", COM_GetCurrentParseLine() );
466 } else if ( token[0] == '{' ) {
467 bracketLevel++;
468 } else if ( token[0] == '}' ) {
469 if ( !--bracketLevel ) {
470 break;
471 }
472 }
473 }
474 }
475 }
476
477 // alloc and copy the events into the gentity_t for this cast
478 if ( numEventItems > 0 ) {
479 ent->scriptEvents = G_Alloc( sizeof( g_script_event_t ) * numEventItems );
480 memcpy( ent->scriptEvents, g_temp_events, sizeof( g_script_event_t ) * numEventItems );
481 ent->numScriptEvents = numEventItems;
482 }
483 }
484
485 /*
486 ================
487 G_Script_ScriptChange
488 ================
489 */
490 qboolean G_Script_ScriptRun( gentity_t *ent );
G_Script_ScriptChange(gentity_t * ent,int newScriptNum)491 void G_Script_ScriptChange( gentity_t *ent, int newScriptNum ) {
492 g_script_status_t scriptStatusBackup;
493
494 // backup the current scripting
495 memcpy( &scriptStatusBackup, &ent->scriptStatus, sizeof( g_script_status_t ) );
496
497 // set the new script to this cast, and reset script status
498 ent->scriptStatus.scriptEventIndex = newScriptNum;
499 ent->scriptStatus.scriptStackHead = 0;
500 ent->scriptStatus.scriptStackChangeTime = level.time;
501 ent->scriptStatus.scriptId = scriptStatusBackup.scriptId + 1;
502
503 // try and run the script, if it doesn't finish, then abort the current script (discard backup)
504 if ( G_Script_ScriptRun( ent ) ) {
505 // completed successfully
506 memcpy( &ent->scriptStatus, &scriptStatusBackup, sizeof( g_script_status_t ) );
507 }
508 }
509
510 /*
511 ================
512 G_Script_ScriptEvent
513
514 An event has occured, for which a script may exist
515 ================
516 */
G_Script_ScriptEvent(gentity_t * ent,char * eventStr,char * params)517 void G_Script_ScriptEvent( gentity_t *ent, char *eventStr, char *params ) {
518 int i, eventNum;
519
520 eventNum = -1;
521
522 // find out which event this is
523 for ( i = 0; gScriptEvents[i].eventStr; i++ )
524 {
525 if ( !Q_strcasecmp( eventStr, gScriptEvents[i].eventStr ) ) { // match found
526 eventNum = i;
527 break;
528 }
529 }
530
531 if ( eventNum < 0 ) {
532 if ( g_cheats.integer ) { // dev mode
533 G_Printf( "devmode-> G_Script_ScriptEvent(), unknown event: %s\n", eventStr );
534 }
535 return;
536 }
537
538 // see if this entity has this event
539 for ( i = 0; i < ent->numScriptEvents; i++ )
540 {
541 if ( ent->scriptEvents[i].eventNum == eventNum ) {
542 if ( ( !ent->scriptEvents[i].params )
543 || ( !gScriptEvents[eventNum].eventMatch || gScriptEvents[eventNum].eventMatch( &ent->scriptEvents[i], params ) ) ) {
544 G_Script_ScriptChange( ent, i );
545 break;
546 }
547 }
548 }
549 }
550
551 /*
552 =============
553 G_Script_ScriptRun
554
555 returns qtrue if the script completed
556 =============
557 */
G_Script_ScriptRun(gentity_t * ent)558 qboolean G_Script_ScriptRun( gentity_t *ent ) {
559 g_script_stack_t *stack;
560
561 if ( saveGamePending ) {
562 return qfalse;
563 }
564
565 if ( strlen( g_missionStats.string ) > 1 ) {
566 return qfalse;
567 }
568
569 //if (!g_scripts.integer)
570 // return qtrue;
571
572 trap_Cvar_Update( &g_scriptDebug );
573
574 if ( !ent->scriptEvents ) {
575 ent->scriptStatus.scriptEventIndex = -1;
576 return qtrue;
577 }
578
579 // if we are still doing a gotomarker, process the movement
580 if ( ent->scriptStatus.scriptFlags & SCFL_GOING_TO_MARKER ) {
581 G_ScriptAction_GotoMarker( ent, NULL );
582 }
583
584 // if we are animating, do the animation
585 if ( ent->scriptStatus.scriptFlags & SCFL_ANIMATING ) {
586 G_ScriptAction_PlayAnim( ent, ent->scriptStatus.animatingParams );
587 }
588
589 if ( ent->scriptStatus.scriptEventIndex < 0 ) {
590 return qtrue;
591 }
592
593 stack = &ent->scriptEvents[ent->scriptStatus.scriptEventIndex].stack;
594
595 if ( !stack->numItems ) {
596 ent->scriptStatus.scriptEventIndex = -1;
597 return qtrue;
598 }
599 //
600 // show debugging info
601 if ( g_scriptDebug.integer && ent->scriptStatus.scriptStackChangeTime == level.time ) {
602 if ( ent->scriptStatus.scriptStackHead < stack->numItems ) {
603 G_Printf( "%i : (%s) GScript command: %s %s\n", level.time, ent->scriptName, stack->items[ent->scriptStatus.scriptStackHead].action->actionString, ( stack->items[ent->scriptStatus.scriptStackHead].params ? stack->items[ent->scriptStatus.scriptStackHead].params : "" ) );
604 }
605 }
606 //
607 while ( ent->scriptStatus.scriptStackHead < stack->numItems )
608 {
609 if ( !stack->items[ent->scriptStatus.scriptStackHead].action->actionFunc( ent, stack->items[ent->scriptStatus.scriptStackHead].params ) ) {
610 return qfalse;
611 }
612 // move to the next action in the script
613 ent->scriptStatus.scriptStackHead++;
614 // record the time that this new item became active
615 ent->scriptStatus.scriptStackChangeTime = level.time;
616 //
617 // show debugging info
618 if ( g_scriptDebug.integer ) {
619 if ( ent->scriptStatus.scriptStackHead < stack->numItems ) {
620 G_Printf( "%i : (%s) GScript command: %s %s\n", level.time, ent->scriptName, stack->items[ent->scriptStatus.scriptStackHead].action->actionString, ( stack->items[ent->scriptStatus.scriptStackHead].params ? stack->items[ent->scriptStatus.scriptStackHead].params : "" ) );
621 }
622 }
623 }
624
625 ent->scriptStatus.scriptEventIndex = -1;
626
627 return qtrue;
628 }
629
630 //================================================================================
631 // Script Entities
632
script_linkentity(gentity_t * ent)633 void script_linkentity( gentity_t *ent ) {
634
635 // this is required since non-solid brushes need to be linked but not solid
636 trap_LinkEntity( ent );
637
638 // if ((ent->s.eType == ET_MOVER) && !(ent->spawnflags & 2)) {
639 // ent->s.solid = 0;
640 // }
641 }
642
script_mover_die(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)643 void script_mover_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
644 if ( self->spawnflags & 4 ) {
645 switch ( mod ) {
646 case MOD_GRENADE:
647 case MOD_GRENADE_SPLASH:
648 case MOD_ROCKET:
649 case MOD_ROCKET_SPLASH:
650 case MOD_AIRSTRIKE:
651 break;
652 default: // no death from this weapon
653 self->health += damage;
654 return;
655 }
656 }
657
658 G_Script_ScriptEvent( self, "death", "" );
659 self->die = 0;
660
661 trap_UnlinkEntity( self );
662 G_FreeEntity( self );
663 }
664
script_mover_pain(gentity_t * self,gentity_t * attacker,int damage,vec3_t point)665 void script_mover_pain( gentity_t *self, gentity_t *attacker, int damage, vec3_t point ) {
666 G_Script_ScriptEvent( self, "pain", va( "%d %d", self->health, self->health + damage ) );
667 }
668
script_mover_spawn(gentity_t * ent)669 void script_mover_spawn( gentity_t *ent ) {
670 if ( ent->spawnflags & 2 ) {
671 ent->clipmask = CONTENTS_SOLID;
672 ent->r.contents = CONTENTS_SOLID;
673 } else {
674 ent->s.eFlags |= EF_NONSOLID_BMODEL;
675 ent->clipmask = 0;
676 ent->r.contents = 0;
677 }
678 //ent->s.eType = ET_GENERAL;
679
680 //ent->s.modelindex = G_ModelIndex (ent->model);
681 //ent->s.frame = 0;
682
683 //VectorCopy( ent->s.origin, ent->s.pos.trBase );
684 //ent->s.pos.trType = TR_STATIONARY;
685
686 script_linkentity( ent );
687 }
688
script_mover_use(gentity_t * ent,gentity_t * other,gentity_t * activator)689 void script_mover_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
690 script_mover_spawn( ent );
691 }
692
script_mover_blocked(gentity_t * ent,gentity_t * other)693 void script_mover_blocked( gentity_t *ent, gentity_t *other ) {
694 // remove it, we must not stop for anything or it will screw up script timing
695 if ( !other->client ) {
696 G_TempEntity( other->s.origin, EV_ITEM_POP );
697 G_FreeEntity( other );
698 return;
699 }
700
701 // FIXME: we could have certain entities stop us, thereby "pausing" movement
702 // until they move out the way. then we can just call the GotoMarker() again,
703 // telling it that we are just now calling it for the first time, so it should
704 // start us on our way again (theoretically speaking).
705
706 // kill them
707 G_Damage( other, ent, ent, NULL, NULL, 9999, 0, MOD_CRUSH );
708 }
709
710 /*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY
711 Scripted brush entity. A simplified means of moving brushes around based on events.
712
713 "modelscale" - Scale multiplier (defaults to 1, and scales uniformly)
714 "modelscale_vec" - Set scale per-axis. Overrides "modelscale", so if you have both the "modelscale" is ignored
715 "model2" optional md3 to draw over the solid clip brush
716 "scriptname" name used for scripting purposes (like aiName in AI scripting)
717 "health" optionally make this entity damagable
718 */
SP_script_mover(gentity_t * ent)719 void SP_script_mover( gentity_t *ent ) {
720
721 float scale[3] = {1,1,1};
722 vec3_t scalevec;
723
724 if ( !ent->model ) {
725 G_Error( "script_model_med must have a \"model\"\n" );
726 }
727 if ( !ent->scriptName ) {
728 G_Error( "script_model_med must have a \"scriptname\"\n" );
729 }
730
731 ent->blocked = script_mover_blocked;
732
733 // first position at start
734 VectorCopy( ent->s.origin, ent->pos1 );
735
736 // VectorCopy( ent->r.currentOrigin, ent->pos1 );
737 VectorCopy( ent->pos1, ent->pos2 ); // don't go anywhere just yet
738
739 trap_SetBrushModel( ent, ent->model );
740
741 InitMover( ent );
742 ent->reached = 0;
743
744 if ( ent->spawnflags & 1 ) {
745 ent->use = script_mover_use;
746 trap_UnlinkEntity( ent ); // make sure it's not visible
747 return;
748 }
749
750 G_SetAngle( ent, ent->s.angles );
751
752 G_SpawnInt( "health", "0", &ent->health );
753 if ( ent->health ) {
754 ent->takedamage = qtrue;
755 }
756
757 ent->die = script_mover_die;
758 ent->pain = script_mover_pain;
759
760 // look for general scaling
761 if ( G_SpawnFloat( "modelscale", "1", &scale[0] ) ) {
762 scale[2] = scale[1] = scale[0];
763 }
764
765 // look for axis specific scaling
766 if ( G_SpawnVector( "modelscale_vec", "1 1 1", &scalevec[0] ) ) {
767 VectorCopy( scalevec, scale );
768 }
769
770 if ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) {
771 // ent->s.eType = ET_MOVERSCALED;
772 ent->s.density = ET_MOVERSCALED;
773 // scale is stored in 'angles2'
774 VectorCopy( scale, ent->s.angles2 );
775 }
776
777 script_mover_spawn( ent );
778 }
779
780 //..............................................................................
781
script_model_med_spawn(gentity_t * ent)782 void script_model_med_spawn( gentity_t *ent ) {
783 if ( ent->spawnflags & 2 ) {
784 ent->clipmask = CONTENTS_SOLID;
785 ent->r.contents = CONTENTS_SOLID;
786 }
787 ent->s.eType = ET_GENERAL;
788
789 ent->s.modelindex = G_ModelIndex( ent->model );
790 ent->s.frame = 0;
791
792 VectorCopy( ent->s.origin, ent->s.pos.trBase );
793 ent->s.pos.trType = TR_STATIONARY;
794
795 trap_LinkEntity( ent );
796 }
797
script_model_med_use(gentity_t * ent,gentity_t * other,gentity_t * activator)798 void script_model_med_use( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
799 script_model_med_spawn( ent );
800 }
801
802 /*QUAKED script_model_med (0.5 0.25 1.0) (-16 -16 -24) (16 16 64) TriggerSpawn Solid
803 MEDIUM SIZED scripted entity, used for animating a model, moving it around, etc
804 SOLID spawnflag means this entity will clip the player and AI, otherwise they can walk
805 straight through it
806 "model" the full path of the model to use
807 "scriptname" name used for scripting purposes (like aiName in AI scripting)
808 */
SP_script_model_med(gentity_t * ent)809 void SP_script_model_med( gentity_t *ent ) {
810 if ( !ent->model ) {
811 G_Error( "script_model_med %s must have a \"model\"\n", ent->scriptName );
812 }
813 if ( !ent->scriptName ) {
814 G_Error( "script_model_med must have a \"scriptname\"\n" );
815 }
816
817 ent->s.eType = ET_GENERAL;
818 ent->s.apos.trType = TR_STATIONARY;
819 ent->s.apos.trTime = 0;
820 ent->s.apos.trDuration = 0;
821 VectorCopy( ent->s.angles, ent->s.apos.trBase );
822 VectorClear( ent->s.apos.trDelta );
823
824 if ( ent->spawnflags & 1 ) {
825 ent->use = script_model_med_use;
826 trap_UnlinkEntity( ent ); // make sure it's not visible
827 return;
828 }
829
830 script_model_med_spawn( ent );
831 }
832
833 //..............................................................................
834
835 /*QUAKED script_camera (1.0 0.25 1.0) (-8 -8 -8) (8 8 8) TriggerSpawn
836
837 This is a camera entity. Used by the scripting to show cinematics, via special
838 camera commands. See scripting documentation.
839
840 "scriptname" name used for scripting purposes (like aiName in AI scripting)
841 */
SP_script_camera(gentity_t * ent)842 void SP_script_camera( gentity_t *ent ) {
843 if ( !ent->scriptName ) {
844 G_Error( "%s must have a \"scriptname\"\n", ent->classname );
845 }
846
847 ent->s.eType = ET_CAMERA;
848 ent->s.apos.trType = TR_STATIONARY;
849 ent->s.apos.trTime = 0;
850 ent->s.apos.trDuration = 0;
851 VectorCopy( ent->s.angles, ent->s.apos.trBase );
852 VectorClear( ent->s.apos.trDelta );
853
854 ent->s.frame = 0;
855
856 ent->r.svFlags |= SVF_NOCLIENT; // only broadcast when in use
857 }
858
859
860 //..DHM-Nerve..................................................................
861
862 /*QUAKED script_multiplayer (1.0 0.25 1.0) (-8 -8 -8) (8 8 8)
863
864 This is used to script multiplayer maps. Entity not displayed in game.
865
866 "scriptname" name used for scripting purposes (REQUIRED)
867 */
SP_script_multiplayer(gentity_t * ent)868 void SP_script_multiplayer( gentity_t *ent ) {
869 if ( !ent->scriptName ) {
870 G_Error( "%s must have a \"scriptname\"\n", ent->classname );
871 }
872
873 ent->s.eType = ET_INVISIBLE;
874
875 ent->r.svFlags |= SVF_NOCLIENT; // only broadcast when in use
876 }
877
878 // dhm
879