3915 lines
85 KiB
C++
3915 lines
85 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Quake 2 Engine/Sin/code/game/scriptmaster.cpp $
|
|
// $Revision:: 158 $
|
|
// $Author:: Markd $
|
|
// $Date:: 10/28/99 10:43a $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
// $Log:: /Quake 2 Engine/Sin/code/game/scriptmaster.cpp $
|
|
//
|
|
// 158 10/28/99 10:43a Markd
|
|
// made a bunch of console commands cheat protected
|
|
//
|
|
// 157 5/19/99 11:30a Markd
|
|
// Added new camera support
|
|
//
|
|
// 156 2/16/99 8:38p Jimdose
|
|
// moved sin arcade comm stuff to client
|
|
//
|
|
// 155 12/18/98 11:03p Jimdose
|
|
// removed include of qcommon.h
|
|
//
|
|
// 154 12/16/98 12:42a Aldie
|
|
// Added endgame command for Sin Arcade
|
|
//
|
|
// 153 12/14/98 6:52p Aldie
|
|
// Added mapname command
|
|
//
|
|
// 152 11/11/98 10:55p Jimdose
|
|
// Added RemoveEnt, RemoveClass, KillEnt, KillClass
|
|
//
|
|
// 151 11/10/98 4:34p Jimdose
|
|
// added ClearSaveGames
|
|
//
|
|
// 150 11/07/98 10:15p Markd
|
|
// put in forcemusic support
|
|
//
|
|
// 149 11/07/98 10:05p Jimdose
|
|
// made spawn work with model names
|
|
//
|
|
// 148 11/06/98 5:17p Jimdose
|
|
// added missionfailed and missionfailedtime to level vars
|
|
// when a mission has failed or the player is dead in single player, the game
|
|
// code immediately shows the loadmenu, preventing them from letting the game
|
|
// continue running if they exit the menu
|
|
//
|
|
// 147 11/05/98 2:17a Aldie
|
|
// Added a check for the world to cacheplayermodel
|
|
//
|
|
// 146 11/04/98 9:50p Aldie
|
|
// Added ability to force cache a player model
|
|
//
|
|
// 145 10/27/98 9:46p Aldie
|
|
// Changed training cvar to level.training
|
|
//
|
|
// 144 10/27/98 9:21p Markd
|
|
// fixed dialogsound bug
|
|
//
|
|
// 143 10/27/98 8:07p Markd
|
|
// Added dialogsound command
|
|
//
|
|
// 142 10/26/98 9:06p Aldie
|
|
// Added stuffcommand
|
|
//
|
|
// 141 10/26/98 5:15p Jimdose
|
|
// changed recalcpaths to passtopathmanager
|
|
//
|
|
// 140 10/26/98 4:42p Jimdose
|
|
// added recalcpaths
|
|
//
|
|
// 139 10/26/98 2:16p Aldie
|
|
// Added AirClamp
|
|
//
|
|
// 138 10/26/98 12:42a Markd
|
|
// added new print commands and crucial dialog
|
|
//
|
|
// 137 10/26/98 12:05a Jimdose
|
|
// added MissionFailed
|
|
//
|
|
// 136 10/25/98 6:26p Markd
|
|
// Added in no_jc abililty
|
|
//
|
|
// 135 10/24/98 9:38p Jimdose
|
|
// Added GetUniqueThreadNumber
|
|
// Made TerminateThread not kill actor threads or thread 0 (an invalid thread)
|
|
//
|
|
// 134 10/24/98 12:42a Markd
|
|
// changed origins to worldorigins where appropriate
|
|
//
|
|
// 133 10/22/98 5:44p Jimdose
|
|
// MapEvent now sets doneProcessing to true
|
|
//
|
|
// 132 10/22/98 4:32p Aldie
|
|
// Added a clear screen print file command
|
|
//
|
|
// 131 10/19/98 11:51p Aldie
|
|
// Added fixslashes to screen print file
|
|
//
|
|
// 130 10/19/98 12:07a Jimdose
|
|
// made all code use fast checks for inheritance (no text lookups when
|
|
// possible)
|
|
// isSubclassOf no longer requires ::_classinfo()
|
|
//
|
|
// 129 10/18/98 7:59p Aldie
|
|
// Added screenprintfile
|
|
//
|
|
// 128 10/14/98 12:12a Aldie
|
|
// Added a bunch of intermission commands
|
|
//
|
|
// 127 10/12/98 9:07p Markd
|
|
// Made it so playermodels are not cached if not in deathmatch
|
|
//
|
|
// 126 10/08/98 3:44p Markd
|
|
// re-merged my changes back into the checked in file
|
|
//
|
|
// 125 10/08/98 12:02a Jimdose
|
|
// made GameTime a ScriptVariablePtr
|
|
// moved PlayerFrozen to game
|
|
// got rid of type in gamescript
|
|
//
|
|
// 124 10/01/98 8:00p Markd
|
|
// Made dialog use NO_PHS
|
|
//
|
|
// 123 9/26/98 7:17p Markd
|
|
// Made WaitFor support more than one object with the same target name
|
|
//
|
|
// 122 9/25/98 4:36p Markd
|
|
// Replaced Event::Find with Event::Exists
|
|
//
|
|
// 121 9/23/98 11:00p Markd
|
|
// put in some garbage collection on stuff that wasn't freed up
|
|
//
|
|
// 120 9/22/98 4:23p Markd
|
|
// Fixed some targetname stuff for lights, doors and scriptobjects
|
|
//
|
|
// 119 9/19/98 6:13p Aldie
|
|
// Added printscreen command
|
|
//
|
|
// 118 9/15/98 7:43p Markd
|
|
// fixed some typos
|
|
//
|
|
// 117 9/15/98 7:35p Markd
|
|
// Moved music and soundtrack stuff to specialfx
|
|
//
|
|
// 116 9/15/98 6:46p Aldie
|
|
// Clear the waitfor's before deleting threads
|
|
//
|
|
// 115 9/10/98 8:34p Markd
|
|
// removed drawoverlay and drawstats variables from level structure
|
|
//
|
|
// 114 9/09/98 3:56p Aldie
|
|
// Changed precision of fade floats
|
|
//
|
|
// 113 9/02/98 7:47p Aldie
|
|
// Added cacheplayermodel function
|
|
//
|
|
// 112 8/29/98 9:45p Jimdose
|
|
// Changed BeginIntermission to G_BeginIntermission
|
|
//
|
|
// 111 8/29/98 7:22p Aldie
|
|
// Updated the spawn function
|
|
//
|
|
// 110 8/29/98 5:27p Markd
|
|
// added specialfx, replaced misc with specialfx where appropriate
|
|
//
|
|
// 109 8/26/98 6:43p Jimdose
|
|
// fixed bug in Restore
|
|
//
|
|
// 108 8/24/98 11:32a Markd
|
|
// Added Start method to threads, repladed all ProcessEvent(
|
|
// EV_ScriptThread_execute) with thread->Start( -1 )
|
|
//
|
|
// 107 8/22/98 10:04p Markd
|
|
// added noprecache support for cachemodel and cachesound
|
|
//
|
|
// 106 8/22/98 6:34p Markd
|
|
// Added Terminate extened behavior
|
|
//
|
|
// 105 8/22/98 1:22a Markd
|
|
// Added WaitForPlayer support
|
|
//
|
|
// 104 8/21/98 5:26p Markd
|
|
// Added sv_precache and cl_precache
|
|
//
|
|
// 103 8/21/98 1:43a Markd
|
|
// Changed parameters to CreateThread
|
|
//
|
|
// 102 8/20/98 4:39p Markd
|
|
// Added print vector support
|
|
//
|
|
// 101 8/19/98 6:38p Markd
|
|
// Added noncinematic event
|
|
//
|
|
// 100 8/18/98 11:08p Markd
|
|
// Added new Alias System
|
|
//
|
|
// 99 8/17/98 7:31p Jimdose
|
|
// Added WaitingFor to test if a thread is waiting for the specified entity
|
|
//
|
|
// 98 8/17/98 4:34p Markd
|
|
// Added cinematic and skipthread events
|
|
//
|
|
// 97 8/15/98 11:18p Jimdose
|
|
// Restore was always starting the thread up.
|
|
//
|
|
// 96 8/15/98 5:31p Jimdose
|
|
// Added SetScript to ScriptThread for changing script files without using a
|
|
// Goto
|
|
//
|
|
// 95 8/14/98 9:09p Jimdose
|
|
// added waitUntil to keep track of wait times when marking and restoring (used
|
|
// when changing actor states)
|
|
//
|
|
// 94 8/14/98 8:14p Aldie
|
|
// Added generic overlay system
|
|
//
|
|
// 93 8/14/98 3:44p Jimdose
|
|
// WaitingFor variables are now all cleared after they're no longer valid
|
|
//
|
|
// 92 8/13/98 7:45p Jimdose
|
|
// EventEnd now sets doneprocessing to true when doing the callback for model
|
|
// scripts
|
|
//
|
|
// 91 8/13/98 7:29p Jimdose
|
|
// Added type to scriptthreads
|
|
//
|
|
// 90 8/13/98 5:00p Jimdose
|
|
// ProcessCommand checks if variables are empty before using them with
|
|
// commands.
|
|
//
|
|
// 89 8/12/98 6:43p Jimdose
|
|
// Changed TriggerEvent so that it doesn't add the entities that were triggered
|
|
// to the updateList. This would cause them to eroniously recieve doMove
|
|
// commands, overriding their real movethread information.
|
|
//
|
|
// 88 8/08/98 7:51p Jimdose
|
|
// changed realWorld to world
|
|
//
|
|
// 87 8/07/98 5:44p Jimdose
|
|
// Process command now uses isVariableCommand to check if a command should be
|
|
// sent to a variable as apposed to the object that variable refers to
|
|
//
|
|
// 86 8/06/98 10:49p Jimdose
|
|
// fixed index out of range bug in ProcessCommand
|
|
//
|
|
// 85 8/06/98 6:59p Jimdose
|
|
// Added GameTime
|
|
//
|
|
// 84 8/06/98 5:18p Jimdose
|
|
// Added ProcessCommandFromEvent
|
|
//
|
|
// 83 7/31/98 8:09p Jimdose
|
|
// Script commands now include flags to indicate cheats and console commands
|
|
//
|
|
// 82 7/26/98 12:12a Markd
|
|
// Added precache ability to dialog stuff
|
|
//
|
|
// 81 7/25/98 4:37p Aldie
|
|
// Menu controls
|
|
//
|
|
// 80 7/24/98 6:18p Aldie
|
|
// enable and disable huds
|
|
//
|
|
// 79 7/24/98 4:52p Jimdose
|
|
// Added setcvar command to script
|
|
//
|
|
// 78 7/24/98 2:11p Markd
|
|
// Changed dialog from VOICE to DIALOG
|
|
//
|
|
// 77 7/21/98 9:06p Markd
|
|
// Added music and soundtrack events
|
|
//
|
|
// 76 7/21/98 4:14p Jimdose
|
|
// waitFor now adds the object to the update list so that if no commands have
|
|
// been issued, the object will give a moveDone right away
|
|
// Made DoMove post the EV_ProcessCommands event so that any callbacks to the
|
|
// thread don't clear the updateList
|
|
//
|
|
// 75 7/20/98 5:48p Jimdose
|
|
// Added map command for changing levels
|
|
//
|
|
// 74 7/18/98 7:36p Aldie
|
|
// Added support for fades
|
|
//
|
|
// 73 7/16/98 5:18p Aldie
|
|
// Fixed error message bug.
|
|
//
|
|
// 72 7/14/98 11:51p Jimdose
|
|
// EventEnd only notifies the owning model if the event comes from script
|
|
//
|
|
// 71 7/11/98 9:24p Markd
|
|
// Forgot to add SetDialogScript event to ScriptThread
|
|
//
|
|
// 70 7/11/98 8:43p Markd
|
|
// Added SetDialogScript, added optional label support to CreateThread
|
|
//
|
|
// 69 7/11/98 2:49p Markd
|
|
// Added dialog event
|
|
//
|
|
// 68 7/09/98 1:39a Jimdose
|
|
// updateList is now cleared everytime we execute the script to insure that it
|
|
// always only contains THIS frame's objects
|
|
//
|
|
// 67 7/07/98 11:36p Jimdose
|
|
// Added error for unsupported global commands
|
|
// made end command signal self if model script
|
|
// added ThreadMarker
|
|
//
|
|
// 66 7/03/98 4:10p Jimdose
|
|
// Added assert and break
|
|
//
|
|
// 65 6/27/98 9:48p Jimdose
|
|
// Fixed a bug with TriggerEvent
|
|
//
|
|
// 64 6/27/98 7:03p Markd
|
|
// Added WaitForSound
|
|
//
|
|
// 63 6/25/98 8:14p Jimdose
|
|
// Made waitFor use *-entnum as well as targetname
|
|
//
|
|
// 62 6/24/98 6:47p Jimdose
|
|
// Made entity number based commands use "*" prefix consistantly
|
|
//
|
|
// 61 6/18/98 8:47p Jimdose
|
|
// Added better event error handling
|
|
// Added source info to events
|
|
//
|
|
// 60 6/17/98 3:03p Markd
|
|
// Changed NumArgs back to previous behavior
|
|
//
|
|
// 59 6/10/98 9:38p Jimdose
|
|
// Since we changed waitingFor to an str, ObjectMoveDone has to check if the
|
|
// length of waitingFor is > 0 before comaring it to the object's targetname,
|
|
// otherwise, calls to this function can cause threads that aren't waiting for
|
|
// objects to continue.
|
|
//
|
|
// 58 6/10/98 7:53p Markd
|
|
// Made NumArgs behave correctly like argc
|
|
//
|
|
// 57 6/10/98 6:08p Jimdose
|
|
// Made ProcessCommand add the thread to * commands
|
|
//
|
|
// 56 6/09/98 4:22p Jimdose
|
|
// Added multi-file scripting
|
|
// each script now has its own set of labels that are only processed once
|
|
//
|
|
// 55 6/04/98 11:07a Markd
|
|
// Made sure to initialize ScriptMaster when CloseScript is called
|
|
//
|
|
// 54 5/27/98 5:03a Aldie
|
|
// Added warning for ent not found in * commands
|
|
//
|
|
// 53 5/26/98 11:59p Markd
|
|
// put label support into ScriptMaster
|
|
//
|
|
// 52 5/26/98 9:25p Aldie
|
|
// Added spawn command
|
|
//
|
|
// 51 5/26/98 7:55p Jimdose
|
|
// added scripted cameras
|
|
//
|
|
// 50 5/26/98 4:46p Aldie
|
|
// Added player commands
|
|
//
|
|
// 49 5/26/98 4:22p Markd
|
|
// Added TargetList stuff and sped up SendToSlaves function
|
|
//
|
|
// 48 5/25/98 1:39p Aldie
|
|
// Added create console user
|
|
//
|
|
// 47 5/24/98 8:46p Jimdose
|
|
// Made a lot of functions more str-friendly.
|
|
// Got rid of a lot of char * based strings
|
|
// Cleaned up get spawn arg functions and sound functions
|
|
// sound functions now use consistant syntax
|
|
//
|
|
// 46 5/24/98 4:48p Jimdose
|
|
// Made char *'s const
|
|
//
|
|
// 45 5/22/98 10:57a Markd
|
|
// Put in label support parsing
|
|
//
|
|
// 44 5/20/98 10:00p Jimdose
|
|
// Rewrote KillThread so that it didn't remove the thread from the list before
|
|
// calling the destructor, thus causing assertion failures
|
|
//
|
|
// 43 5/20/98 5:30p Jimdose
|
|
// Added threadDying to prevent threads from executing after an "end" was
|
|
// reached. There was a bug where a thread had a callback on the same frame
|
|
// that it was killed that caused it to execute code from another thread.
|
|
//
|
|
// 42 5/20/98 4:31p Markd
|
|
// bullet-proofed thread code a bit
|
|
//
|
|
// 41 5/19/98 4:24p Aldie
|
|
// Added DeathCallback and WaitForDeath
|
|
//
|
|
// 40 5/13/98 4:57p Jimdose
|
|
// EventEnd now posts remove 0 in the future
|
|
//
|
|
// 39 5/11/98 5:53p Markd
|
|
// Added aliascache command
|
|
//
|
|
// 38 5/08/98 2:56p Markd
|
|
// Added alias event
|
|
//
|
|
// 37 5/05/98 3:46p Aldie
|
|
// Uncommented surfaceManager stuff.
|
|
//
|
|
// 36 5/04/98 8:31p Markd
|
|
// Added setlightstyle, cachemodel and cachesound
|
|
//
|
|
// 35 5/04/98 8:11p Aldie
|
|
// Updated surfaceManager name
|
|
//
|
|
// 34 5/02/98 8:47p Markd
|
|
// Added server and client event prefixes also modified CreateThread stuff
|
|
//
|
|
// 33 5/01/98 5:05p Jimdose
|
|
// Added TriggerEvent
|
|
//
|
|
// 32 4/03/98 1:10p Aldie
|
|
// Added waitForVariable
|
|
//
|
|
// 31 3/28/98 4:35p Jimdose
|
|
// Disabled assertion in threadCallback
|
|
//
|
|
// 30 3/28/98 3:28p Aldie
|
|
// Fixed some console input problems.
|
|
//
|
|
// 29 3/27/98 6:46p Jimdose
|
|
// Made EventEnd notify waiting threads that it was ending
|
|
//
|
|
// 28 3/27/98 6:23p Aldie
|
|
// Added some better console input stuff.
|
|
//
|
|
// 27 3/26/98 7:15p Aldie
|
|
// Added console type vars.
|
|
//
|
|
// 26 3/26/98 2:52p Aldie
|
|
// Added interface between consoles and scripts.
|
|
//
|
|
// 25 3/24/98 8:54p Jimdose
|
|
// Added thread management script commands
|
|
//
|
|
// 24 3/24/98 4:59p Jimdose
|
|
// Fixed various bugs related to the new event system
|
|
//
|
|
// 23 3/23/98 1:45p Jimdose
|
|
// Fixed bug where GetThread was returning the wrong thread
|
|
//
|
|
// 22 3/23/98 1:32p Jimdose
|
|
// Revamped event and command system
|
|
// Separated threads from controlling object for better thread management
|
|
//
|
|
// 21 3/07/98 5:17p Aldie
|
|
// Fixed trans_mag and trans_angle.
|
|
//
|
|
// 20 3/02/98 8:49p Jimdose
|
|
// Changed the classid parameter of CLASS_DECLARATION to a quoted string so
|
|
// that you could have a NULL classid.
|
|
//
|
|
// 19 2/28/98 4:43p Aldie
|
|
// Added Matrix4TransformVector
|
|
//
|
|
// 18 2/25/98 2:24p Aldie
|
|
// Updated texinfo command system.
|
|
//
|
|
// 17 2/24/98 3:38p Aldie
|
|
// Added support for texinfo commands.
|
|
//
|
|
// 16 2/21/98 1:10p Jimdose
|
|
// Changed initThread so that it doesn't search for a label if a NULL pointer
|
|
// is passed in and added a parameter to determine if it should execute the
|
|
// code immediately.
|
|
//
|
|
// 15 2/18/98 8:05p Jimdose
|
|
// GetObject replaces getObjectFromScrip, getScriptObject, and FindEntity
|
|
// Commands are now sent to all entities with a specified targetname, rather
|
|
// than just the first one.
|
|
//
|
|
//
|
|
// 13 2/18/98 4:40p Jimdose
|
|
// Syntax for conditionals changed from "var condition value label" to "var
|
|
// condition value statement"
|
|
// Added check for non-numeric values in variables in GetFloat, GetDouble, and
|
|
// GetInt
|
|
//
|
|
// 12 2/17/98 7:08p Jimdose
|
|
// Rewrote script variable support. New support KICKS ASS. :)
|
|
//
|
|
// 11 2/06/98 5:38p Jimdose
|
|
// Added check for missing file in setScript
|
|
//
|
|
// 10 2/03/98 10:48a Jimdose
|
|
// Updated to work with Quake 2 engine
|
|
// Got rid of preprocessing stage.
|
|
//
|
|
// 8 11/20/97 9:02p Jimdose
|
|
// Changed Container class to use ints instead of void *
|
|
//
|
|
// 7 11/07/97 6:41p Jimdose
|
|
// Changed DoMove to remove entities from the updateList more efficiently.
|
|
// Added support for variables.
|
|
//
|
|
// 6 10/28/97 4:22p Jimdose
|
|
// Made work with any Entity instead of just ScriptSlaves.
|
|
// Moved all general entity commands into Entity. ScriptMaster now only
|
|
// handles script flow commands.
|
|
// updateList only contains the entities that received commands for the current
|
|
// frame and is then clear when processing for that iteration is done.
|
|
//
|
|
// 5 10/27/97 3:30p Jimdose
|
|
// Removed dependency on quakedef.h
|
|
//
|
|
// 4 10/06/97 6:36p Jimdose
|
|
// Added control over dynamic lights.
|
|
//
|
|
// 3 9/26/97 5:23p Jimdose
|
|
// Added standard Ritual headers
|
|
//
|
|
// DESCRIPTION:
|
|
// Script masters are invisible entities that are spawned at the begining of each
|
|
// map. They simple parse the script and send commands to the specified objects
|
|
// at the apropriate time. Using a combination of simple commands, very complex
|
|
// scripted events can occur.
|
|
//
|
|
|
|
#include "g_local.h"
|
|
#include "class.h"
|
|
#include "scriptmaster.h"
|
|
#include "container.h"
|
|
#include "scriptvariable.h"
|
|
#include "surface.h"
|
|
#include "console.h"
|
|
#include "misc.h"
|
|
#include "specialfx.h"
|
|
#include "worldspawn.h"
|
|
#include "player.h"
|
|
|
|
ScriptVariableList gameVars;
|
|
ScriptVariableList levelVars;
|
|
ScriptVariableList parmVars;
|
|
ScriptVariableList consoleVars;
|
|
static ScriptVariablePtr GameTime = NULL;
|
|
|
|
Event EV_ProcessCommands( "processCommands" );
|
|
Event EV_Script_NewOrders( "newOrders" );
|
|
Event EV_ScriptThread_Execute( "execute" );
|
|
Event EV_ScriptThread_Callback( "script_callback" );
|
|
Event EV_ScriptThread_ThreadCallback( "thread_callback" );
|
|
Event EV_ScriptThread_ConsoleCallback( "console_callback" );
|
|
Event EV_ScriptThread_VariableCallback( "variable_callback" );
|
|
Event EV_ScriptThread_DeathCallback( "death_callback" );
|
|
Event EV_ScriptThread_CreateThread( "thread" );
|
|
Event EV_ScriptThread_TerminateThread( "terminate" );
|
|
Event EV_ScriptThread_ControlObject( "control" );
|
|
Event EV_ScriptThread_Goto( "goto" );
|
|
Event EV_ScriptThread_Pause( "pause" );
|
|
Event EV_ScriptThread_Wait( "wait" );
|
|
Event EV_ScriptThread_WaitFor( "waitFor" );
|
|
Event EV_ScriptThread_WaitForThread( "waitForThread" );
|
|
Event EV_ScriptThread_WaitForConsole( "waitForConsole" );
|
|
Event EV_ScriptThread_WaitForVariable( "waitForVariable" );
|
|
Event EV_ScriptThread_WaitForDeath( "waitForDeath" );
|
|
Event EV_ScriptThread_WaitForSound( "waitForSound" );
|
|
Event EV_ScriptThread_WaitForPlayer( "waitForPlayer" );
|
|
Event EV_ScriptThread_End( "end" );
|
|
Event EV_ScriptThread_Print( "print" );
|
|
Event EV_ScriptThread_PrintInt( "printint" );
|
|
Event EV_ScriptThread_PrintFloat( "printfloat" );
|
|
Event EV_ScriptThread_PrintVector( "printvector" );
|
|
Event EV_ScriptThread_NewLine( "newline" );
|
|
Event EV_ScriptThread_UserPrint( "uprint" );
|
|
Event EV_ScriptThread_UserPrintInt( "uprintint" );
|
|
Event EV_ScriptThread_UserPrintFloat( "uprintfloat" );
|
|
Event EV_ScriptThread_UserPrintVector( "uprintvector" );
|
|
Event EV_ScriptThread_UserNewLine( "unewline" );
|
|
Event EV_ScriptThread_Assert( "assert" );
|
|
Event EV_ScriptThread_Break( "break" );
|
|
Event EV_ScriptThread_Clear( "clear" );
|
|
Event EV_ScriptThread_Trigger( "trigger" );
|
|
Event EV_ScriptThread_Spawn( "spawn", EV_CHEAT );
|
|
Event EV_ScriptThread_Map( "map" );
|
|
Event EV_ScriptThread_SetCvar( "setcvar" );
|
|
Event EV_ScriptThread_CueCamera( "cuecamera" );
|
|
Event EV_ScriptThread_CuePlayer( "cueplayer" );
|
|
Event EV_ScriptThread_FreezePlayer( "freezeplayer" );
|
|
Event EV_ScriptThread_ReleasePlayer( "releaseplayer" );
|
|
Event EV_ScriptThread_Menu( "menu" );
|
|
Event EV_ScriptThread_MissionFailed( "missionfailed" );
|
|
Event EV_ScriptThread_KillEnt( "killent", EV_CONSOLE | EV_CHEAT );
|
|
Event EV_ScriptThread_KillClass( "killclass", EV_CONSOLE | EV_CHEAT );
|
|
Event EV_ScriptThread_RemoveEnt( "removeent", EV_CONSOLE | EV_CHEAT );
|
|
Event EV_ScriptThread_RemoveClass( "removeclass", EV_CONSOLE | EV_CHEAT );
|
|
|
|
// client/server flow control
|
|
Event EV_ScriptThread_ServerOnly( "server" );
|
|
Event EV_ScriptThread_ClientOnly( "client" );
|
|
Event EV_ScriptThread_StuffCommand( "stuffcmd" );
|
|
Event EV_ScriptThread_Training( "training" );
|
|
Event EV_ScriptThread_ClearSaveGames( "clearsavegames" );
|
|
|
|
//
|
|
// world stuff
|
|
//
|
|
Event EV_ScriptThread_SetLightStyle( "lightstyle" );
|
|
Event EV_ScriptThread_RegisterAlias( "alias" );
|
|
Event EV_ScriptThread_RegisterAliasAndCache( "aliascache" );
|
|
Event EV_ScriptThread_SetCinematic( "cinematic" );
|
|
Event EV_ScriptThread_SetNonCinematic( "noncinematic" );
|
|
Event EV_ScriptThread_SetSkipThread( "skipthread" );
|
|
Event EV_ScriptThread_AirClamp( "airclamp" );
|
|
// Precache specific
|
|
Event EV_ScriptThread_Precache_Model( "cachemodel" );
|
|
Event EV_ScriptThread_Precache_PlayerModel( "cacheplayermodel" );
|
|
Event EV_ScriptThread_Precache_Sound( "cachesound" );
|
|
// set dialog script
|
|
Event EV_ScriptThread_SetDialogScript( "setdialogscript" );
|
|
// fades for movies
|
|
Event EV_ScriptThread_FadeIn( "fadein" );
|
|
Event EV_ScriptThread_FadeOut( "fadeout" );
|
|
Event EV_ScriptThread_Hud( "hud" );
|
|
Event EV_ScriptThread_LoadOverlay( "loadoverlay" );
|
|
Event EV_ScriptThread_LoadIntermission( "loadintermission" );
|
|
Event EV_ScriptThread_IntermissionLayout( "intermissionlayout" );
|
|
Event EV_ScriptThread_Overlay( "overlay" );
|
|
Event EV_ScriptThread_ScreenPrint( "screenprint" );
|
|
Event EV_ScriptThread_ScreenPrintFile( "screenprintfile" );
|
|
Event EV_ScriptThread_ClearScreenPrintFile( "clearscreenprintfile" );
|
|
Event EV_ScriptThread_MapName( "mapname" );
|
|
Event EV_ScriptThread_EndGame( "endgame" );
|
|
Event EV_ScriptThread_CameraCommand( "cam" );
|
|
|
|
// music command
|
|
Event EV_ScriptThread_MusicEvent( "music" );
|
|
Event EV_ScriptThread_ForceMusicEvent( "forcemusic" );
|
|
Event EV_ScriptThread_SoundtrackEvent( "soundtrack" );
|
|
|
|
// Precache specific
|
|
Event EV_ScriptThread_JC_Hearable( "jc_hearable" );
|
|
Event EV_ScriptThread_JC_Not_Hearable( "jc_not_hearable" );
|
|
|
|
// crucial dialog command
|
|
Event EV_ScriptThread_CrucialDialog( "crucialdialog" );
|
|
|
|
// dialog sound command
|
|
Event EV_ScriptThread_DialogSound( "dialogsound" );
|
|
|
|
CLASS_DECLARATION( Class, ThreadMarker, NULL );
|
|
|
|
ResponseDef ThreadMarker::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
ResponseDef ScriptMaster::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
ScriptMaster Director;
|
|
|
|
CLASS_DECLARATION( Listener, ScriptMaster, NULL );
|
|
|
|
ScriptMaster::ScriptMaster()
|
|
{
|
|
threadIndex = 0;
|
|
currentThread = NULL;
|
|
player_ready = false;
|
|
}
|
|
|
|
ScriptMaster::~ScriptMaster()
|
|
{
|
|
GameTime = NULL;
|
|
CloseScript();
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::CloseScript
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
KillThreads();
|
|
ScriptLib.CloseScripts();
|
|
GameTime = NULL;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::KillThreads
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int i;
|
|
int num;
|
|
|
|
num = Threads.NumObjects();
|
|
|
|
// Clear the waitfor's from each thread before deleting
|
|
// so they don't get triggered.
|
|
for( i = num; i > 0; i-- )
|
|
{
|
|
Threads.ObjectAt( i )->ClearWaitFor();
|
|
}
|
|
|
|
for( i = num; i > 0; i-- )
|
|
{
|
|
delete Threads.ObjectAt( i );
|
|
}
|
|
|
|
Threads.FreeObjectList();
|
|
|
|
threadIndex = 0;
|
|
currentThread = NULL;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptMaster::NotifyOtherThreads
|
|
(
|
|
int num
|
|
)
|
|
|
|
{
|
|
ScriptThread *thread1;
|
|
ScriptThread *thread2;
|
|
int i;
|
|
int n;
|
|
Event *ev;
|
|
|
|
thread1 = GetThread( num );
|
|
assert( thread1 );
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
thread2 = Threads.ObjectAt( i );
|
|
assert( thread2 );
|
|
if ( thread2->WaitingOnThread() == thread1 )
|
|
{
|
|
ev = new Event( EV_ScriptThread_ThreadCallback );
|
|
ev->SetThread( thread1 );
|
|
thread2->ProcessEvent( ev );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::DeathMessage
|
|
(
|
|
const char *name
|
|
)
|
|
{
|
|
ScriptThread *thread;
|
|
Event *ev;
|
|
int i,n;
|
|
|
|
// Look for threads that are waiting for this name
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
const char *waiting;
|
|
thread = Threads.ObjectAt( i );
|
|
assert( thread );
|
|
if (thread)
|
|
{
|
|
waiting = thread->WaitingOnDeath();
|
|
if (waiting)
|
|
{
|
|
if (!strcmp(waiting, name))
|
|
{
|
|
ev = new Event( EV_ScriptThread_DeathCallback );
|
|
thread->ProcessEvent(ev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptMaster::PlayerReady
|
|
(
|
|
void
|
|
)
|
|
{
|
|
return player_ready;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::PlayerNotReady
|
|
(
|
|
void
|
|
)
|
|
{
|
|
player_ready = false;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::PlayerSpawned
|
|
(
|
|
void
|
|
)
|
|
{
|
|
ScriptThread *thread;
|
|
int i,n;
|
|
|
|
player_ready = true;
|
|
// Look for threads that are waiting for the player
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
thread = Threads.ObjectAt( i );
|
|
assert( thread );
|
|
if (thread)
|
|
{
|
|
if ( thread->WaitingOnPlayer() )
|
|
{
|
|
thread->ClearWaitFor();
|
|
thread->Start( -1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::CreateConsoleUser
|
|
(
|
|
const char *console_name,
|
|
int user
|
|
)
|
|
{
|
|
ScriptVariable *var;
|
|
ScriptVariableList *vars;
|
|
const char *v;
|
|
char varname[256];
|
|
|
|
// Create a variable that holds the value for the slider.
|
|
sprintf(varname,"console.%s",console_name);
|
|
|
|
var = GetExistingVariable( varname );
|
|
if ( !var )
|
|
{
|
|
vars = GetVarGroup( varname );
|
|
if ( !vars )
|
|
{
|
|
return;
|
|
}
|
|
v = strchr( varname, '.' );
|
|
assert( v );
|
|
var = vars->CreateVariable( v + 1, user );
|
|
}
|
|
else
|
|
{
|
|
var->setIntValue( user );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::ConsoleVariable
|
|
(
|
|
const char *name,
|
|
const char *text
|
|
)
|
|
{
|
|
ScriptThread *thread;
|
|
ScriptVariable *var;
|
|
ScriptVariableList *vars;
|
|
Event *ev;
|
|
int i,n;
|
|
const char *v;
|
|
char varname[256];
|
|
|
|
// Create a variable that holds the value for the slider.
|
|
sprintf(varname,"console.%s",name);
|
|
|
|
var = GetExistingVariable( varname );
|
|
if ( !var )
|
|
{
|
|
vars = GetVarGroup( varname );
|
|
if ( !vars )
|
|
{
|
|
return;
|
|
}
|
|
v = strchr( varname, '.' );
|
|
assert( v );
|
|
var = vars->CreateVariable( v + 1, text );
|
|
}
|
|
else
|
|
{
|
|
var->setStringValue( text );
|
|
}
|
|
|
|
// Look for threads that are waiting for this variable
|
|
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
const char *waiting;
|
|
|
|
thread = Threads.ObjectAt( i );
|
|
assert( thread );
|
|
if (thread)
|
|
{
|
|
waiting = thread->WaitingOnVariable();
|
|
if (waiting)
|
|
{
|
|
if (!strcmp(waiting, name))
|
|
{
|
|
ev = new Event( EV_ScriptThread_VariableCallback );
|
|
thread->ProcessEvent(ev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::ConsoleInput
|
|
(
|
|
const char *name,
|
|
const char *text
|
|
)
|
|
{
|
|
ScriptThread *thread;
|
|
ScriptVariable *var;
|
|
ScriptVariableList *vars;
|
|
Event *ev;
|
|
int i,n;
|
|
const char *v;
|
|
char varname[256];
|
|
|
|
// Create a variable that holds the text.
|
|
sprintf(varname,"console.%s",name);
|
|
|
|
var = GetExistingVariable( varname );
|
|
if ( !var )
|
|
{
|
|
vars = GetVarGroup( varname );
|
|
if ( !vars )
|
|
{
|
|
return;
|
|
}
|
|
v = strchr( varname, '.' );
|
|
assert( v );
|
|
var = vars->CreateVariable( v + 1, text );
|
|
}
|
|
else
|
|
{
|
|
var->setStringValue( text );
|
|
}
|
|
|
|
// Look for threads that are waiting for input from
|
|
// this console.
|
|
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
const char *waiting;
|
|
thread = Threads.ObjectAt( i );
|
|
assert( thread );
|
|
if ( thread )
|
|
{
|
|
waiting = thread->WaitingOnConsole();
|
|
if (waiting)
|
|
{
|
|
if (!strcmp(waiting, name))
|
|
{
|
|
ev = new Event( EV_ScriptThread_ConsoleCallback );
|
|
thread->ProcessEvent(ev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptMaster::KillThread
|
|
(
|
|
int num
|
|
)
|
|
|
|
{
|
|
ScriptThread *thread;
|
|
|
|
// Must be safely reentryable so that the thread destructor can tell us that it's being deleted.
|
|
thread = GetThread( num );
|
|
if ( thread )
|
|
{
|
|
if ( currentThread == thread )
|
|
{
|
|
SetCurrentThread( NULL );
|
|
}
|
|
delete thread;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptMaster::RemoveThread
|
|
(
|
|
int num
|
|
)
|
|
|
|
{
|
|
ScriptThread *thread;
|
|
int i;
|
|
int n;
|
|
|
|
// Must be safely reentryable so that the thread destructor can tell us that it's being deleted.
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
thread = Threads.ObjectAt( i );
|
|
assert( thread );
|
|
if ( thread )
|
|
{
|
|
if ( thread->ThreadNum() == num )
|
|
{
|
|
Threads.RemoveObjectAt( i );
|
|
if ( currentThread == thread )
|
|
{
|
|
SetCurrentThread( NULL );
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptThread *ScriptMaster::CurrentThread
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return currentThread;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptMaster::SetCurrentThread
|
|
(
|
|
ScriptThread *thread
|
|
)
|
|
|
|
{
|
|
currentThread = thread;
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptThread *ScriptMaster::CreateThread
|
|
(
|
|
GameScript *scr,
|
|
const char *label,
|
|
scripttype_t type
|
|
)
|
|
|
|
{
|
|
ScriptThread *thread;
|
|
int threadnum;
|
|
|
|
thread = new ScriptThread;
|
|
|
|
thread->SetType( type );
|
|
threadnum = GetUniqueThreadNumber();
|
|
Threads.AddObject( thread );
|
|
|
|
if ( !thread->Setup( threadnum, scr, label ) )
|
|
{
|
|
KillThread( threadnum );
|
|
return NULL;
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
//FIXME
|
|
// Why can't these be one function?
|
|
EXPORT_FROM_DLL ScriptThread *ScriptMaster::CreateThread
|
|
(
|
|
const char *name,
|
|
scripttype_t type,
|
|
const char *label
|
|
)
|
|
|
|
{
|
|
GameScript *scr;
|
|
ScriptThread *thread;
|
|
|
|
scr = ScriptLib.GetScript( name );
|
|
if ( scr )
|
|
{
|
|
thread = CreateThread( scr, label, type );
|
|
return thread;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptThread *ScriptMaster::GetThread
|
|
(
|
|
int num
|
|
)
|
|
|
|
{
|
|
int i;
|
|
int n;
|
|
ScriptThread *thread;
|
|
|
|
n = Threads.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
thread = Threads.ObjectAt( i );
|
|
assert( thread );
|
|
if ( thread )
|
|
{
|
|
if ( thread->ThreadNum() == num )
|
|
{
|
|
return thread;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EXPORT_FROM_DLL int ScriptMaster::GetUniqueThreadNumber
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
do
|
|
{
|
|
threadIndex++;
|
|
if ( threadIndex == 0 )
|
|
{
|
|
threadIndex = 1;
|
|
}
|
|
}
|
|
while( GetThread( threadIndex ) );
|
|
|
|
return threadIndex;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptMaster::labelExists
|
|
(
|
|
GameScript * scr,
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
return scr->labelExists( name );
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptMaster::Goto
|
|
(
|
|
GameScript * scr,
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
return scr->Goto( name );
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptVariableList *ScriptMaster::GetVarGroup
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
ScriptVariableList *vars;
|
|
const char *v;
|
|
char vargroup[ 20 ];
|
|
int len;
|
|
|
|
v = strchr( name, '.' );
|
|
if ( !v )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
len = v - name;
|
|
if ( len > sizeof( vargroup ) - 1 )
|
|
{
|
|
len = sizeof( vargroup ) - 1;
|
|
}
|
|
memset( vargroup, 0, sizeof( vargroup ) );
|
|
strncpy( vargroup, name, len );
|
|
|
|
vars = NULL;
|
|
if ( !strcmp( vargroup, "game" ) )
|
|
{
|
|
vars = &gameVars;
|
|
}
|
|
else if ( !strcmp( vargroup, "level" ) )
|
|
{
|
|
vars = &levelVars;
|
|
}
|
|
else if ( !strcmp( vargroup, "local" ) && currentThread )
|
|
{
|
|
vars = currentThread->Vars();
|
|
}
|
|
else if ( !strcmp( vargroup, "parm" ) )
|
|
{
|
|
vars = &parmVars;
|
|
}
|
|
else if ( !strcmp( vargroup, "console" ) )
|
|
{
|
|
vars = &consoleVars;
|
|
}
|
|
|
|
return vars;
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptVariable *ScriptMaster::GetExistingVariable
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
ScriptVariableList *vars;
|
|
const char *v;
|
|
|
|
vars = GetVarGroup( name );
|
|
if ( !vars )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
v = strchr( name, '.' );
|
|
if ( !v )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return vars->GetVariable( v + 1 );
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptVariable *ScriptMaster::GetVariable
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
ScriptVariable *var;
|
|
ScriptVariableList *vars;
|
|
const char *v;
|
|
|
|
var = GetExistingVariable( name );
|
|
if ( !var )
|
|
{
|
|
vars = GetVarGroup( name );
|
|
if ( !vars )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
v = strchr( name, '.' );
|
|
assert( v );
|
|
var = vars->CreateVariable( v + 1, "" );
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
CLASS_DECLARATION( Listener, ScriptThread, NULL );
|
|
|
|
ResponseDef ScriptThread::Responses[] =
|
|
{
|
|
{ &EV_ScriptThread_Execute, ( Response )ScriptThread::Execute },
|
|
{ &EV_MoveDone, ( Response )ScriptThread::ObjectMoveDone },
|
|
{ &EV_ScriptThread_Callback, ( Response )ScriptThread::ScriptCallback },
|
|
{ &EV_ScriptThread_ThreadCallback, ( Response )ScriptThread::ThreadCallback },
|
|
{ &EV_ScriptThread_ConsoleCallback, ( Response )ScriptThread::ConsoleCallback },
|
|
{ &EV_ScriptThread_VariableCallback,( Response )ScriptThread::VariableCallback },
|
|
{ &EV_ScriptThread_DeathCallback, ( Response )ScriptThread::DeathCallback },
|
|
{ &EV_ScriptThread_CreateThread, ( Response )ScriptThread::CreateThread },
|
|
{ &EV_ScriptThread_TerminateThread, ( Response )ScriptThread::TerminateThread },
|
|
{ &EV_ScriptThread_ControlObject, ( Response )ScriptThread::ControlObject },
|
|
{ &EV_ScriptThread_Goto, ( Response )ScriptThread::EventGoto },
|
|
{ &EV_ScriptThread_Pause, ( Response )ScriptThread::EventPause },
|
|
{ &EV_ScriptThread_Wait, ( Response )ScriptThread::EventWait },
|
|
{ &EV_ScriptThread_WaitFor, ( Response )ScriptThread::EventWaitFor },
|
|
{ &EV_ScriptThread_WaitForThread, ( Response )ScriptThread::EventWaitForThread },
|
|
{ &EV_ScriptThread_WaitForConsole, ( Response )ScriptThread::EventWaitForConsole },
|
|
{ &EV_ScriptThread_WaitForVariable, ( Response )ScriptThread::EventWaitForVariable },
|
|
{ &EV_ScriptThread_WaitForDeath, ( Response )ScriptThread::EventWaitForDeath },
|
|
{ &EV_ScriptThread_WaitForSound, ( Response )ScriptThread::EventWaitForSound },
|
|
{ &EV_ScriptThread_WaitForPlayer, ( Response )ScriptThread::EventWaitForPlayer },
|
|
{ &EV_ScriptThread_End, ( Response )ScriptThread::EventEnd },
|
|
{ &EV_ScriptThread_Print, ( Response )ScriptThread::Print },
|
|
{ &EV_ScriptThread_PrintInt, ( Response )ScriptThread::PrintInt },
|
|
{ &EV_ScriptThread_PrintFloat, ( Response )ScriptThread::PrintFloat },
|
|
{ &EV_ScriptThread_PrintVector, ( Response )ScriptThread::PrintVector },
|
|
{ &EV_ScriptThread_NewLine, ( Response )ScriptThread::NewLine },
|
|
{ &EV_ScriptThread_UserPrint, ( Response )ScriptThread::UserPrint },
|
|
{ &EV_ScriptThread_UserPrintInt, ( Response )ScriptThread::UserPrintInt },
|
|
{ &EV_ScriptThread_UserPrintFloat, ( Response )ScriptThread::UserPrintFloat },
|
|
{ &EV_ScriptThread_UserPrintVector, ( Response )ScriptThread::UserPrintVector },
|
|
{ &EV_ScriptThread_UserNewLine, ( Response )ScriptThread::UserNewLine },
|
|
{ &EV_ScriptThread_Assert, ( Response )ScriptThread::Assert },
|
|
{ &EV_ScriptThread_Break, ( Response )ScriptThread::Break },
|
|
{ &EV_ScriptThread_Clear, ( Response )ScriptThread::Clear },
|
|
{ &EV_ScriptThread_Trigger, ( Response )ScriptThread::TriggerEvent },
|
|
{ &EV_ScriptThread_ServerOnly, ( Response )ScriptThread::ServerEvent },
|
|
{ &EV_ScriptThread_ClientOnly, ( Response )ScriptThread::ClientEvent },
|
|
{ &EV_ScriptThread_StuffCommand, ( Response )ScriptThread::StuffCommand },
|
|
{ &EV_ScriptThread_Training, ( Response )ScriptThread::Training },
|
|
{ &EV_ScriptThread_ClearSaveGames, ( Response )ScriptThread::ClearSaveGames },
|
|
{ &EV_ScriptThread_Spawn, ( Response )ScriptThread::Spawn },
|
|
{ &EV_ScriptThread_SetLightStyle, ( Response )ScriptThread::EventSetLightStyle },
|
|
{ &EV_ScriptThread_Precache_Model, ( Response )ScriptThread::CacheModel },
|
|
{ &EV_ScriptThread_Precache_PlayerModel, ( Response )ScriptThread::CachePlayerModel },
|
|
{ &EV_ScriptThread_Precache_Sound, ( Response )ScriptThread::CacheSound },
|
|
{ &EV_ScriptThread_RegisterAlias, ( Response )ScriptThread::RegisterAlias },
|
|
{ &EV_ScriptThread_RegisterAliasAndCache, ( Response )ScriptThread::RegisterAliasAndCache },
|
|
{ &EV_ScriptThread_Map, ( Response )ScriptThread::MapEvent },
|
|
{ &EV_ScriptThread_SetCvar, ( Response )ScriptThread::SetCvarEvent },
|
|
{ &EV_ScriptThread_CueCamera, ( Response )ScriptThread::CueCamera },
|
|
{ &EV_ScriptThread_CuePlayer, ( Response )ScriptThread::CuePlayer },
|
|
{ &EV_ScriptThread_FreezePlayer, ( Response )ScriptThread::FreezePlayer },
|
|
{ &EV_ScriptThread_ReleasePlayer, ( Response )ScriptThread::ReleasePlayer },
|
|
{ &EV_DialogEvent, ( Response )ScriptThread::DialogEvent },
|
|
{ &EV_ScriptThread_SetDialogScript, ( Response )ScriptThread::SetDialogScript },
|
|
{ &EV_ScriptThread_FadeIn, ( Response )ScriptThread::FadeIn },
|
|
{ &EV_ScriptThread_FadeOut, ( Response )ScriptThread::FadeOut },
|
|
{ &EV_ScriptThread_Hud, ( Response )ScriptThread::Hud },
|
|
{ &EV_ScriptThread_Menu, ( Response )ScriptThread::MenuEvent },
|
|
{ &EV_ScriptThread_MusicEvent, ( Response )ScriptThread::MusicEvent },
|
|
{ &EV_ScriptThread_ForceMusicEvent, ( Response )ScriptThread::ForceMusicEvent },
|
|
{ &EV_ScriptThread_SoundtrackEvent, ( Response )ScriptThread::SoundtrackEvent },
|
|
{ &EV_ScriptThread_LoadOverlay, ( Response )ScriptThread::LoadOverlay },
|
|
{ &EV_ScriptThread_LoadIntermission,( Response )ScriptThread::LoadIntermission },
|
|
{ &EV_ScriptThread_IntermissionLayout, ( Response )ScriptThread::IntermissionLayout },
|
|
{ &EV_ScriptThread_Overlay, ( Response )ScriptThread::Overlay },
|
|
{ &EV_ScriptThread_ScreenPrint, ( Response )ScriptThread::ScreenPrint },
|
|
{ &EV_ScriptThread_ScreenPrintFile, ( Response )ScriptThread::ScreenPrintFile },
|
|
{ &EV_ScriptThread_MapName, ( Response )ScriptThread::MapName },
|
|
{ &EV_ScriptThread_EndGame, ( Response )ScriptThread::EndGame },
|
|
{ &EV_ScriptThread_ClearScreenPrintFile, ( Response )ScriptThread::ClearScreenPrintFile },
|
|
{ &EV_ScriptThread_SetCinematic, ( Response )ScriptThread::SetCinematic },
|
|
{ &EV_ScriptThread_SetNonCinematic, ( Response )ScriptThread::SetNonCinematic },
|
|
{ &EV_ScriptThread_SetSkipThread, ( Response )ScriptThread::SetSkipThread },
|
|
{ &EV_ScriptThread_AirClamp, ( Response )ScriptThread::AirClamp },
|
|
{ &EV_ScriptThread_JC_Hearable, ( Response )ScriptThread::JC_Hearable },
|
|
{ &EV_ScriptThread_JC_Not_Hearable, ( Response )ScriptThread::JC_Not_Hearable },
|
|
{ &EV_ScriptThread_MissionFailed, ( Response )ScriptThread::MissionFailed },
|
|
{ &EV_ScriptThread_KillEnt, ( Response )ScriptThread::KillEnt },
|
|
{ &EV_ScriptThread_RemoveEnt, ( Response )ScriptThread::RemoveEnt },
|
|
{ &EV_ScriptThread_KillClass, ( Response )ScriptThread::KillClass },
|
|
{ &EV_ScriptThread_RemoveClass, ( Response )ScriptThread::RemoveClass },
|
|
{ &EV_ScriptThread_CrucialDialog, ( Response )ScriptThread::CrucialDialogEvent },
|
|
{ &EV_ScriptThread_DialogSound, ( Response )ScriptThread::DialogSoundEvent },
|
|
{ &EV_AI_RecalcPaths, ( Response )ScriptThread::PassToPathmanager },
|
|
{ &EV_AI_CalcPath, ( Response )ScriptThread::PassToPathmanager },
|
|
{ &EV_AI_DisconnectPath, ( Response )ScriptThread::PassToPathmanager },
|
|
{ &EV_ScriptThread_CameraCommand, ( Response )ScriptThread::CameraCommand },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
ScriptThread::ScriptThread()
|
|
{
|
|
threadNum = 0;
|
|
ClearWaitFor();
|
|
threadDying = false;
|
|
doneProcessing = true;
|
|
type = LEVEL_SCRIPT;
|
|
}
|
|
|
|
ScriptThread::~ScriptThread()
|
|
{
|
|
Director.NotifyOtherThreads( threadNum );
|
|
Director.RemoveThread( threadNum );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ClearWaitFor
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
waitUntil = 0;
|
|
waitingFor = "";
|
|
waitingNumObjects = 0;
|
|
waitingForThread = NULL;
|
|
waitingForConsole = "";
|
|
waitingForVariable = "";
|
|
waitingForDeath = "";
|
|
waitingForPlayer = false;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::SetType
|
|
(
|
|
scripttype_t newtype
|
|
)
|
|
|
|
{
|
|
type = newtype;
|
|
}
|
|
|
|
EXPORT_FROM_DLL scripttype_t ScriptThread::GetType
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return type;
|
|
}
|
|
|
|
EXPORT_FROM_DLL int ScriptThread::ThreadNum
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return threadNum;
|
|
}
|
|
|
|
EXPORT_FROM_DLL const char *ScriptThread::ThreadName
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return threadName.c_str();
|
|
}
|
|
|
|
EXPORT_FROM_DLL int ScriptThread::CurrentLine
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return linenumber;
|
|
}
|
|
|
|
EXPORT_FROM_DLL const char *ScriptThread::Filename
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return script.Filename();
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptThread *ScriptThread::WaitingOnThread
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return waitingForThread;
|
|
}
|
|
|
|
EXPORT_FROM_DLL const char *ScriptThread::WaitingOnConsole
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return waitingForConsole.c_str();
|
|
}
|
|
|
|
EXPORT_FROM_DLL const char *ScriptThread::WaitingOnVariable
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return waitingForVariable.c_str();
|
|
}
|
|
|
|
EXPORT_FROM_DLL const char *ScriptThread::WaitingOnDeath
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return waitingForDeath.c_str();
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptThread::WaitingOnPlayer
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return waitingForPlayer;
|
|
}
|
|
|
|
EXPORT_FROM_DLL ScriptVariableList *ScriptThread::Vars
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return &localVars;
|
|
}
|
|
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptThread::Setup
|
|
(
|
|
int num,
|
|
GameScript *scr,
|
|
const char *label
|
|
)
|
|
|
|
{
|
|
threadNum = num;
|
|
|
|
ClearWaitFor();
|
|
script.SetSourceScript( scr );
|
|
if ( label && !Goto( label ) )
|
|
{
|
|
ScriptError( "Can't create thread. Label '%s' not found", label );
|
|
return false;
|
|
}
|
|
|
|
if ( label )
|
|
{
|
|
threadName = label;
|
|
}
|
|
else
|
|
{
|
|
threadName = script.Filename();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptThread::SetScript
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
GameScript *scr;
|
|
|
|
scr = ScriptLib.GetScript( name );
|
|
if ( scr )
|
|
{
|
|
Setup( threadNum, scr, NULL );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptThread::Goto
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
qboolean result;
|
|
|
|
result = ScriptLib.Goto( &script, name );
|
|
if ( result )
|
|
{
|
|
// Cancel pending execute events when waitUntil is set
|
|
if ( waitUntil )
|
|
{
|
|
CancelEventsOfType( EV_ScriptThread_Execute );
|
|
}
|
|
ClearWaitFor();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptThread::labelExists
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
return ScriptLib.labelExists( &script, name );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::Mark
|
|
(
|
|
ThreadMarker *mark
|
|
)
|
|
|
|
{
|
|
assert( mark );
|
|
|
|
mark->linenumber = linenumber;
|
|
mark->doneProcessing = doneProcessing;
|
|
mark->waitingFor = waitingFor;
|
|
mark->waitingForThread = waitingForThread;
|
|
mark->waitingForConsole = waitingForConsole;
|
|
mark->waitingForVariable = waitingForVariable;
|
|
mark->waitingForDeath = waitingForDeath;
|
|
mark->waitingForPlayer = waitingForPlayer;
|
|
mark->waitingNumObjects = waitingNumObjects;
|
|
if ( waitUntil )
|
|
{
|
|
// add one so that 0 is always reserved for no wait
|
|
mark->waitUntil = waitUntil - level.time + 1;
|
|
}
|
|
else
|
|
{
|
|
mark->waitUntil = 0;
|
|
}
|
|
|
|
script.Mark( &mark->scriptmarker );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::Restore
|
|
(
|
|
ThreadMarker *mark
|
|
)
|
|
|
|
{
|
|
assert( mark );
|
|
|
|
linenumber = mark->linenumber;
|
|
doneProcessing = mark->doneProcessing;
|
|
|
|
waitingFor = mark->waitingFor;
|
|
waitingForThread = mark->waitingForThread;
|
|
waitingForConsole = mark->waitingForConsole;
|
|
waitingForVariable = mark->waitingForVariable;
|
|
waitingForDeath = mark->waitingForDeath;
|
|
waitingForPlayer = mark->waitingForPlayer;
|
|
waitingNumObjects = mark->waitingNumObjects;
|
|
|
|
script.Restore( &mark->scriptmarker );
|
|
|
|
if ( mark->waitUntil )
|
|
{
|
|
// subtract one since we added one when we stored it.
|
|
// this way, 0 is always reserved for no wait
|
|
// Cheezy, yeah, but since I'm commenting it, it's "ok". :)
|
|
waitUntil = mark->waitUntil + level.time - 1;
|
|
Start( waitUntil - level.time );
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL TargetList *ScriptThread::GetTargetList
|
|
(
|
|
str &targetname
|
|
)
|
|
{
|
|
TargetList *tlist;
|
|
int num;
|
|
int i;
|
|
|
|
num = targets.NumObjects();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
tlist = targets.ObjectAt( i );
|
|
if ( targetname == tlist->targetname )
|
|
{
|
|
return tlist;
|
|
}
|
|
}
|
|
|
|
tlist = world->GetTargetList( targetname );
|
|
targets.AddObject( tlist );
|
|
|
|
return tlist;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::SendCommandToSlaves
|
|
(
|
|
const char *name,
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Event *sendevent;
|
|
Entity *ent;
|
|
TargetList *tlist;
|
|
int i;
|
|
int num;
|
|
|
|
if ( name && name[ 0 ] )
|
|
{
|
|
tlist = GetTargetList( str(name+1) );
|
|
num = tlist->list.NumObjects();
|
|
for ( i = 1; i <= num; i++ )
|
|
{
|
|
ent = tlist->list.ObjectAt( i );
|
|
|
|
assert( ent );
|
|
|
|
sendevent = new Event( *ev );
|
|
|
|
if ( !updateList.ObjectInList( ent->entnum ) )
|
|
{
|
|
updateList.AddObject( ent->entnum );
|
|
|
|
// Tell the object that we're about to send it some orders
|
|
ent->ProcessEvent( EV_Script_NewOrders );
|
|
}
|
|
|
|
// Send the command
|
|
ent->ProcessEvent( sendevent );
|
|
}
|
|
}
|
|
//
|
|
// free up the event
|
|
//
|
|
delete ev;
|
|
}
|
|
|
|
qboolean ScriptThread::FindEvent
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
if ( !Event::Exists( name ) )
|
|
{
|
|
ScriptError( "Unknown command '%s'\n", name );
|
|
script.SkipToEOL();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ProcessCommand
|
|
(
|
|
int argc,
|
|
const char **argv
|
|
)
|
|
|
|
{
|
|
ScriptVariableList *vars;
|
|
ScriptVariable *var;
|
|
str command;
|
|
str name;
|
|
Event *event;
|
|
Entity *ent;
|
|
|
|
if ( argc < 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
name = argv[ 0 ];
|
|
if ( argc > 1 )
|
|
{
|
|
command = argv[ 1 ];
|
|
}
|
|
|
|
// Check for variable commands
|
|
vars = Director.GetVarGroup( name.c_str() );
|
|
if ( vars )
|
|
{
|
|
var = Director.GetVariable( name.c_str() );
|
|
if ( var->isVariableCommand( command.c_str() ) )
|
|
{
|
|
event = new Event( command );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddTokens( argc - 2, &argv[ 2 ] );
|
|
var->ProcessEvent( event );
|
|
return;
|
|
}
|
|
|
|
name = var->stringValue();
|
|
if ( !name.length() )
|
|
{
|
|
ScriptError( "Uninitialized variable '%s' used for command '%s'", var->getName(), command.c_str() );
|
|
return;
|
|
}
|
|
else if ( name[ 0 ] != '$' && name[ 0 ] != '@' && name[ 0 ] != '%' && name[ 0 ] != '*' )
|
|
{
|
|
ScriptError( "Invalid variable command '%s'", command.c_str() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for object commands
|
|
if ( name[ 0 ] == '$' )
|
|
{
|
|
if ( FindEvent( command.c_str() ) )
|
|
{
|
|
event = new Event( command );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddTokens( argc - 2, &argv[ 2 ] );
|
|
SendCommandToSlaves( name.c_str(), event );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Check for surface commands
|
|
if ( name[ 0 ] == '@' )
|
|
{
|
|
if ( FindEvent( command.c_str() ) )
|
|
{
|
|
event = new Event( command );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddToken( &name[ 1 ] );
|
|
event->AddTokens( argc - 2, &argv[ 2 ] );
|
|
surfaceManager.ProcessEvent( event );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Check for console commands
|
|
if ( name[ 0 ] == '%' )
|
|
{
|
|
if ( FindEvent( command.c_str() ) )
|
|
{
|
|
event = new Event( command );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddToken( &name[ 1 ] );
|
|
event->AddTokens( argc - 2, &argv[ 2 ] );
|
|
consoleManager.ProcessEvent( event );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Check for entnum commands
|
|
if ( name[ 0 ] == '*' )
|
|
{
|
|
if ( !IsNumeric( &name[ 1 ] ) )
|
|
{
|
|
ScriptError( "Expecting numeric value for * command, but found '%s'\n", &name[ 1 ] );
|
|
}
|
|
else if ( FindEvent( command.c_str() ) )
|
|
{
|
|
ent = G_GetEntity( atoi( &name[ 1 ] ) );
|
|
if ( ent )
|
|
{
|
|
event = new Event( command );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddTokens( argc - 2, &argv[ 2 ] );
|
|
ent->ProcessEvent( event );
|
|
}
|
|
else
|
|
{
|
|
ScriptError( "Entity not found for * command\n" );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Handle global commands
|
|
if ( FindEvent( name.c_str() ) )
|
|
{
|
|
event = new Event( name );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddTokens( argc - 1, &argv[ 1 ] );
|
|
if ( !ProcessEvent( event ) )
|
|
{
|
|
ScriptError( "Invalid global command '%s'\n", name.c_str() );
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ProcessCommandFromEvent
|
|
(
|
|
Event *ev,
|
|
int startarg
|
|
)
|
|
|
|
{
|
|
int argc;
|
|
int numargs;
|
|
const char *argv[ MAX_COMMANDS ];
|
|
str args[ MAX_COMMANDS ];
|
|
int i;
|
|
|
|
numargs = ev->NumArgs();
|
|
if ( numargs < startarg )
|
|
{
|
|
ev->Error( "Expecting statement after conditional", MAX_COMMANDS );
|
|
return;
|
|
}
|
|
|
|
argc = numargs - startarg + 1;
|
|
|
|
if ( argc >= MAX_COMMANDS )
|
|
{
|
|
ev->Error( "Line exceeds %d command limit", MAX_COMMANDS );
|
|
return;
|
|
}
|
|
|
|
for( i = 0; i < argc; i++ )
|
|
{
|
|
args[ i ] = ev->GetToken( startarg + i );
|
|
argv[ i ] = args[ i ].c_str();
|
|
}
|
|
|
|
ProcessCommand( argc, argv );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::Start
|
|
(
|
|
float delay
|
|
)
|
|
{
|
|
CancelEventsOfType( EV_ScriptThread_Execute );
|
|
if ( delay < 0 )
|
|
ProcessEvent( EV_ScriptThread_Execute );
|
|
else
|
|
PostEvent( EV_ScriptThread_Execute, delay );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::Execute
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int num;
|
|
ScriptThread *oldthread;
|
|
int argc;
|
|
const char *argv[ MAX_COMMANDS ];
|
|
char args[ MAX_COMMANDS ][ MAXTOKEN ];
|
|
ScriptVariable *var;
|
|
|
|
if ( threadDying )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// set the current game time
|
|
if ( !GameTime )
|
|
{
|
|
GameTime = gameVars.CreateVariable( "time", 0 );
|
|
}
|
|
|
|
GameTime->setFloatValue( level.time );
|
|
|
|
// clear the updateList so that all objects moved this frame are notified before they receive any commands
|
|
// we have to do this here as well as in DoMove, since DoMove may not be called
|
|
updateList.ClearObjectList();
|
|
|
|
oldthread = Director.CurrentThread();
|
|
Director.SetCurrentThread( this );
|
|
|
|
// if we're not being called from another thread, clear the parm variables
|
|
if ( !oldthread )
|
|
{
|
|
parmVars.ClearList();
|
|
}
|
|
|
|
ClearWaitFor();
|
|
|
|
var = Director.GetVariable( "parm.previousthread" );
|
|
if ( oldthread )
|
|
{
|
|
var->setIntValue( oldthread->ThreadNum() );
|
|
}
|
|
else
|
|
{
|
|
var->setIntValue( 0 );
|
|
}
|
|
|
|
var = Director.GetVariable( "parm.currentthread" );
|
|
|
|
doneProcessing = false;
|
|
|
|
num = 0;
|
|
while( ( num++ < 10000 ) && !doneProcessing && !threadDying )
|
|
{
|
|
// keep our thread number up to date
|
|
var->setIntValue( threadNum );
|
|
|
|
script.SkipNonToken( true );
|
|
|
|
// save the line number for errors
|
|
linenumber = script.GetLineNumber();
|
|
|
|
argc = 0;
|
|
while( script.TokenAvailable( false ) )
|
|
{
|
|
if ( argc >= MAX_COMMANDS )
|
|
{
|
|
ScriptError( "Line exceeds %d command limit", MAX_COMMANDS );
|
|
script.SkipToEOL();
|
|
break;
|
|
}
|
|
strcpy( args[ argc ], script.GetToken( false ) );
|
|
argv[ argc ] = args[ argc ];
|
|
argc++;
|
|
}
|
|
|
|
assert( argc > 0 );
|
|
|
|
// Ignore labels
|
|
if ( args[ 0 ][ strlen( args[ 0 ] ) - 1 ] != ':' )
|
|
{
|
|
ProcessCommand( argc, argv );
|
|
}
|
|
}
|
|
|
|
if ( !doneProcessing )
|
|
{
|
|
gi.error( "Command overflow. Possible infinite loop in thread '%s'.\n"
|
|
"Stopping on line %d of %s\n", threadName.c_str(), script.GetLineNumber(), script.Filename() );
|
|
}
|
|
|
|
Director.SetCurrentThread( oldthread );
|
|
|
|
// Set the thread number on exit, in case we were called by someone who wants to know our thread
|
|
var = Director.GetVariable( "parm.previousthread" );
|
|
var->setIntValue( threadNum );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ScriptError
|
|
(
|
|
const char *fmt,
|
|
...
|
|
)
|
|
|
|
{
|
|
va_list argptr;
|
|
char text[ 1024 ];
|
|
|
|
va_start( argptr, fmt );
|
|
vsprintf( text, fmt, argptr );
|
|
va_end( argptr );
|
|
|
|
gi.dprintf( "%s(%d):: %s\n", Filename(), linenumber, text );
|
|
}
|
|
|
|
//****************************************************************************************
|
|
//
|
|
// global commands
|
|
//
|
|
//****************************************************************************************
|
|
|
|
EXPORT_FROM_DLL qboolean ScriptThread::WaitingFor
|
|
(
|
|
Entity *obj
|
|
)
|
|
|
|
{
|
|
assert( obj );
|
|
|
|
if ( waitingFor.length() )
|
|
{
|
|
if ( ( ( waitingFor[ 0 ] == '*' ) && ( atoi( &waitingFor.c_str()[ 1 ] ) == obj->entnum ) ) ||
|
|
( waitingFor == obj->TargetName() ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ObjectMoveDone
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *obj;
|
|
|
|
obj = ev->GetEntity( 1 );
|
|
assert( obj );
|
|
|
|
#if 0
|
|
gi.dprintf( "Move done %d:'%s'\n", obj->entnum, obj->TargetName() );
|
|
#endif
|
|
|
|
if ( WaitingFor( obj ) )
|
|
{
|
|
waitingNumObjects--;
|
|
if ( waitingNumObjects <= 0 )
|
|
{
|
|
ClearWaitFor();
|
|
// start right away
|
|
Start( -1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptThread::CreateThread
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ScriptThread * pThread;
|
|
|
|
pThread = Director.CreateThread( &script, ev->GetToken( 1 ) );
|
|
if ( pThread )
|
|
{
|
|
// start right away
|
|
pThread->Start( -1 );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::TerminateThread
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int threadnum;
|
|
ScriptThread *thread;
|
|
|
|
threadnum = 0;
|
|
// we specified the thread to kill
|
|
if ( ev->NumArgs() > 0 )
|
|
{
|
|
threadnum = ev->GetInteger( 1 );
|
|
}
|
|
else
|
|
{
|
|
thread = ev->GetThread();
|
|
if ( thread )
|
|
{
|
|
threadnum = thread->ThreadNum();
|
|
}
|
|
}
|
|
|
|
if ( threadnum != 0 )
|
|
{
|
|
thread = Director.GetThread( threadnum );
|
|
if ( thread && ( thread->GetType() == MODEL_SCRIPT ) && ( ev->GetSource() == EV_FROM_SCRIPT ) )
|
|
{
|
|
ev->Error( "Can't terminate an actor's thread via script." );
|
|
return;
|
|
}
|
|
Director.KillThread( threadnum );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::ControlObject
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->GetEntity( 1 );
|
|
}
|
|
|
|
void ScriptThread::EventGoto
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const char *label;
|
|
|
|
label = ev->GetToken( 1 );
|
|
if ( !Goto( label ) )
|
|
{
|
|
ev->Error( "Label '%s' not found", label );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::EventPause
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ClearWaitFor();
|
|
doneProcessing = true;
|
|
}
|
|
|
|
void ScriptThread::EventWait
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
DoMove();
|
|
|
|
ClearWaitFor();
|
|
|
|
waitUntil = ev->GetFloat( 1 ) + level.time;
|
|
|
|
Start( ev->GetFloat( 1 ) );
|
|
doneProcessing = true;
|
|
}
|
|
|
|
void ScriptThread::EventWaitFor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *ent;
|
|
const char *objname;
|
|
const char *tname;
|
|
|
|
ClearWaitFor();
|
|
doneProcessing = true;
|
|
|
|
ent = ev->GetEntity( 1 );
|
|
if ( ent )
|
|
{
|
|
objname = ev->GetString( 1 );
|
|
tname = ent->TargetName();
|
|
if ( objname && objname[ 0 ] == '*' )
|
|
{
|
|
if ( !IsNumeric( &objname[ 1 ] ) )
|
|
{
|
|
ScriptError( "Expecting *-prefixed numeric value for waitFor command, but found '%s'\n", objname );
|
|
return;
|
|
}
|
|
|
|
waitingFor = objname;
|
|
}
|
|
else if ( !tname || !tname[ 0 ] )
|
|
{
|
|
// Probably won't happen, but check for it anyway.
|
|
ScriptError( "Entity doesn't have a targetname.\n" );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
TargetList *tlist;
|
|
waitingFor = tname;
|
|
//
|
|
// set the number of objects that belong to this targetname
|
|
//
|
|
tlist = GetTargetList( waitingFor );
|
|
waitingNumObjects = tlist->list.NumObjects();
|
|
if ( waitingNumObjects <= 0 )
|
|
{
|
|
waitingNumObjects = 1;
|
|
ScriptError( "no objects of targetname %s found.\n", tname );
|
|
}
|
|
else
|
|
{
|
|
Entity * tent;
|
|
int i;
|
|
//
|
|
// make sure all these objects are in the update list
|
|
//
|
|
for ( i = 1; i <= waitingNumObjects; i++ )
|
|
{
|
|
tent = tlist->list.ObjectAt( i );
|
|
// add the object to the update list to make sure we tell it to do a move
|
|
if ( !updateList.ObjectInList( tent->entnum ) )
|
|
{
|
|
updateList.AddObject( tent->entnum );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the object to the update list to make sure we tell it to do a move
|
|
if ( !updateList.ObjectInList( ent->entnum ) )
|
|
{
|
|
updateList.AddObject( ent->entnum );
|
|
}
|
|
}
|
|
|
|
DoMove();
|
|
}
|
|
|
|
void ScriptThread::EventWaitForThread
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
doneProcessing = true;
|
|
|
|
ClearWaitFor();
|
|
waitingForThread = Director.GetThread( ev->GetInteger( 1 ) );
|
|
if ( !waitingForThread )
|
|
{
|
|
ev->Error( "EventWaitForThread", "Thread %d not running", ev->GetInteger( 1 ) );
|
|
return;
|
|
}
|
|
|
|
DoMove();
|
|
}
|
|
|
|
void ScriptThread::EventWaitForDeath
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
doneProcessing = true;
|
|
|
|
ClearWaitFor();
|
|
waitingForDeath = ev->GetString(1);
|
|
if ( !waitingForDeath.length() )
|
|
{
|
|
ev->Error( "EventWaitForDeath", "Null name" );
|
|
return;
|
|
}
|
|
|
|
DoMove();
|
|
}
|
|
|
|
void ScriptThread::EventWaitForConsole
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
doneProcessing = true;
|
|
|
|
ClearWaitFor();
|
|
waitingForConsole = ev->GetString(1);
|
|
|
|
if ( !waitingForConsole.length() )
|
|
{
|
|
ev->Error( "EventWaitForConsole", "Null console" );
|
|
return;
|
|
}
|
|
|
|
DoMove();
|
|
}
|
|
|
|
void ScriptThread::EventWaitForVariable
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
doneProcessing = true;
|
|
|
|
ClearWaitFor();
|
|
waitingForVariable = ev->GetString(1);
|
|
|
|
if ( !waitingForVariable.length() )
|
|
{
|
|
ev->Error( "EventWaitForVariable", "Null variable" );
|
|
return;
|
|
}
|
|
|
|
DoMove();
|
|
}
|
|
|
|
void ScriptThread::EventWaitForSound
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str sound;
|
|
float delay;
|
|
|
|
ClearWaitFor();
|
|
|
|
DoMove();
|
|
|
|
delay = 0;
|
|
sound = ev->GetString( 1 );
|
|
if ( ev->NumArgs() > 1 )
|
|
delay = ev->GetFloat( 2 );
|
|
|
|
delay += gi.SoundLength( sound.c_str() );
|
|
|
|
Start( delay );
|
|
doneProcessing = true;
|
|
}
|
|
|
|
void ScriptThread::EventWaitForPlayer
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if ( !Director.PlayerReady() )
|
|
{
|
|
doneProcessing = true;
|
|
|
|
ClearWaitFor();
|
|
waitingForPlayer = true;
|
|
|
|
DoMove();
|
|
}
|
|
}
|
|
|
|
void ScriptThread::EventEnd
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ScriptVariable *var;
|
|
const char *text;
|
|
Entity *ent;
|
|
|
|
ClearWaitFor();
|
|
|
|
// If we're a model script, we have to tell our owning entity that we're done.
|
|
if ( ( ev->GetSource() == EV_FROM_SCRIPT ) && ( type == MODEL_SCRIPT ) )
|
|
{
|
|
doneProcessing = true;
|
|
var = Vars()->GetVariable( "self" );
|
|
if ( !var )
|
|
{
|
|
ev->Error( "Model script ending without local.self set" );
|
|
}
|
|
else
|
|
{
|
|
text = var->stringValue();
|
|
if ( ( text[ 0 ] == '*' ) && IsNumeric( &text[ 1 ] ) )
|
|
{
|
|
ent = G_GetEntity( atoi( &text[ 1 ] ) );
|
|
|
|
// pass the event on to entity pointed to by self
|
|
if ( ent )
|
|
{
|
|
ent->ProcessEvent( ev );
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Model script ending. Invalid value in local.self '%s'", text );
|
|
}
|
|
}
|
|
}
|
|
|
|
Director.NotifyOtherThreads( threadNum );
|
|
PostEvent( EV_Remove, 0 );
|
|
doneProcessing = true;
|
|
threadDying = true;
|
|
}
|
|
|
|
void ScriptThread::Print
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.dprintf( "%s", ev->GetString( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::PrintInt
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.dprintf( "%d", ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::PrintFloat
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.dprintf( "%.2f", ev->GetFloat( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::PrintVector
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Vector vec;
|
|
|
|
vec = ev->GetVector( 1 );
|
|
gi.dprintf( "(%.2f %.2f %.2f)", vec.x, vec.y, vec.z );
|
|
}
|
|
|
|
void ScriptThread::NewLine
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.dprintf( "\n" );
|
|
}
|
|
|
|
|
|
void ScriptThread::UserPrint
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.bprintf( PRINT_HIGH, "%s", ev->GetString( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::UserPrintInt
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.bprintf( PRINT_HIGH, "%d", ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::UserPrintFloat
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.bprintf( PRINT_HIGH, "%.2f", ev->GetFloat( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::UserPrintVector
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Vector vec;
|
|
|
|
vec = ev->GetVector( 1 );
|
|
gi.bprintf( PRINT_HIGH, "(%.2f %.2f %.2f)", vec.x, vec.y, vec.z );
|
|
}
|
|
|
|
void ScriptThread::UserNewLine
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.bprintf( PRINT_HIGH, "\n" );
|
|
}
|
|
|
|
void ScriptThread::Assert
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
assert( ev->GetFloat( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::Break
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
// Break into the debugger
|
|
assert( 0 );
|
|
}
|
|
|
|
void ScriptThread::Clear
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ScriptVariableList *vars;
|
|
|
|
vars = Director.GetVarGroup( ev->GetToken( 1 ) );
|
|
if ( vars )
|
|
{
|
|
vars->ClearList();
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ScriptCallback
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
char name[ MAXTOKEN ];
|
|
Entity *other;
|
|
Entity *slave;
|
|
|
|
if ( threadDying )
|
|
{
|
|
return;
|
|
}
|
|
|
|
slave = ev->GetEntity( 1 );
|
|
strcpy( name, ev->GetString( 2 ) );
|
|
other = ev->GetEntity( 3 );
|
|
|
|
if ( !Goto( name ) )
|
|
{
|
|
ev->Error( "Label '%s' not found", name );
|
|
}
|
|
else
|
|
{
|
|
// kill any execute events (in case our last command was "wait")
|
|
ClearWaitFor();
|
|
// start right away
|
|
Start( -1 );
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ThreadCallback
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ScriptThread *thread;
|
|
|
|
if ( threadDying )
|
|
{
|
|
return;
|
|
}
|
|
|
|
thread = ev->GetThread();
|
|
|
|
// should never happen, so catch it during development
|
|
//assert( thread && ( thread == waitingForThread ) );
|
|
|
|
if ( thread && ( thread == waitingForThread ) )
|
|
{
|
|
ClearWaitFor();
|
|
// start right away
|
|
Start( -1 );
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::ConsoleCallback
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if ( threadDying )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClearWaitFor();
|
|
// start right away
|
|
Start( -1 );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::VariableCallback
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ClearWaitFor();
|
|
// start right away
|
|
Start( -1 );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::DeathCallback
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if ( threadDying )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClearWaitFor();
|
|
// start right away
|
|
Start( -1 );
|
|
}
|
|
|
|
|
|
EXPORT_FROM_DLL void ScriptThread::DoMove
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int entnum;
|
|
Entity *ent;
|
|
Event *event;
|
|
int count;
|
|
int i;
|
|
|
|
count = updateList.NumObjects();
|
|
|
|
for( i = 1; i <= count; i++ )
|
|
{
|
|
entnum = ( int )updateList.ObjectAt( i );
|
|
ent = G_GetEntity( entnum );
|
|
if ( ent )
|
|
{
|
|
if ( ent->ValidEvent( EV_ProcessCommands ) )
|
|
{
|
|
event = new Event( EV_ProcessCommands );
|
|
event->SetThread( this );
|
|
ent->PostEvent( event, 0 );
|
|
}
|
|
else
|
|
{
|
|
// try to remove this from the update list
|
|
if ( waitingNumObjects > 0 )
|
|
{
|
|
waitingNumObjects--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
updateList.ClearObjectList();
|
|
}
|
|
|
|
void ScriptThread::TriggerEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const char *name;
|
|
Event *event;
|
|
Entity *ent;
|
|
TargetList *tlist;
|
|
int i;
|
|
int num;
|
|
|
|
name = ev->GetString( 1 );
|
|
|
|
// Check for object commands
|
|
if ( name && name[ 0 ] == '$' )
|
|
{
|
|
tlist = GetTargetList( str( name + 1 ) );
|
|
num = tlist->list.NumObjects();
|
|
for ( i = 1; i <= num; i++ )
|
|
{
|
|
ent = tlist->list.ObjectAt( i );
|
|
|
|
assert( ent );
|
|
|
|
event = new Event( EV_Activate );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddEntity( world );
|
|
ent->ProcessEvent( event );
|
|
}
|
|
}
|
|
else if ( name[ 0 ] == '*' ) // Check for entnum commands
|
|
{
|
|
if ( !IsNumeric( &name[ 1 ] ) )
|
|
{
|
|
ScriptError( "Expecting numeric value for * command, but found '%s'\n", &name[ 1 ] );
|
|
}
|
|
else
|
|
{
|
|
ent = G_GetEntity( atoi( &name[ 1 ] ) );
|
|
if ( ent )
|
|
{
|
|
event = new Event( EV_Activate );
|
|
event->SetSource( EV_FROM_SCRIPT );
|
|
event->SetThread( this );
|
|
event->SetLineNumber( linenumber );
|
|
event->AddEntity( world );
|
|
ent->ProcessEvent( event );
|
|
}
|
|
else
|
|
{
|
|
ScriptError( "Entity not found for * command\n" );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ScriptError( "Invalid entity reference '%s'.\n", name );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::ServerEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int i, argc;
|
|
const char *argv[ MAX_COMMANDS ];
|
|
|
|
argc = 0;
|
|
for ( i = 1; i <= ev->NumArgs(); i++ )
|
|
argv[argc++] = ev->GetString( i );
|
|
|
|
if (argc)
|
|
ProcessCommand( argc, argv );
|
|
}
|
|
|
|
void ScriptThread::ClientEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
//
|
|
// do nothing
|
|
//
|
|
}
|
|
|
|
void ScriptThread::SetLightStyle
|
|
(
|
|
int stylenum,
|
|
const char *stylestring
|
|
)
|
|
|
|
{
|
|
gi.configstring( CS_LIGHTS + stylenum, stylestring );
|
|
}
|
|
|
|
void ScriptThread::EventSetLightStyle
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if ( ev->NumArgs() < 2 )
|
|
{
|
|
ev->Error( "Too few arguments\n" );
|
|
return;
|
|
}
|
|
SetLightStyle( ev->GetInteger( 1 ), ev->GetString( 2 ) );
|
|
}
|
|
|
|
void ScriptThread::CacheModel( Event *ev )
|
|
{
|
|
const char *model;
|
|
|
|
if ( !precache->value )
|
|
return;
|
|
model = ev->GetString( 1 );
|
|
if ( model )
|
|
{
|
|
char str[ 128 ];
|
|
|
|
// Prepend 'models/' to make things easier
|
|
str[ 0 ] = 0;
|
|
if ( !strchr( model, '*' ) && !strchr( model, '\\' ) && !strchr( model, '/' ) )
|
|
{
|
|
strcpy( str, "models/" );
|
|
}
|
|
strcat( str, model );
|
|
gi.modelindex( str );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::CachePlayerModel( Event *ev )
|
|
{
|
|
str model;
|
|
Entity *ent;
|
|
Event *event;
|
|
qboolean forcecache = false;
|
|
|
|
if ( ev->NumArgs() > 1 )
|
|
{
|
|
forcecache = ev->GetInteger( 2 );
|
|
}
|
|
|
|
if ( ( !deathmatch->value && !forcecache ) || !world )
|
|
{
|
|
return;
|
|
}
|
|
|
|
model = ev->GetString( 1 );
|
|
|
|
// Create a new instance of this model and process the commands.
|
|
// This will get the sounds and animations into the system.
|
|
if ( model.length() )
|
|
{
|
|
// Add modelname to the list of valid player models
|
|
game.ValidPlayerModels.AddObject( model );
|
|
|
|
ent = new Entity;
|
|
ent->setModel( model );
|
|
event = new Event( EV_ProcessInitCommands );
|
|
event->AddInteger( ent->edict->s.modelindex );
|
|
ent->ProcessEvent( event );
|
|
}
|
|
|
|
delete ent;
|
|
}
|
|
|
|
void ScriptThread::CacheSound( Event *ev )
|
|
{
|
|
const char *sound;
|
|
|
|
if ( !precache->value )
|
|
return;
|
|
sound = ev->GetString( 1 );
|
|
if ( sound )
|
|
gi.soundindex( sound );
|
|
}
|
|
|
|
void ScriptThread::RegisterAlias
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if (ev->NumArgs() < 3)
|
|
gi.GlobalAlias_Add( ev->GetString( 1 ), ev->GetString( 2 ), 1 );
|
|
else
|
|
gi.GlobalAlias_Add( ev->GetString( 1 ), ev->GetString( 2 ), ev->GetInteger( 3 ) );
|
|
}
|
|
|
|
void ScriptThread::RegisterAliasAndCache
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int length;
|
|
const char * realname;
|
|
|
|
realname = ev->GetString( 2 );
|
|
|
|
if (ev->NumArgs() < 3)
|
|
gi.GlobalAlias_Add( ev->GetString( 1 ), realname, 1 );
|
|
else
|
|
gi.GlobalAlias_Add( ev->GetString( 1 ), realname, ev->GetInteger( 3 ) );
|
|
|
|
if ( !precache->value )
|
|
return;
|
|
|
|
length = strlen( realname );
|
|
if ( (length > 4) && ( !strcmpi( &realname[ length-4 ], ".wav" ) ) )
|
|
gi.soundindex( realname );
|
|
else if ( (length > 4) && ( !strcmpi( &realname[ length-4 ], ".def" ) ) )
|
|
gi.modelindex( realname );
|
|
}
|
|
|
|
void ScriptThread::MapEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
G_BeginIntermission( ev->GetString( 1 ) );
|
|
doneProcessing = true;
|
|
}
|
|
|
|
void ScriptThread::SetCvarEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str name;
|
|
|
|
name = ev->GetString( 1 );
|
|
if ( name != "" )
|
|
{
|
|
gi.cvar_set( name.c_str(), ev->GetString( 2 ) );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::CueCamera
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *ent;
|
|
|
|
ent = ev->GetEntity( 1 );
|
|
if ( ent )
|
|
{
|
|
SetCamera( ent );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Camera named %s not found", ev->GetString( 1 ) );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::CuePlayer
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
SetCamera( NULL );
|
|
}
|
|
|
|
void ScriptThread::FreezePlayer
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.playerfrozen = true;
|
|
}
|
|
|
|
void ScriptThread::ReleasePlayer
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.playerfrozen = false;
|
|
}
|
|
|
|
void ScriptThread::AirClamp
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.airclamp = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void ScriptThread::Spawn
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *ent;
|
|
Entity *tent;
|
|
const char *name;
|
|
ClassDef *cls;
|
|
char text[ 128 ];
|
|
int n;
|
|
int i;
|
|
int num;
|
|
const char *targetname;
|
|
ScriptVariable *var;
|
|
|
|
if ( ev->NumArgs() < 1 )
|
|
{
|
|
ev->Error( "Usage: spawn entityname [keyname] [value]..." );
|
|
return;
|
|
}
|
|
|
|
// create a new entity
|
|
G_InitSpawnArguments();
|
|
|
|
name = ev->GetString( 1 );
|
|
|
|
if ( name )
|
|
{
|
|
cls = getClassForID( name );
|
|
if ( !cls )
|
|
{
|
|
cls = getClass( name );
|
|
}
|
|
|
|
if ( !cls )
|
|
{
|
|
str n;
|
|
|
|
n = name;
|
|
if ( !strstr( n.c_str(), ".def" ) )
|
|
{
|
|
n += ".def";
|
|
}
|
|
G_SetSpawnArg( "model", n.c_str() );
|
|
}
|
|
else
|
|
{
|
|
G_SetSpawnArg( "classname", name );
|
|
}
|
|
}
|
|
|
|
if ( ev->NumArgs() > 2 )
|
|
{
|
|
n = ev->NumArgs();
|
|
for( i = 2; i <= n; i += 2 )
|
|
{
|
|
G_SetSpawnArg( ev->GetString( i ), ev->GetString( i + 1 ) );
|
|
}
|
|
}
|
|
|
|
cls = G_GetClassFromArgs();
|
|
if ( !cls )
|
|
{
|
|
ev->Error( "'%s' is not a valid entity name", name );
|
|
return;
|
|
}
|
|
|
|
// If there is a target set, then use that entity's origin and angles
|
|
targetname = G_GetSpawnArg("target", NULL);
|
|
|
|
if ( targetname )
|
|
{
|
|
num = G_FindTarget( 0, targetname );
|
|
if ( num )
|
|
{
|
|
tent = G_GetEntity( num );
|
|
if (tent)
|
|
{
|
|
sprintf( text, "%f %f %f", tent->worldorigin[ 0 ], tent->worldorigin[ 1 ], tent->worldorigin[ 2 ] );
|
|
G_SetSpawnArg( "origin", text );
|
|
sprintf( text, "%f", tent->angles[1] );
|
|
G_SetSpawnArg( "angle", text );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Can't find entity with targetname %s", targetname );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Can't find targetname %s", targetname );
|
|
}
|
|
}
|
|
|
|
ent = ( Entity * )cls->newInstance();
|
|
var = Director.GetVariable( "parm.lastspawn" );
|
|
var->setIntValue( ent->entnum );
|
|
G_InitSpawnArguments();
|
|
}
|
|
|
|
|
|
void ScriptThread::DialogEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str name;
|
|
float volume;
|
|
int channel;
|
|
float attenuation;
|
|
float pitch;
|
|
float timeofs;
|
|
float fadetime;
|
|
int flags;
|
|
int i;
|
|
str icon_name;
|
|
str dialog_text;
|
|
|
|
// Check to send text
|
|
if ( ( dialog->value == 1 ) || ( dialog->value == 3 ) )
|
|
{
|
|
icon_name = ev->GetString( 1 );
|
|
dialog_text = ev->GetString( 2 );
|
|
SendDialog( icon_name.c_str(), dialog_text.c_str() );
|
|
}
|
|
|
|
// Check to send speech
|
|
if ( ( dialog->value == 0 ) || ( dialog->value == 1 ) )
|
|
return;
|
|
|
|
//
|
|
// set defaults
|
|
//
|
|
volume = 1.0f;
|
|
channel = CHAN_DIALOG | CHAN_NO_PHS_ADD;
|
|
attenuation = ATTN_NORM;
|
|
pitch = 1.0f;
|
|
timeofs = 0;
|
|
fadetime = 0;
|
|
flags = SOUND_SYNCH | SOUND_LOCAL_DIALOG;
|
|
for ( i = 3 ; i <= ev->NumArgs() ; i++ )
|
|
{
|
|
switch (i-3)
|
|
{
|
|
case 0:
|
|
name = ev->GetString( i );
|
|
break;
|
|
case 1:
|
|
volume = ev->GetFloat( i );
|
|
break;
|
|
case 2:
|
|
channel = ev->GetInteger( i );
|
|
break;
|
|
case 3:
|
|
attenuation = ev->GetFloat( i );
|
|
break;
|
|
case 4:
|
|
pitch = ev->GetFloat( i );
|
|
break;
|
|
case 5:
|
|
timeofs = ev->GetFloat( i );
|
|
break;
|
|
case 6:
|
|
fadetime = ev->GetFloat( i );
|
|
break;
|
|
case 7:
|
|
flags = ev->GetInteger( i );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
channel |= CHAN_NO_PHS_ADD;
|
|
|
|
if ( name.length() )
|
|
{
|
|
//
|
|
// we put in zero here because we aren't an entity
|
|
//
|
|
gi.sound( &g_edicts[ 0 ], channel, gi.soundindex( name.c_str() ), volume,
|
|
attenuation, timeofs, pitch, fadetime, flags );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Null sound specified." );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::CrucialDialogEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str name;
|
|
float volume;
|
|
int channel;
|
|
float attenuation;
|
|
float pitch;
|
|
float timeofs;
|
|
float fadetime;
|
|
int flags;
|
|
int i;
|
|
str icon_name;
|
|
str dialog_text;
|
|
|
|
// Check to send text
|
|
icon_name = ev->GetString( 1 );
|
|
dialog_text = ev->GetString( 2 );
|
|
SendDialog( icon_name.c_str(), dialog_text.c_str() );
|
|
|
|
//
|
|
// set defaults
|
|
//
|
|
volume = 1.0f;
|
|
channel = CHAN_DIALOG | CHAN_NO_PHS_ADD;
|
|
attenuation = ATTN_NORM;
|
|
pitch = 1.0f;
|
|
timeofs = 0;
|
|
fadetime = 0;
|
|
flags = SOUND_SYNCH | SOUND_LOCAL_DIALOG;
|
|
for ( i = 3 ; i <= ev->NumArgs() ; i++ )
|
|
{
|
|
switch (i-3)
|
|
{
|
|
case 0:
|
|
name = ev->GetString( i );
|
|
break;
|
|
case 1:
|
|
volume = ev->GetFloat( i );
|
|
break;
|
|
case 2:
|
|
channel = ev->GetInteger( i );
|
|
break;
|
|
case 3:
|
|
attenuation = ev->GetFloat( i );
|
|
break;
|
|
case 4:
|
|
pitch = ev->GetFloat( i );
|
|
break;
|
|
case 5:
|
|
timeofs = ev->GetFloat( i );
|
|
break;
|
|
case 6:
|
|
fadetime = ev->GetFloat( i );
|
|
break;
|
|
case 7:
|
|
flags = ev->GetInteger( i );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
channel |= CHAN_NO_PHS_ADD;
|
|
|
|
if ( name.length() )
|
|
{
|
|
//
|
|
// we put in zero here because we aren't an entity
|
|
//
|
|
gi.sound( &g_edicts[ 0 ], channel, gi.soundindex( name.c_str() ), volume,
|
|
attenuation, timeofs, pitch, fadetime, flags );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Null sound specified." );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::DialogSoundEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str name;
|
|
float volume;
|
|
int channel;
|
|
float attenuation;
|
|
float pitch;
|
|
float timeofs;
|
|
float fadetime;
|
|
int flags;
|
|
int i;
|
|
|
|
// Check to send speech
|
|
if ( ( dialog->value == 0 ) || ( dialog->value == 1 ) )
|
|
return;
|
|
|
|
//
|
|
// set defaults
|
|
//
|
|
volume = 1.0f;
|
|
channel = CHAN_DIALOG | CHAN_NO_PHS_ADD;
|
|
attenuation = ATTN_NORM;
|
|
pitch = 1.0f;
|
|
timeofs = 0;
|
|
fadetime = 0;
|
|
flags = SOUND_SYNCH | SOUND_LOCAL_DIALOG;
|
|
for ( i = 1 ; i <= ev->NumArgs() ; i++ )
|
|
{
|
|
switch (i-1)
|
|
{
|
|
case 0:
|
|
name = ev->GetString( i );
|
|
break;
|
|
case 1:
|
|
volume = ev->GetFloat( i );
|
|
break;
|
|
case 2:
|
|
channel = ev->GetInteger( i );
|
|
break;
|
|
case 3:
|
|
attenuation = ev->GetFloat( i );
|
|
break;
|
|
case 4:
|
|
pitch = ev->GetFloat( i );
|
|
break;
|
|
case 5:
|
|
timeofs = ev->GetFloat( i );
|
|
break;
|
|
case 6:
|
|
fadetime = ev->GetFloat( i );
|
|
break;
|
|
case 7:
|
|
flags = ev->GetInteger( i );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
channel |= CHAN_NO_PHS_ADD;
|
|
|
|
if ( name.length() )
|
|
{
|
|
//
|
|
// we put in zero here because we aren't an entity
|
|
//
|
|
gi.sound( &g_edicts[ 0 ], channel, gi.soundindex( name.c_str() ), volume,
|
|
attenuation, timeofs, pitch, fadetime, flags );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Null sound specified." );
|
|
}
|
|
}
|
|
|
|
|
|
void ScriptThread::SetDialogScript
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ScriptThread * pThread;
|
|
ScriptLib.SetDialogScript( ev->GetString( 1 ) );
|
|
pThread = Director.CreateThread( &script, "dialog::precache" );
|
|
if ( pThread )
|
|
{
|
|
// start right away
|
|
pThread->Start( -1 );
|
|
}
|
|
}
|
|
|
|
void ScriptThread::FadeIn
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
float r,g,b,time;
|
|
|
|
time = ev->GetFloat(1);
|
|
r = ev->GetFloat(2);
|
|
g = ev->GetFloat(3);
|
|
b = ev->GetFloat(4);
|
|
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "fi %0.2f %0.2f %0.2f %0.2f",time,r,g,b ) );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
}
|
|
|
|
void ScriptThread::FadeOut
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
float r,g,b,time;
|
|
|
|
time = ev->GetFloat(1);
|
|
r = ev->GetFloat(2);
|
|
g = ev->GetFloat(3);
|
|
b = ev->GetFloat(4);
|
|
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "fo %0.2f %0.2f %0.2f %0.2f",time,r,g,b ) );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
}
|
|
|
|
void ScriptThread::Hud
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int val;
|
|
int j;
|
|
edict_t *other;
|
|
|
|
val = !ev->GetInteger( 1 );
|
|
|
|
for( j = 1; j <= game.maxclients; j++ )
|
|
{
|
|
other = &g_edicts[ j ];
|
|
if ( other->inuse && other->client )
|
|
{
|
|
Player * client;
|
|
Event * ev;
|
|
|
|
client = ( Player * )other->entity;
|
|
if ( val )
|
|
ev = new Event( EV_Player_HideStats );
|
|
else
|
|
ev = new Event( EV_Player_DrawStats );
|
|
client->ProcessEvent( ev );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptThread::Overlay
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int val;
|
|
int j;
|
|
edict_t *other;
|
|
|
|
val = ev->GetInteger( 1 );
|
|
|
|
for( j = 1; j <= game.maxclients; j++ )
|
|
{
|
|
other = &g_edicts[ j ];
|
|
if ( other->inuse && other->client )
|
|
{
|
|
Player * client;
|
|
Event * ev;
|
|
|
|
client = ( Player * )other->entity;
|
|
if ( val )
|
|
ev = new Event( EV_Player_DrawOverlay );
|
|
else
|
|
ev = new Event( EV_Player_HideOverlay );
|
|
client->ProcessEvent( ev );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptThread::LoadOverlay
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
SendOverlay( NULL, str( ev->GetString( 1 ) ) );
|
|
}
|
|
|
|
void ScriptThread::LoadIntermission
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
SendIntermission( NULL, str( ev->GetString( 1 ) ) );
|
|
}
|
|
|
|
void ScriptThread::IntermissionLayout
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str layout;
|
|
|
|
layout = ev->GetString( 1 );
|
|
|
|
if (layout.length() > (MAX_LAYOUT_LENGTH - 5))
|
|
{
|
|
error("ScriptThread::IntermissionLayout", "Max layout length exceeded for intermission layout.\n" );
|
|
}
|
|
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "iml %s", layout.c_str() ) );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
}
|
|
|
|
void ScriptThread::MusicEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
const char *current;
|
|
const char *fallback;
|
|
|
|
current = NULL;
|
|
fallback = NULL;
|
|
current = ev->GetString( 1 );
|
|
|
|
if ( ev->NumArgs() > 1 )
|
|
fallback = ev->GetString( 2 );
|
|
|
|
ChangeMusic( current, fallback, false );
|
|
}
|
|
|
|
void ScriptThread::ForceMusicEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
const char *current;
|
|
const char *fallback;
|
|
|
|
current = NULL;
|
|
fallback = NULL;
|
|
current = ev->GetString( 1 );
|
|
|
|
if ( ev->NumArgs() > 1 )
|
|
fallback = ev->GetString( 2 );
|
|
|
|
ChangeMusic( current, fallback, true );
|
|
}
|
|
|
|
void ScriptThread::SoundtrackEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
{
|
|
ChangeSoundtrack( ev->GetString( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::MenuEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.AddCommandString( va( "menu_load %s; menu_generic\n", ev->GetString( 1) ) );
|
|
}
|
|
|
|
void ScriptThread::SetCinematic
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.cinematic = true;
|
|
}
|
|
|
|
void ScriptThread::SetNonCinematic
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.cinematic = false;
|
|
}
|
|
|
|
void ScriptThread::SetSkipThread
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
world->skipthread = ev->GetString( 1 );
|
|
}
|
|
|
|
void ScriptThread::ScreenPrint
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "spr %f %s\n", ev->GetFloat( 1 ), ev->GetString( 2 ) ) );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
}
|
|
|
|
void ScriptThread::ScreenPrintFile
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str path;
|
|
|
|
path = ev->GetString( 1 );
|
|
G_FixSlashes( path.c_str() );
|
|
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "spf %s\n", path.c_str() ) );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
}
|
|
|
|
void ScriptThread::ClearScreenPrintFile
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str path;
|
|
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "cspf\n" ) );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
}
|
|
|
|
void ScriptThread::JC_Hearable
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.no_jc = false;
|
|
}
|
|
|
|
void ScriptThread::JC_Not_Hearable
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.no_jc = true;
|
|
}
|
|
|
|
void ScriptThread::MissionFailed
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int i;
|
|
|
|
ChangeMusic( "failure", "normal", true );
|
|
|
|
for( i = 1; i <= game.maxclients; i++ )
|
|
{
|
|
if ( g_edicts[ i ].inuse )
|
|
{
|
|
gi.centerprintf ( &g_edicts[ i ], "jcx yv 20 string \"Mission Failed.\"" );
|
|
if ( g_edicts[ i ].entity )
|
|
{
|
|
g_edicts[ i ].entity->PostEvent( EV_Player_Respawn, 3.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
level.missionfailed = true;
|
|
level.missionfailedtime = level.time + 3;
|
|
}
|
|
|
|
void ScriptThread::PassToPathmanager
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
PathManager.ProcessEvent( ev );
|
|
}
|
|
|
|
|
|
void ScriptThread::StuffCommand
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.AddCommandString( va( "%s\n", ev->GetString( 1 ) ) );
|
|
}
|
|
|
|
void ScriptThread::Training
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.training = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void ScriptThread::ClearSaveGames
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
level.clearsavegames = true;
|
|
}
|
|
|
|
void ScriptThread::KillEnt
|
|
(
|
|
Event * ev
|
|
)
|
|
{
|
|
int num;
|
|
Entity *ent;
|
|
|
|
if ( ev->NumArgs() != 1 )
|
|
{
|
|
ev->Error( "No args passed in" );
|
|
return;
|
|
}
|
|
|
|
num = ev->GetInteger( 1 );
|
|
if ( ( num < 0 ) || ( num >= globals.max_edicts ) )
|
|
{
|
|
ev->Error( "Value out of range. Possible values range from 0 to %d.\n", globals.max_edicts );
|
|
return;
|
|
}
|
|
|
|
ent = G_GetEntity( num );
|
|
ent->Damage( world, world, ent->max_health + 25, vec_zero, vec_zero, vec_zero, 0, 0, 0, -1, -1, 1 );
|
|
}
|
|
|
|
void ScriptThread::RemoveEnt
|
|
(
|
|
Event * ev
|
|
)
|
|
{
|
|
int num;
|
|
Entity *ent;
|
|
|
|
if ( ev->NumArgs() != 1 )
|
|
{
|
|
ev->Error( "No args passed in" );
|
|
return;
|
|
}
|
|
|
|
num = ev->GetInteger( 1 );
|
|
if ( ( num < 0 ) || ( num >= globals.max_edicts ) )
|
|
{
|
|
ev->Error( "Value out of range. Possible values range from 0 to %d.\n", globals.max_edicts );
|
|
return;
|
|
}
|
|
|
|
ent = G_GetEntity( num );
|
|
ent->PostEvent( Event( EV_Remove ), 0 );
|
|
}
|
|
|
|
void ScriptThread::KillClass
|
|
(
|
|
Event * ev
|
|
)
|
|
{
|
|
int except;
|
|
str classname;
|
|
edict_t * from;
|
|
Entity *ent;
|
|
|
|
if ( ev->NumArgs() < 1 )
|
|
{
|
|
ev->Error( "No args passed in" );
|
|
return;
|
|
}
|
|
|
|
classname = ev->GetString( 1 );
|
|
|
|
except = 0;
|
|
if ( ev->NumArgs() == 2 )
|
|
{
|
|
except = ev->GetInteger( 1 );
|
|
}
|
|
|
|
for ( from = &g_edicts[ game.maxclients + 1 ]; from < &g_edicts[ globals.num_edicts ]; from++ )
|
|
{
|
|
if ( !from->inuse )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
assert( from->entity );
|
|
|
|
ent = from->entity;
|
|
|
|
if ( ent->entnum == except )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( ent->inheritsFrom( classname.c_str() ) )
|
|
{
|
|
ent->Damage( world, world, ent->max_health + 25, vec_zero, vec_zero, vec_zero, 0, 0, 0, -1, -1, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptThread::RemoveClass
|
|
(
|
|
Event * ev
|
|
)
|
|
{
|
|
int except;
|
|
str classname;
|
|
edict_t * from;
|
|
Entity *ent;
|
|
|
|
if ( ev->NumArgs() < 1 )
|
|
{
|
|
ev->Error( "No args passed in" );
|
|
return;
|
|
}
|
|
|
|
classname = ev->GetString( 1 );
|
|
|
|
except = 0;
|
|
if ( ev->NumArgs() == 2 )
|
|
{
|
|
except = ev->GetInteger( 1 );
|
|
}
|
|
|
|
for ( from = &g_edicts[ game.maxclients + 1 ]; from < &g_edicts[ globals.num_edicts ]; from++ )
|
|
{
|
|
if ( !from->inuse )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
assert( from->entity );
|
|
|
|
ent = from->entity;
|
|
|
|
if ( ent->entnum == except )
|
|
continue;
|
|
|
|
if ( ent->inheritsFrom( classname.c_str() ) )
|
|
{
|
|
ent->PostEvent( Event( EV_Remove ), 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptThread::MapName
|
|
(
|
|
Event * ev
|
|
)
|
|
|
|
{
|
|
gi.configstring( CS_NAME, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void ScriptThread::EndGame
|
|
(
|
|
Event * ev
|
|
)
|
|
|
|
{
|
|
#ifdef SIN_ARCADE
|
|
gi.WriteByte( svc_stufftext );
|
|
gi.WriteString( "gameover" );
|
|
gi.multicast( NULL, MULTICAST_ALL );
|
|
#endif
|
|
}
|
|
|
|
void ScriptThread::CameraCommand
|
|
(
|
|
Event * ev
|
|
)
|
|
|
|
{
|
|
Event *e;
|
|
const char *cmd;
|
|
int i;
|
|
int n;
|
|
|
|
if ( !ev->NumArgs() )
|
|
{
|
|
ev->Error( "Usage: cam [command] [arg 1]...[arg n]" );
|
|
return;
|
|
}
|
|
|
|
cmd = ev->GetString( 1 );
|
|
if ( Event::Exists( cmd ) )
|
|
{
|
|
e = new Event( cmd );
|
|
e->SetSource( EV_FROM_SCRIPT );
|
|
e->SetThread( this );
|
|
e->SetLineNumber( linenumber );
|
|
|
|
n = ev->NumArgs();
|
|
for( i = 2; i <= n; i++ )
|
|
{
|
|
e->AddToken( ev->GetToken( i ) );
|
|
}
|
|
|
|
CameraMan.ProcessEvent( e );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Unknown camera command '%s'.\n", cmd );
|
|
}
|
|
}
|