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