//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/scriptmaster.cpp $ // $Revision:: 13 $ // $Author:: Steven $ // $Date:: 10/04/02 1:32p $ // // 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. // // // 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 "_pch_cpp.h" #include "class.h" #include "scriptmaster.h" #include "container.h" #include "scriptvariable.h" #include "misc.h" #include "specialfx.h" #include "worldspawn.h" #include "program.h" #include "interpreter.h" #include "globalcmd.h" Program program; ScriptVariableList gameVars; ScriptVariableList levelVars; //static ScriptVariablePtr GameTime = NULL; /* Event EV_ProcessCommands ( "processCommands", EV_DEFAULT, NULL, NULL, "Not used." ); Event EV_Script_NewOrders ( "newOrders", EV_DEFAULT, NULL, NULL, "Inform script that it is about to get new orders." ); Event EV_ScriptThread_Execute ( "execute", EV_DEFAULT, NULL, NULL, "Execute the thread." ); Event EV_ScriptThread_Callback ( "script_callback", EV_DEFAULT, "ese", "slave name other", "Script callback." ); Event EV_ScriptThread_ThreadCallback ( "thread_callback", EV_DEFAULT, NULL, NULL, "Thread callback." ); Event EV_ScriptThread_VariableCallback ( "variable_callback", EV_DEFAULT, NULL, NULL, "Variable callback." ); Event EV_ScriptThread_DeathCallback ( "death_callback", EV_DEFAULT, NULL, NULL, "Death callback." ); Event EV_ScriptThread_CreateThread ( "thread", EV_DEFAULT, "s", "label", "Creates a thread starting at label." ); Event EV_ScriptThread_TerminateThread ( "terminate", EV_DEFAULT, "i", "thread_number", "Terminates the specified thread or the current one if none specified." ); Event EV_ScriptThread_ControlObject ( "control", EV_DEFAULT, "e", "object", "Not used." ); Event EV_ScriptThread_Goto ( "goto", EV_DEFAULT, "s", "label", "Goes to the specified label." ); Event EV_ScriptThread_Pause ( "pause", EV_DEFAULT, NULL, NULL, "Pauses the thread." ); Event EV_ScriptThread_Wait ( "wait", EV_DEFAULT, "f", "wait_time", "Wait for the specified amount of time." ); Event EV_ScriptThread_WaitFor ( "waitFor", EV_DEFAULT, "e", "ent", "Wait for the specified entity." ); Event EV_ScriptThread_WaitForThread ( "waitForThread", EV_DEFAULT, "i", "thread_number", "Wait for the specified thread." ); Event EV_ScriptThread_WaitForVariable ( "waitForVariable", EV_DEFAULT, "s", "variable_name", "Wait for the specified variable." ); Event EV_ScriptThread_WaitForDeath ( "waitForDeath", EV_DEFAULT, "s", "name", "Wait for death." ); Event EV_ScriptThread_WaitForSound ( "waitForSound", EV_DEFAULT, "sf", "sound_name delay", "Wait for end of the named sound plus an optional delay." ); Event EV_ScriptThread_WaitForDialog ( "waitForDialog", EV_DEFAULT, "eF", "actor additional_delay", "Wait for end of the dialog for the specified actor.\n" "If additional_delay is specified, than the actor will\n" "wait the dialog length plus the delay." ); Event EV_ScriptThread_WaitForPlayer ( "waitForPlayer", EV_DEFAULT, NULL, NULL, "Wait for player to be ready." ); Event EV_ScriptThread_End ( "end", EV_DEFAULT, NULL, NULL, "End the thread." ); Event EV_ScriptThread_Print ( "print", EV_DEFAULT, "s", "string", "Prints a string." ); Event EV_ScriptThread_PrintInt ( "printint", EV_DEFAULT, "i", "integer", "Print integer." ); Event EV_ScriptThread_PrintFloat ( "printfloat", EV_DEFAULT, "f", "float", "Prints a float."); Event EV_ScriptThread_PrintVector ( "printvector", EV_DEFAULT, "v", "vector", "Prints a vector." ); Event EV_ScriptThread_NewLine ( "newline", EV_DEFAULT, NULL, NULL, "Prints a newline." ); Event EV_ScriptThread_Assert ( "assert", EV_DEFAULT, "f", "value", "Assert if value is 0." ); Event EV_ScriptThread_Break ( "break", EV_DEFAULT, NULL, NULL, "Asserts so that we can break back into the debugger from script." ); Event EV_ScriptThread_Clear ( "clear", EV_DEFAULT, "s", "var_group", "Clears the specified var group." ); Event EV_ScriptThread_Trigger ( "trigger", EV_DEFAULT, "s", "name", "Trigger the specified target or entity." ); Event EV_ScriptThread_Spawn ( "spawn", EV_CHEAT, "sSSSSSSSS", "entityname keyname1 value1 keyname2 value2 keyname3 value3 keyname4 value4", "Spawn the specified entity." ); Event EV_ScriptThread_Map ( "map", EV_DEFAULT, "s", "map_name", "Starts the specified map." ); Event EV_ScriptThread_SetCvar ( "setcvar", EV_DEFAULT, "ss", "cvar_name value", "Sets the value of the specified cvar." ); Event EV_ScriptThread_CueCamera ( "cuecamera", EV_DEFAULT, "eF", "entity switchTime", "Cue the camera. If switchTime is specified, then the camera\n" "will switch over that length of time." ); Event EV_ScriptThread_CuePlayer ( "cueplayer", EV_DEFAULT, "F", "switchTime", "Go back to the normal camera. If switchTime is specified,\n" "then the camera will switch over that length of time." ); Event EV_ScriptThread_FreezePlayer ( "freezeplayer", EV_DEFAULT, NULL, NULL, "Freeze the player." ); Event EV_ScriptThread_ReleasePlayer ( "releaseplayer", EV_DEFAULT, NULL, NULL, "Release the player." ); Event EV_ScriptThread_FakePlayer ( "fakeplayer", EV_DEFAULT, NULL, NULL, "Will create a fake version of the player, hide the real one and\n" "call the fake one 'fakeplayer'." ); Event EV_ScriptThread_RemoveFakePlayer ( "removefakeplayer", EV_DEFAULT, NULL, NULL, "Will delete the fake version of the player, un-hide the real one and\n" "return to the normal game" ); Event EV_ScriptThread_KillEnt ( "killent", EV_CHEAT, "i", "ent_num", "Kill the specified entity." ); Event EV_ScriptThread_KillClass ( "killclass", EV_CHEAT, "sI", "class_name except", "Kills everything in the specified class except for the specified entity (optional)." ); Event EV_ScriptThread_RemoveEnt ( "removeent", EV_CHEAT, "i", "ent_num", "Removes the specified entity." ); Event EV_ScriptThread_RemoveClass ( "removeclass", EV_CHEAT, "sI", "class_name except", "Removes everything in the specified class except for the specified entity (optional)." ); // client/server flow control Event EV_ScriptThread_ServerOnly ( "server", EV_DEFAULT, "SSSSSS", "arg1 arg2 arg3 arg4 arg5 arg6", "Server only command." ); Event EV_ScriptThread_ClientOnly ( "client", EV_DEFAULT, "SSSSSS", "arg1 arg2 arg3 arg4 arg5 arg6", "Client only command." ); Event EV_ScriptThread_StuffCommand ( "stuffcmd", EV_DEFAULT, "SSSSSS", "arg1 arg2 arg3 arg4 arg5 arg6", "Server only command." ); // // world stuff // Event EV_ScriptThread_RegisterAlias ( "alias", EV_DEFAULT, "ssSSSS", "alias_name real_name arg1 arg2 arg3 arg4", "Sets up an alias." ); Event EV_ScriptThread_RegisterAliasAndCache ( "aliascache", EV_DEFAULT, "ssSSSS", "alias_name real_name arg1 arg2 arg3 arg4", "Sets up an alias and caches the resourse." ); Event EV_ScriptThread_SetCinematic ( "cinematic", EV_DEFAULT, NULL, NULL, "Turns on cinematic." ); Event EV_ScriptThread_SetNonCinematic ( "noncinematic", EV_DEFAULT, NULL, NULL, "Turns off cinematic." ); Event EV_ScriptThread_SetAllAIOff ( "all_ai_off", EV_DEFAULT, NULL, NULL, "Turns all AI off." ); Event EV_ScriptThread_SetAllAIOn ( "all_ai_on", EV_DEFAULT, NULL, NULL, "Turns all AI back on." ); Event EV_ScriptThread_SetSkipThread ( "skipthread", EV_DEFAULT, "s", "thread_name", "Set the thread to skip." ); // Precache specific Event EV_ScriptThread_Precache_Cache ( "cache", EV_DEFAULT, "s", "resource_name", "Cache the specified resource." ); // fades for movies Event EV_ScriptThread_FadeIn ( "fadein", EV_DEFAULT, "fffffI", "time red green blue alpha mode", "Sets up fadein in values." ); Event EV_ScriptThread_FadeOut ( "fadeout", EV_DEFAULT, "fffffI", "time red green blue alpha mode", "Sets up fadeout values." ); Event EV_ScriptThread_FadeSound ( "fadesound", EV_DEFAULT, "f", "time", "fades the sound out over the given time." ); Event EV_ScriptThread_CameraCommand ( "cam", EV_DEFAULT, "sSSSSSS", "command arg1 arg2 arg3 arg4 arg5 arg6", "Processes a camera command." ); // music command Event EV_ScriptThread_MusicEvent ( "music", EV_DEFAULT, "sS", "current fallback", "Sets the current and fallback (optional) music moods." ); Event EV_ScriptThread_ForceMusicEvent ( "forcemusic", EV_DEFAULT, "sS", "current fallback", "Forces the current and fallback (optional) music moods." ); Event EV_ScriptThread_MusicVolumeEvent ( "musicvolume", EV_DEFAULT, "ff", "volume fade_time", "Sets the volume and fade time of the music." ); Event EV_ScriptThread_RestoreMusicVolumeEvent ( "restoremusicvolume", EV_DEFAULT, "f", "fade_time", "Restores the music volume to its previous value." ); Event EV_ScriptThread_SoundtrackEvent ( "soundtrack", EV_DEFAULT, "s", "soundtrack_name", "Changes the soundtrack." ); Event EV_ScriptThread_RestoreSoundtrackEvent ( "restoresoundtrack", EV_DEFAULT, NULL, NULL, "Restores the soundtrack to the previous one." ); Event EV_ScriptThread_ClearFade ( "clearfade", EV_DEFAULT, NULL, NULL, "Clear the fade from the screen" ); Event EV_ScriptThread_Letterbox ( "letterbox", EV_DEFAULT, "f", "time", "Puts the game in letterbox mode." ); Event EV_ScriptThread_ClearLetterbox ( "clearletterbox", EV_DEFAULT, "f", "time", "Clears letterbox mode." ); Event EV_ScriptThread_SetDialogScript ( "setdialogscript" , EV_DEFAULT, "s", "dialog_script", "Set the script to be used when dialog:: is used" ); Event EV_ScriptThread_SetLightStyle ( "setlightstyle" , EV_DEFAULT, "is", "lightstyleindex lightstyledata", "Set up the lightstyle with lightstyleindex to the specified data" ); Event EV_ScriptThread_KillThreadEvent ( "killthread", EV_DEFAULT, "s", "threadName", "kills all threads starting with the specified name" ); Event EV_ScriptThread_SetThreadNameEvent ( "threadname", EV_DEFAULT, "s", "threadName", "sets the name of the thread" ); Event EV_ScriptThread_CenterPrint ( "centerprint", EV_DEFAULT, "s", "stuffToPrint", "prints the included message in the middle of all player's screens" ); Event EV_ScriptThread_LocationPrint ( "locprint", EV_DEFAULT, "iis", "xoffset yoffset stuffToPrint", "prints the included message in the specified location of all player's screens" ); Event EV_ScriptThread_MissionFailed ( "missionfailed", EV_DEFAULT, NULL, NULL, "Makes the player fail their mission, level restarts." ); Event EV_ScriptThread_ArenaCommand ( "arena", EV_DEFAULT, "dsSSSSSS", "arenanum command arg1 arg2 arg3 arg4 arg5 arg6", "Send a command to the specified arena" ); CLASS_DECLARATION( Class, ThreadMarker, NULL ) { { NULL, NULL } }; */ ScriptMaster Director; CLASS_DECLARATION( Listener, ScriptMaster, NULL ) { { NULL, NULL } }; ScriptMaster::ScriptMaster() { threadIndex = 0; currentThread = NULL; player_ready = false; } ScriptMaster::~ScriptMaster() { /* if ( GameTime ) { gameVars.RemoveVariable( GameTime ); GameTime = NULL; } */ CloseScript(); } void ScriptMaster::CloseScript( void ) { KillThreads(); ScriptLib.CloseScripts(); /* if ( GameTime ) { gameVars.RemoveVariable( GameTime ); GameTime = NULL; } */ } 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; } qboolean ScriptMaster::NotifyOtherThreads( int num ) { CThread *thread1; CThread *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; } void ScriptMaster::DeathMessage( const char *name ) { CThread *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); } } } } } void ScriptMaster::PlayerSpawned( void ) { CThread *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->DelayedStart( 0.0f ); } } } } void ScriptMaster::KillThread( const str &name ) { int i; int num; int len; const char * ptr; CThread *thread; bool useWildCard; useWildCard = false; len = 0; // see if the name uses a wild card ptr = strchr( name.c_str(), '*' ); if ( ptr ) { len = ptr - name.c_str(); useWildCard = true; } // kill only those threads whose name matches name num = Threads.NumObjects(); // Clear the waitfor's from each thread before deleting // so they don't get triggered. for( i = num; i > 0; i-- ) { thread = Threads.ObjectAt( i ); int result; //With the wild card used, we want to delete any matches of the thread //name if(useWildCard == true) { result = strnicmp( thread->ThreadName(), name.c_str(), len ); } else { //if the wild card is not active, then we want the exact thread //name result = stricmp( thread->ThreadName(), name.c_str()); } //if the result is 0, then we have found a thread to delete. if ( result == 0 ) { thread->ClearWaitFor(); if ( currentThread == thread ) { SetCurrentThread( NULL ); } delete thread; } } } qboolean ScriptMaster::KillThread( int num ) { CThread *thread; // Must be safely reentryable so that the thread destructor can tell us that it's being deleted. thread = GetThread( num ); if ( thread ) { thread->ClearWaitFor(); if ( currentThread == thread ) { SetCurrentThread( NULL ); } delete thread; return true; } return false; } qboolean ScriptMaster::RemoveThread( int num ) { CThread *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 = n; i > 0; i-- ) { thread = Threads.ObjectAt( i ); assert( thread ); if ( thread ) { if ( thread->ThreadNum() == num ) { Threads.ObjectAt( i )->ClearWaitFor(); Threads.RemoveObjectAt( i ); if ( currentThread == thread ) { SetCurrentThread( NULL ); } return true; } } } return false; } CThread *ScriptMaster::CurrentThread( void ) { return currentThread; } void ScriptMaster::SetCurrentThread( CThread *thread ) { currentThread = thread; } CThread *ScriptMaster::CreateThread( Program *program ) { CThread *thread; thread = new CThread; thread->program = program; Threads.AddObject( thread ); return thread; } CThread *ScriptMaster::CreateThread( const char *label, Program *program ) { CThread *thread; int threadnum; thread = new CThread; thread->program = program; threadnum = GetUniqueThreadNumber(); Threads.AddObject( thread ); if ( !thread->Setup( threadnum, label ) ) { KillThread( threadnum ); return NULL; } return thread; } CThread *ScriptMaster::CreateThread( void ) { CThread *thread; thread = new CThread; thread->SetThreadNum( GetUniqueThreadNumber() ); Threads.AddObject( thread ); return thread; } CThread *ScriptMaster::CreateThread( const char *label ) { CThread *thread; int threadnum; thread = new CThread; threadnum = GetUniqueThreadNumber(); Threads.AddObject( thread ); if ( !thread->Setup( threadnum, label ) ) { KillThread( threadnum ); return NULL; } return thread; } CThread *ScriptMaster::GetThread( int num ) { int i; int n; CThread *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; } int ScriptMaster::GetUniqueThreadNumber( void ) { do { threadIndex++; if ( threadIndex == 0 ) { threadIndex = 1; } } while( GetThread( threadIndex ) ); return threadIndex; } /* qboolean ScriptMaster::labelExists ( GameScript * scr, const char *name ) { return scr->labelExists( name ); } qboolean ScriptMaster::labelExists ( const char *filename, const char *name ) { GameScript *scr; scr = ScriptLib.GetScript( filename ); if ( scr ) { return scr->labelExists( name ); } return false; } qboolean ScriptMaster::Goto ( GameScript * scr, const char *name ) { return scr->Goto( name ); } bool ScriptMaster::isVarGroup ( const char *name ) { const char *v; char vargroup[ 20 ]; int len; assert( name ); if ( !name ) { return false; } v = strchr( name, '.' ); if ( !v ) { return false; } len = v - name; if ( len > sizeof( vargroup ) - 1 ) { len = sizeof( vargroup ) - 1; } memset( vargroup, 0, sizeof( vargroup ) ); strncpy( vargroup, name, len ); if ( !strcmp( vargroup, "game" ) || !strcmp( vargroup, "level" ) || !strcmp( vargroup, "local" ) || !strcmp( vargroup, "parm" ) ) { return true; } return false; } 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; } return vars; } 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 ); } 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 ) { { &EV_ScriptThread_Execute, Execute }, { &EV_MoveDone, ObjectMoveDone }, { &EV_ScriptThread_Callback, ScriptCallback }, { &EV_ScriptThread_ThreadCallback, ThreadCallback }, { &EV_ScriptThread_VariableCallback, VariableCallback }, { &EV_ScriptThread_DeathCallback, DeathCallback }, { &EV_ScriptThread_CreateThread, CreateThread }, { &EV_ScriptThread_TerminateThread, TerminateThread }, { &EV_ScriptThread_ControlObject, ControlObject }, { &EV_ScriptThread_Goto, EventGoto }, { &EV_ScriptThread_Pause, EventPause }, { &EV_ScriptThread_Wait, EventWait }, { &EV_ScriptThread_WaitFor, EventWaitFor }, { &EV_ScriptThread_WaitForThread, EventWaitForThread }, { &EV_ScriptThread_WaitForVariable, EventWaitForVariable }, { &EV_ScriptThread_WaitForDeath, EventWaitForDeath }, { &EV_ScriptThread_WaitForSound, EventWaitForSound }, { &EV_ScriptThread_WaitForDialog, EventWaitForDialog }, { &EV_ScriptThread_WaitForPlayer, EventWaitForPlayer }, { &EV_ScriptThread_End, EventEnd }, { &EV_ScriptThread_Print, Print }, { &EV_ScriptThread_PrintInt, PrintInt }, { &EV_ScriptThread_PrintFloat, PrintFloat }, { &EV_ScriptThread_PrintVector, PrintVector }, { &EV_ScriptThread_NewLine, NewLine }, { &EV_ScriptThread_Assert, Assert }, { &EV_ScriptThread_Break, Break }, { &EV_ScriptThread_Clear, Clear }, { &EV_ScriptThread_Trigger, TriggerEvent }, { &EV_ScriptThread_ServerOnly, ServerEvent }, { &EV_ScriptThread_ClientOnly, ClientEvent }, { &EV_ScriptThread_StuffCommand, StuffCommand }, { &EV_ScriptThread_Spawn, Spawn }, { &EV_ScriptThread_Precache_Cache, CacheResourceEvent }, { &EV_ScriptThread_RegisterAlias, RegisterAlias }, { &EV_ScriptThread_RegisterAliasAndCache, RegisterAliasAndCache }, { &EV_ScriptThread_Map, MapEvent }, { &EV_ScriptThread_SetCvar, SetCvarEvent }, { &EV_ScriptThread_CueCamera, CueCamera }, { &EV_ScriptThread_CuePlayer, CuePlayer }, { &EV_ScriptThread_FreezePlayer, FreezePlayer }, { &EV_ScriptThread_ReleasePlayer, ReleasePlayer }, { &EV_ScriptThread_FadeIn, FadeIn }, { &EV_ScriptThread_FadeOut, FadeOut }, { &EV_ScriptThread_FadeSound, FadeSound }, { &EV_ScriptThread_ClearFade, ClearFade }, { &EV_ScriptThread_Letterbox, Letterbox }, { &EV_ScriptThread_ClearLetterbox, ClearLetterbox }, { &EV_ScriptThread_MusicEvent, MusicEvent }, { &EV_ScriptThread_ForceMusicEvent, ForceMusicEvent }, { &EV_ScriptThread_MusicVolumeEvent, MusicVolumeEvent }, { &EV_ScriptThread_RestoreMusicVolumeEvent, RestoreMusicVolumeEvent }, { &EV_ScriptThread_SoundtrackEvent, SoundtrackEvent }, { &EV_ScriptThread_RestoreSoundtrackEvent, RestoreSoundtrackEvent }, { &EV_ScriptThread_CameraCommand, CameraCommand }, { &EV_ScriptThread_SetCinematic, SetCinematic }, { &EV_ScriptThread_SetNonCinematic, SetNonCinematic }, { &EV_ScriptThread_SetAllAIOff, SetAllAIOff }, { &EV_ScriptThread_SetAllAIOn, SetAllAIOn }, { &EV_ScriptThread_SetSkipThread, SetSkipThread }, { &EV_ScriptThread_KillEnt, KillEnt }, { &EV_ScriptThread_RemoveEnt, RemoveEnt }, { &EV_ScriptThread_KillClass, KillClass }, { &EV_ScriptThread_RemoveClass, RemoveClass }, { &EV_ScriptThread_FakePlayer, FakePlayer }, { &EV_ScriptThread_RemoveFakePlayer, RemoveFakePlayer }, { &EV_ScriptThread_SetDialogScript, SetDialogScript }, { &EV_ScriptThread_SetLightStyle, SetLightStyle }, { &EV_ScriptThread_KillThreadEvent, KillThreadEvent }, { &EV_ScriptThread_SetThreadNameEvent, SetThreadName }, { &EV_ScriptThread_CenterPrint, CenterPrint }, { &EV_ScriptThread_LocationPrint, LocationPrint }, { &EV_ScriptThread_MissionFailed, MissionFailed }, { &EV_ScriptThread_ArenaCommand, ArenaCommand }, { &EV_AI_RecalcPaths, PassToPathmanager }, { &EV_AI_CalcPath, PassToPathmanager }, { NULL, NULL } }; ScriptThread::ScriptThread() { threadNum = 0; ClearWaitFor(); threadDying = false; doneProcessing = true; type = LEVEL_SCRIPT; } ScriptThread::~ScriptThread() { Director.NotifyOtherThreads( threadNum ); Director.RemoveThread( threadNum ); } void ScriptThread::ClearWaitFor ( void ) { waitUntil = 0; waitingFor = ""; waitingNumObjects = 0; waitingForThread = NULL; waitingForVariable = ""; waitingForDeath = ""; waitingForPlayer = false; } void ScriptThread::SetType ( scripttype_t newtype ) { type = newtype; } scripttype_t ScriptThread::GetType ( void ) { return type; } void ScriptThread::SetThreadNum ( int num ) { threadNum = num; } int ScriptThread::ThreadNum ( void ) { return threadNum; } const char *ScriptThread::ThreadName ( void ) { return threadName.c_str(); } void ScriptThread::SetThreadName ( Event *ev ) { threadName = ev->GetString( 1 ); } int ScriptThread::CurrentLine ( void ) { return linenumber; } const char *ScriptThread::Filename ( void ) { return script.Filename(); } ScriptThread *ScriptThread::WaitingOnThread ( void ) { return waitingForThread; } const char *ScriptThread::WaitingOnVariable ( void ) { return waitingForVariable.c_str(); } const char *ScriptThread::WaitingOnDeath ( void ) { return waitingForDeath.c_str(); } qboolean ScriptThread::WaitingOnPlayer ( void ) { return waitingForPlayer; } ScriptVariableList *ScriptThread::Vars ( void ) { return &localVars; } 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; } qboolean ScriptThread::SetScript ( const char *name ) { GameScript *scr; scr = ScriptLib.GetScript( name ); if ( scr ) { Setup( threadNum, scr, NULL ); return true; } return false; } 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; } qboolean ScriptThread::labelExists ( const char *name ) { return ScriptLib.labelExists( &script, name ); } void ScriptThread::Mark ( ThreadMarker *mark ) { assert( mark ); mark->linenumber = linenumber; mark->doneProcessing = doneProcessing; mark->waitingFor = waitingFor; mark->waitingForThread = waitingForThread; 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 ); } void ScriptThread::Restore ( ThreadMarker *mark ) { assert( mark ); linenumber = mark->linenumber; doneProcessing = mark->doneProcessing; waitingFor = mark->waitingFor; waitingForThread = mark->waitingForThread; 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 ); } } TargetList *ScriptThread::GetTargetList ( const 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; } 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 ); } if ( !num ) { warning( "SendCommandToSlaves", "Could not find target %s in world.\n", name ); } } // // 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; } 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 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() ); } } } 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 ); } void ScriptThread::Start ( float delay ) { CancelEventsOfType( EV_ScriptThread_Execute ); PostEvent( EV_ScriptThread_Execute, delay ); } void ScriptThread::StartImmediately ( void ) { CancelEventsOfType( EV_ScriptThread_Execute ); ProcessEvent( EV_ScriptThread_Execute ); } 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( ERR_DROP, "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 ); } 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 // //**************************************************************************************** /* 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; } void ScriptThread::ObjectMoveDone ( Event *ev ) { Entity *obj; obj = ev->GetEntity( 1 ); if ( !obj ) { gi.DPrintf( "Object no longer exists, MoveDone invalid now\n" ); return; } #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( 0 ); } } } void ScriptThread::CreateThread ( Event *ev ) { ScriptThread * pThread; pThread = Director.CreateThread( &script, ev->GetToken( 1 ) ); if ( pThread ) { // start right away pThread->StartImmediately(); } } 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 ) { DoMove(); 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(); // // we need to make sure we have an entity we are going to wait for // ent = ev->GetEntity( 1 ); if ( ent ) { doneProcessing = true; 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 ) { ClearWaitFor(); waitingForThread = Director.GetThread( ev->GetInteger( 1 ) ); if ( !waitingForThread ) { ev->Error( "EventWaitForThread", "Thread %d not running", ev->GetInteger( 1 ) ); return; } doneProcessing = true; DoMove(); } void ScriptThread::EventWaitForDeath ( Event *ev ) { ClearWaitFor(); waitingForDeath = ev->GetString(1); if ( !waitingForDeath.length() ) { ev->Error( "EventWaitForDeath", "Null name" ); return; } doneProcessing = true; DoMove(); } void ScriptThread::EventWaitForVariable ( Event *ev ) { ClearWaitFor(); waitingForVariable = ev->GetString(1); if ( !waitingForVariable.length() ) { ev->Error( "EventWaitForVariable", "Null variable" ); return; } doneProcessing = true; DoMove(); } void ScriptThread::EventWaitForSound ( Event *ev ) { str sound; float delay; ClearWaitFor(); DoMove(); delay = 0; sound = ev->GetString( 1 ); delay = gi.SoundLength( sound.c_str() ); if ( delay < 0 ) gi.DPrintf( "Lip file not found for dialog %s\n", sound.c_str() ); if ( ev->NumArgs() > 1 ) delay += ev->GetFloat( 2 ); Start( delay ); doneProcessing = true; } void ScriptThread::EventWaitForDialog ( Event *ev ) { float delay; Actor *act; Entity *ent; ClearWaitFor(); ent = ev->GetEntity( 1 ); if ( ent && ent->isSubclassOf( Actor ) ) { act = (Actor *)ent; delay = act->GetDialogRemainingTime( ); if ( ev->NumArgs() > 1 ) { delay += ev->GetFloat( 2 ); } Start( delay ); doneProcessing = true; } } void ScriptThread::EventWaitForPlayer ( Event *ev ) { 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 ) { Event * newevent; newevent = new Event( ev ); ent->ProcessEvent( newevent ); 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::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(); } } 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 StartImmediately(); } } void ScriptThread::ThreadCallback ( Event *ev ) { ScriptThread *thread; if ( threadDying ) { return; } thread = ev->GetThread(); if ( thread && ( thread == waitingForThread ) ) { ClearWaitFor(); StartImmediately(); } } void ScriptThread::VariableCallback ( Event *ev ) { ClearWaitFor(); // start right away Start( 0 ); } void ScriptThread::DeathCallback ( Event *ev ) { if ( threadDying ) { return; } ClearWaitFor(); // start right away Start( 0 ); } 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::CacheResourceEvent ( Event * ev ) { if ( !precache->integer ) { return; } if ( world ) { CacheResource( ev->GetString( 1 ), world ); } else { CacheResource( ev->GetString( 1 ), NULL ); } } void ScriptThread::RegisterAlias ( Event *ev ) { char parameters[100]; int i; // Get the parameters for this alias command parameters[0] = 0; for( i = 3 ; i <= ev->NumArgs() ; i++ ) { strcat( parameters, ev->GetString( i ) ); strcat( parameters, " "); } gi.GlobalAlias_Add( ev->GetString( 1 ), ev->GetString( 2 ), parameters ); } void ScriptThread::RegisterAliasAndCache ( Event *ev ) { RegisterAlias(ev); if ( !precache->integer ) return; CacheResource( ev->GetString( 2 ) ); } void ScriptThread::MapEvent ( Event *ev ) { if ( level.mission_failed ) return; 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 ) { float switchTime; Entity *ent; if ( ev->NumArgs() > 1 ) { switchTime = ev->GetFloat( 2 ); } else { switchTime = 0; } ent = ev->GetEntity( 1 ); if ( ent ) { SetCamera( ent, switchTime ); } else { ev->Error( "Camera named %s not found", ev->GetString( 1 ) ); } } void ScriptThread::CuePlayer ( Event *ev ) { float switchTime; if ( ev->NumArgs() > 0 ) { switchTime = ev->GetFloat( 1 ); } else { switchTime = 0; } SetCamera( NULL, switchTime ); } void ScriptThread::FreezePlayer ( Event *ev ) { level.playerfrozen = true; } void ScriptThread::ReleasePlayer ( Event *ev ) { level.playerfrozen = false; } void ScriptThread::FakePlayer ( Event *ev ) { qboolean holster; Player *player; player = ( Player * )g_entities[ 0 ].entity; if ( ( ev->NumArgs() < 1 ) || ev->GetBoolean( 1 ) ) { holster = true; } else { holster = false; } if ( player && player->edict->inuse && player->edict->client ) { player->FakePlayer( holster ); } } void ScriptThread::RemoveFakePlayer ( Event *ev ) { Player *player; player = ( Player * )g_entities[ 0 ].entity; if ( player && player->edict->inuse && player->edict->client ) { player->RemoveFakePlayer(); } } void ScriptThread::Spawn ( Event *ev ) { Entity *ent; Entity *tent; const char *name; ClassDef *cls; int n; int i; const char *targetname; ScriptVariable *var; const char *value; if ( ev->NumArgs() < 1 ) { ev->Error( "Usage: spawn entityname [keyname] [value]..." ); return; } // create a new entity SpawnArgs args; 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(), ".tik" ) ) { n += ".tik"; } args.setArg( "model", n.c_str() ); } else { args.setArg( "classname", name ); } } if ( ev->NumArgs() > 2 ) { n = ev->NumArgs(); for( i = 2; i <= n; i += 2 ) { args.setArg( ev->GetString( i ), ev->GetString( i + 1 ) ); } } cls = args.getClassDef(); if ( !cls ) { ev->Error( "'%s' is not a valid entity name", name ); return; } // If there is a spawntarget set, then use that entity's origin and angles targetname = args.getArg( "spawntarget" ); if ( targetname ) { tent = G_FindTarget( NULL, targetname ); if ( tent ) { args.setArg( "origin", va( "%f %f %f", tent->origin[ 0 ], tent->origin[ 1 ], tent->origin[ 2 ] ) ); args.setArg( "angle", va( "%f", tent->angles[1] ) ); } else { ev->Error( "Can't find targetname %s", targetname ); } } // // make sure to setup spawnflags properly // level.spawnflags = 0; value = args.getArg( "spawnflags" ); if ( value ) { level.spawnflags = atoi( value ); } ent = args.Spawn(); if ( ent ) { ent->ProcessPendingEvents(); } var = Director.GetVariable( "parm.lastspawn" ); var->setIntValue( ent->entnum ); } //FIXME //Move this to someplace Level class. static float last_fraction = 1.0f/8.0f; void ScriptThread::Letterbox ( Event *ev ) { level.m_letterbox_fraction = 1.0f/8.0f; level.m_letterbox_time = ev->GetFloat( 1 ); level.m_letterbox_time_start = ev->GetFloat( 1 ); level.m_letterbox_dir = letterbox_in; if ( ev->NumArgs() > 1 ) level.m_letterbox_fraction = ev->GetFloat( 2 ); } void ScriptThread::ClearLetterbox ( Event *ev ) { level.m_letterbox_time = level.m_letterbox_time_start; level.m_letterbox_dir = letterbox_out; } void ScriptThread::SetDialogScript ( Event *ev ) { ScriptThread * pThread; ScriptLib.SetDialogScript( ev->GetString( 1 ) ); // // precache the data in the dialog script // pThread = Director.CreateThread( &script, "dialog::precache" ); if ( pThread ) { // start right away pThread->Start( -1 ); } } void ScriptThread::SetLightStyle ( Event *ev ) { lightStyles.SetLightStyle( ev->GetInteger( 1 ), ev->GetString( 2 ) ); } void ScriptThread::FadeIn ( Event *ev ) { level.m_fade_time_start = ev->GetFloat( 1 ); level.m_fade_time = ev->GetFloat( 1 ); level.m_fade_color[0] = ev->GetFloat( 2 ); level.m_fade_color[1] = ev->GetFloat( 3 ); level.m_fade_color[2] = ev->GetFloat( 4 ); level.m_fade_alpha = ev->GetFloat( 5 ); level.m_fade_type = fadein; level.m_fade_style = alphablend; if ( ev->NumArgs() > 5 ) { level.m_fade_style = (fadestyle_t)ev->GetInteger( 6 ); } } void ScriptThread::ClearFade ( Event *ev ) { level.m_fade_time = -1; level.m_fade_type = fadein; } void ScriptThread::FadeOut ( Event *ev ) { level.m_fade_time_start = ev->GetFloat( 1 ); level.m_fade_time = ev->GetFloat( 1 ); level.m_fade_color[0] = ev->GetFloat( 2 ); level.m_fade_color[1] = ev->GetFloat( 3 ); level.m_fade_color[2] = ev->GetFloat( 4 ); level.m_fade_alpha = ev->GetFloat( 5 ); level.m_fade_type = fadeout; level.m_fade_style = alphablend; if ( ev->NumArgs() > 5 ) { level.m_fade_style = (fadestyle_t)ev->GetInteger( 6 ); } } void ScriptThread::FadeSound ( Event *ev ) { G_FadeSound( ev->GetFloat( 1 ) ); } 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::MusicVolumeEvent ( Event *ev ) { float volume; float fade_time; volume = ev->GetFloat( 1 ); fade_time = ev->GetFloat( 2 ); ChangeMusicVolume( volume, fade_time ); } void ScriptThread::RestoreMusicVolumeEvent ( Event *ev ) { float fade_time; fade_time = ev->GetFloat( 1 ); RestoreMusicVolume( fade_time ); } 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::RestoreSoundtrackEvent ( Event *ev ) { RestoreSoundtrack(); } void ScriptThread::SetCinematic ( Event *ev ) { G_StartCinematic(); } void ScriptThread::SetNonCinematic ( Event *ev ) { G_StopCinematic(); } void ScriptThread::SetAllAIOff ( Event *ev ) { level.ai_on = false; } void ScriptThread::SetAllAIOn ( Event *ev ) { level.ai_on = true; } void ScriptThread::SetSkipThread ( Event *ev ) { str label; label = ev->GetString( 1 ); if ( label.length() && label.icmp( "null" ) ) { world->skipthread = script.Filename() + str( "::" ) + label; } else { world->skipthread = ""; } } void ScriptThread::KillThreadEvent ( Event *ev ) { Director.KillThread( ev->GetString( 1 ) ); } void ScriptThread::PassToPathmanager ( Event *ev ) { PathManager.ProcessEvent( ev ); } void ScriptThread::CenterPrint ( Event *ev ) { int j; gentity_t *other; for( j = 0; j < game.maxclients; j++ ) { other = &g_entities[ j ]; if ( other->inuse && other->client ) { gi.centerprintf( other, ev->GetString( 1 ) ); } } } void ScriptThread::LocationPrint ( Event *ev ) { int j; gentity_t *other; int x,y; x = ev->GetInteger( 1 ); y = ev->GetInteger( 2 ); for( j = 0; j < game.maxclients; j++ ) { other = &g_entities[ j ]; if ( other->inuse && other->client ) { gi.locationprintf( other, x, y, ev->GetString( 3 ) ); } } } void ScriptThread::StuffCommand ( Event *ev ) { gi.SendConsoleCommand( va( "%s\n", ev->GetString( 1 ) ) ); } 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_entities ) ) { ev->Error( "Value out of range. Possible values range from 0 to %d.\n", globals.max_entities ); return; } ent = G_GetEntity( num ); ent->Damage( world, world, ent->max_health + 25, vec_zero, vec_zero, vec_zero, 0, 0, 0 ); } 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_entities ) ) { ev->Error( "Value out of range. Possible values range from 0 to %d.\n", globals.max_entities ); return; } ent = G_GetEntity( num ); ent->PostEvent( Event( EV_Remove ), 0 ); } void ScriptThread::KillClass ( Event * ev ) { int except; str classname; gentity_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_entities[ game.maxclients ]; from < &g_entities[ globals.num_entities ]; 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 ); } } } void ScriptThread::RemoveClass ( Event * ev ) { int except; str classname; gentity_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_entities[ game.maxclients ]; from < &g_entities[ globals.num_entities ]; 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::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 ); } } void ScriptThread::MissionFailed ( Event *ev ) { G_MissionFailed(); } void ScriptThread::ArenaCommand ( Event *ev ) { // Make sure we are in arena mode for this command if ( g_gametype->integer == GT_ARENA ) { dmManager.ArenaCommand( ev ); } else { warning( "ScriptThread::ArenaCommand", "Arena mode not set, ignoring arena command.\n" ); } } */ //////////////////////// // // LIGHTSTYLE REPOSITORY // //////////////////////// LightStyleClass lightStyles; CLASS_DECLARATION( Class, LightStyleClass, NULL ) { { NULL, NULL } }; void LightStyleClass::SetLightStyle( int index, const str &style ) { if ( ( index < 0 ) || ( index >= MAX_LIGHTSTYLES ) ) { assert( 0 ); return; } styles[ index ] = style; gi.SetLightStyle( index, style.c_str() ); } void LightStyleClass::Archive( Archiver &arc ) { int i; for( i = 0; i < MAX_LIGHTSTYLES; i++ ) { arc.ArchiveString( &styles[ i ] ); if ( arc.Loading() && styles[ i ].length() ) { gi.SetLightStyle( i, styles[ i ].c_str() ); } } }