openmohaa/code/fgame/scriptmaster.cpp
2025-02-02 19:35:01 +01:00

1305 lines
30 KiB
C++

/*
===========================================================================
Copyright (C) 2023 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// scriptmaster.cpp : Spawns at the beginning of the maps, parse scripts.
#include "g_local.h"
#include "scriptmaster.h"
#include "scriptthread.h"
#include "scriptclass.h"
#include "gamescript.h"
#include "game.h"
#include "g_spawn.h"
#include "object.h"
#include "worldspawn.h"
#include "scriptcompiler.h"
#include "scriptexception.h"
#ifdef WIN32
# include <direct.h>
#else
# include <sys/stat.h>
# include <unistd.h>
#endif
#if defined(CGAME_DLL)
# include <hud.h>
# define SCRIPT_Printf cgi.Printf
# define SCRIPT_DPrintf cgi.DPrintf
#elif defined(GAME_DLL)
# include "hud.h"
# include "dm_manager.h"
# include "player.h"
# include "entity.h"
# include "huddraw.h"
# include "weaputils.h"
# include "camera.h"
# include "consoleevent.h"
# define SCRIPT_Printf gi.Printf
# define SCRIPT_DPrintf gi.DPrintf
#endif
con_set<str, ScriptThreadLabel> m_scriptCmds;
str vision_current;
qboolean disable_team_change;
qboolean disable_team_spectate;
ScriptMaster Director;
ScriptEvent scriptedEvents[SE_MAX];
CLASS_DECLARATION(Class, ScriptEvent, NULL) {
{NULL, NULL}
};
//
// world stuff
//
Event EV_SetTiki
(
"settiki",
EV_DEFAULT,
NULL,
NULL,
"Not implemented - used to stop script error on dedicated server"
);
Event EV_RegisterAliasAndCache
(
"aliascache",
EV_DEFAULT,
"ssSSSS",
"alias_name real_name arg1 arg2 arg3 arg4",
"Sets up an alias and caches the resourse."
);
Event EV_RegisterAlias
(
"alias",
EV_DEFAULT,
"ssSSSS",
"alias_name real_name arg1 arg2 arg3 arg4",
"Sets up an alias."
);
Event EV_Cache
(
"cache",
EV_CACHE,
"s",
"resourceName",
"pre-cache the given resource."
);
CLASS_DECLARATION(Listener, ScriptMaster, NULL) {
{&EV_RegisterAliasAndCache, &ScriptMaster::RegisterAliasAndCache},
{&EV_RegisterAlias, &ScriptMaster::RegisterAlias },
{&EV_Cache, &ScriptMaster::Cache },
{NULL, NULL }
};
const char *ScriptMaster::ConstStrings[] = {
"",
"touch",
"block",
"trigger",
"use",
"damage",
"location",
"say",
"fail",
"bump",
"default",
"all",
"move_action",
"resume",
"open",
"close",
"pickup",
"reach",
"start",
"teleport",
"move",
"move_end",
"moveto",
"walkto",
"runto",
"crouchto",
"crawlto",
"stop",
"reset",
"prespawn",
"spawn",
"playerspawn",
"skip",
"roundstart",
"visible",
"not_visible",
"done",
"animdone",
"upperanimdone",
"saydone",
"flaggedanimdone",
"idle",
"walk",
"shuffle",
"anim/crouch.scr",
"forgot",
"jog_hunch",
"jog_hunch_rifle",
"killed",
"alarm",
"scriptclass",
"ai/utils/fact_script_factory.scr",
"death",
"death_fall_to_knees",
"enemy",
"dead",
"mood",
"patrol",
"runner",
"follow",
"action",
"move_begin",
"action_begin",
"action_end",
"success",
"entry",
"exit",
"path",
"node",
"ask_count",
"attacker",
"usecover",
"waitcover",
"void",
"end",
"attack",
"near",
"papers",
"check_papers",
"timeout",
"hostile",
"leader",
"gamemap",
"bored",
"nervous",
"curious",
"alert",
"greet",
"depend",
"anim",
"anim_scripted",
"anim_curious",
"animloop",
"undefined",
"notset",
"increment",
"decrement",
"toggle",
"normal",
"suspend",
"mystery",
"surprise",
"anim/crouch_run.scr",
"anim/aim.scr",
"anim/shoot.scr",
"anim/mg42_shoot.scr",
"anim/mg42_idle.scr",
"anim/mg42_reload.scr",
"drive",
"global/weapon.scr",
"global/moveto.scr",
"global/anim.scr",
"global/anim_scripted.scr",
"global/anim_noclip.scr",
"global/anim_attached.scr",
"global/walkto.scr",
"global/runto.scr",
"aimat",
"global/disabled_ai.scr",
"global/crouchto.scr",
"global/crawlto.scr",
"global/killed.scr",
"global/pain.scr",
"pain",
"track",
"hasenemy",
"anim/cower.scr",
"anim/stand.scr",
"anim/idle.scr",
"anim/surprise.scr",
"anim/standshock.scr",
"anim/standidentify.scr",
"anim/standflinch.scr",
"anim/dog_idle.scr",
"anim/dog_attack.scr",
"anim/dog_curious.scr",
"anim/dog_chase.scr",
"cannon",
"grenade",
"badplace",
"heavy",
"item",
"items",
"item1",
"item2",
"item3",
"item4",
"stand",
"mg",
"pistol",
"rifle",
"smg",
"turnto",
"standing",
"crouching",
"prone",
"offground",
"walking",
"running",
"falling",
"anim_nothing",
"anim_direct",
"anim_path",
"anim_waypoint",
"anim_direct_nogravity",
"emotion_none",
"emotion_neutral",
"emotion_worry",
"emotion_panic",
"emotion_fear",
"emotion_disgust",
"emotion_anger",
"emotion_aiming",
"emotion_determined",
"emotion_dead",
"emotion_curious",
"anim/emotion.scr",
"forceanim",
"forceanim_scripted",
"turret",
"cover",
"anim/pain.scr",
"anim/killed.scr",
"anim/attack.scr",
"anim/sniper.scr",
"anim/suppress.scr",
"knees",
"crawl",
"floor",
"anim/patrol.scr",
"anim/run.scr",
"crouch",
"crouchwalk",
"crouchrun",
"anim/crouch_walk.scr",
"anim/walk.scr",
"anim/prone.scr",
"anim/runawayfiring.scr",
"anim/run_shoot.scr",
"anim/runto_alarm.scr",
"anim/runto_casual.scr",
"anim/runto_cover.scr",
"anim/runto_danger.scr",
"anim/runto_dive.scr",
"anim/runto_flee.scr",
"anim/runto_inopen.scr",
"anim/disguise_salute.scr",
"anim/disguise_wait.scr",
"anim/disguise_papers.scr",
"anim/disguise_enemy.scr",
"anim/disguise_halt.scr",
"anim/disguise_accept.scr",
"anim/disguise_deny.scr",
"anim/cornerleft.scr",
"anim/cornerright.scr",
"anim/overattack.scr",
"anim/lowwall.scr",
"anim/highwall.scr",
"anim/continue_last_anim.scr",
"flagged",
"anim/fullbody.scr",
"salute",
"sentry",
"officer",
"rover",
"none",
"machinegunner",
"disguise",
"dog_idle",
"dog_attack",
"dog_curious",
"dog_grenade",
"anim/grenadeturn.scr",
"anim/grenadekick.scr",
"anim/grenadethrow.scr",
"anim/grenadetoss.scr",
"anim/grenademartyr.scr",
"movedone",
"aim",
"ontarget",
"unarmed",
"balcony_idle",
"balcony_curious",
"balcony_attack",
"balcony_disguise",
"balcony_grenade",
"balcony_pain",
"balcony_killed",
"weaponless",
"death_balcony_intro",
"death_balcony_loop",
"death_balcony_outtro",
"sounddone",
"noclip",
"german",
"american",
"spectator",
"freeforall",
"allies",
"axis",
"draw",
"kills",
"allieswin",
"axiswin",
"anim/say_curious_sight.scr",
"anim/say_curious_sound.scr",
"anim/say_grenade_sighted.scr",
"anim/say_kill.scr",
"anim/say_mandown.scr",
"anim/say_sighted.scr",
"vehicleanimdone",
"postthink",
"turndone",
"anim/no_anim_killed.scr",
"mg42",
"mp40",
// Added in 2.0
"auto",
"both",
// Added in 2.30
"runandshoot",
// Added in OPM
"respawn",
"viewmodelanim_done"
};
ScriptMaster::~ScriptMaster()
{
Reset(false);
}
static int bLoadForMap(char *psMapsBuffer, const char *name)
{
cvar_t *mapname = gi.Cvar_Get("mapname", "", 0);
const char *token;
if (!strncmp("test", mapname->string, sizeof("test"))) {
return true;
}
token = COM_Parse(&psMapsBuffer);
if (!token || !*token) {
Com_Printf("ERROR bLoadForMap: %s alias with empty maps specification.\n", name);
return false;
}
while (token && *token) {
if (!Q_stricmpn(token, mapname->string, strlen(token))) {
return true;
}
token = COM_Parse(&psMapsBuffer);
}
return false;
}
void ScriptMaster::RegisterAliasAndCache(Event *ev)
{
int i;
char parameters[MAX_STRING_CHARS];
char *psMapsBuffer;
bool bAlwaysLoaded = false;
if (ev->NumArgs() < 2) {
return;
}
// Get the parameters for this alias command
parameters[0] = 0;
psMapsBuffer = NULL;
for (i = 3; i <= ev->NumArgs(); i++) {
str s;
// Added in OPM
// MOHAA doesn't check that
if (ev->IsListenerAt(i)) {
Listener *l = ev->GetListener(i);
if (l && l == Director.CurrentThread()) {
s = "local";
} else {
s = ev->GetString(i);
}
} else {
s = ev->GetString(i);
}
if (!s.icmp("maps")) {
i++;
psMapsBuffer = (char *)ev->GetToken(i).c_str();
continue;
}
if (!s.icmp("always")) {
bAlwaysLoaded = true;
continue;
}
strcat(parameters, s);
strcat(parameters, " ");
}
if (bAlwaysLoaded) {
gi.GlobalAlias_Add(ev->GetString(1), ev->GetString(2), parameters);
}
if (bLoadForMap(psMapsBuffer, ev->GetString(1))) {
if (!bAlwaysLoaded) {
gi.GlobalAlias_Add(ev->GetString(1), ev->GetString(2), parameters);
}
CacheResource(ev->GetString(2));
}
}
void ScriptMaster::RegisterAlias(Event *ev)
{
int i;
char parameters[MAX_STRING_CHARS];
char *psMapsBuffer;
qboolean subtitle;
bool bAlwaysLoaded = false;
if (ev->NumArgs() < 2) {
return;
}
// Get the parameters for this alias command
parameters[0] = 0;
subtitle = 0;
psMapsBuffer = NULL;
for (i = 3; i <= ev->NumArgs(); i++) {
str s;
// Added in OPM
// MOHAA doesn't check that
if (ev->IsListenerAt(i)) {
Listener *l = ev->GetListener(i);
if (l && l == Director.CurrentThread()) {
s = "local";
} else {
s = ev->GetString(i);
}
} else {
s = ev->GetString(i);
}
if (!s.icmp("maps")) {
i++;
psMapsBuffer = (char *)ev->GetToken(i).c_str();
continue;
}
if (!s.icmp("always")) {
bAlwaysLoaded = true;
} else if (subtitle) {
strcat(parameters, "\"");
strcat(parameters, s);
strcat(parameters, "\" ");
subtitle = 0;
} else {
subtitle = s.icmp("subtitle") == 0;
strcat(parameters, s);
}
strcat(parameters, " ");
}
if (bAlwaysLoaded || bLoadForMap(psMapsBuffer, ev->GetString(1))) {
gi.GlobalAlias_Add(ev->GetString(1), ev->GetString(2), parameters);
}
}
void ScriptMaster::Cache(Event *ev)
{
#ifdef GAME_DLL
if (!precache->integer) {
return;
}
CacheResource(ev->GetString(1));
#endif
}
void ScriptMaster::InitConstStrings(void)
{
EventDef *eventDef;
const_str name;
unsigned int eventnum;
con_map_enum<Event *, EventDef> en;
int i;
static_assert(ARRAY_LEN(ConstStrings) == (STRING_LENGTH_ - 1), "Constant strings don't match. Make sure the 'const_str' enum match with the 'ConstStrings' string array");
for (i = 0; i < ARRAY_LEN(ConstStrings); i++) {
AddString(ConstStrings[i]);
}
if (!Listener::EventSystemStarted) {
// Added in OPM
// This usually means the game module is getting destroyed
// most often, the event command list has been destroyed earlier
return;
}
Event::normalCommandList.clear();
Event::returnCommandList.clear();
Event::getterCommandList.clear();
Event::setterCommandList.clear();
en = Event::eventDefList;
for (en.NextValue(); en.CurrentValue() != NULL; en.NextValue()) {
eventDef = en.CurrentValue();
eventnum = (*en.CurrentKey())->eventnum;
str command = eventDef->command.c_str();
if (eventDef->type == EV_NORMAL || eventDef->type == EV_RETURN) {
command.tolower();
}
name = AddString(command);
if (eventDef->type == EV_NORMAL) {
Event::normalCommandList[name] = eventnum;
} else if (eventDef->type == EV_RETURN) {
Event::returnCommandList[name] = eventnum;
} else if (eventDef->type == EV_GETTER) {
Event::getterCommandList[name] = eventnum;
} else if (eventDef->type == EV_SETTER) {
Event::setterCommandList[name] = eventnum;
}
}
}
ScriptThread *ScriptMaster::CreateThread(str filename, str label, Listener *self)
{
GameScript *scr = GetScript(filename);
if (!scr) {
return NULL;
}
return CreateThread(scr, label, self);
}
ScriptThread *ScriptMaster::CreateThread(GameScript *scr, str label, Listener *self)
{
try {
return CreateScriptThread(scr, self, label);
} catch (ScriptException& exc) {
gi.DPrintf("ScriptMaster::CreateThread: %s\n", exc.string.c_str());
return NULL;
}
}
void ScriptMaster::ExecuteThread(str filename, str label)
{
GameScript *scr = GetScript(filename);
if (!scr) {
return;
}
ExecuteThread(scr, label);
}
void ScriptMaster::ExecuteThread(GameScript *scr, str label)
{
ScriptThread *thread = CreateThread(scr, label);
try {
if (thread) {
thread->Execute();
}
} catch (ScriptException& exc) {
gi.DPrintf("ScriptMaster::ExecuteThread: %s\n", exc.string.c_str());
}
}
void ScriptMaster::ExecuteThread(str filename, str label, Event& parms)
{
GameScript *scr = GetScript(filename);
if (!scr) {
return;
}
ExecuteThread(scr, label, parms);
}
void ScriptMaster::ExecuteThread(GameScript *scr, str label, Event& parms)
{
ScriptThread *thread = CreateThread(scr, label);
try {
thread->Execute(parms);
} catch (ScriptException& exc) {
gi.DPrintf("ScriptMaster::ExecuteThread: %s\n", exc.string.c_str());
}
}
ScriptThread *ScriptMaster::CreateScriptThread(GameScript *scr, Listener *self, const_str label)
{
ScriptClass *scriptClass = new ScriptClass(scr, self);
return CreateScriptThread(scriptClass, label);
}
ScriptThread *ScriptMaster::CreateScriptThread(ScriptClass *scriptClass, unsigned char *m_pCodePos)
{
return new ScriptThread(scriptClass, m_pCodePos);
}
ScriptThread *ScriptMaster::CreateScriptThread(ScriptClass *scriptClass, const_str label)
{
unsigned char *m_pCodePos = scriptClass->FindLabel(label);
if (!m_pCodePos) {
throw ScriptException(
"ScriptMaster::CreateScriptThread: label '%s' does not exist in '%s'.",
Director.GetString(label).c_str(),
scriptClass->Filename().c_str()
);
}
return CreateScriptThread(scriptClass, m_pCodePos);
}
ScriptThread *ScriptMaster::CreateScriptThread(GameScript *scr, Listener *self, str label)
{
return CreateScriptThread(scr, self, Director.AddString(label));
}
ScriptThread *ScriptMaster::CreateScriptThread(ScriptClass *scriptClass, str label)
{
if (label.length() && *label) {
return CreateScriptThread(scriptClass, Director.AddString(label));
} else {
return CreateScriptThread(scriptClass, STRING_EMPTY);
}
}
ScriptMaster::ScriptMaster()
{
}
void ScriptMaster::Reset(qboolean samemap)
{
ScriptClass_allocator.FreeAll();
stackCount = 0;
cmdCount = 0;
cmdTime = 0;
maxTime = MAX_EXECUTION_TIME;
//pTop = &avar_Stack[ 0 ];
iPaused = 0;
#if defined(GAME_DLL)
for (int i = 1; i <= m_menus.NumObjects(); i++) {
Hidemenu(m_menus.ObjectAt(i), true);
}
m_menus.ClearObjectList();
#endif
if (!samemap) {
for (int i = 0; i < SE_MAX; i++) {
scriptedEvents[i] = ScriptEvent();
}
CloseGameScript();
StringDict.clear();
InitConstStrings();
}
ScriptDelegate::ResetAllDelegates();
}
void ScriptMaster::ExecuteRunning(void)
{
int i;
int startTime;
int startMs;
str fileName;
str sourcePosString;
if (stackCount) {
return;
}
if (!timerList.IsDirty()) {
return;
}
cmdTime = 0;
cmdCount = 0;
startTime = level.svsTime;
try {
while ((m_CurrentThread = (ScriptThread *)timerList.GetNextElement(i))) {
if (g_timescripts->integer) {
fileName = m_CurrentThread->FileName();
sourcePosString = m_CurrentThread->m_ScriptVM->GetSourcePos();
startMs = gi.Milliseconds();
}
level.setTime(level.svsStartTime + i);
m_CurrentThread->m_ScriptVM->m_ThreadState = THREAD_RUNNING;
m_CurrentThread->m_ScriptVM->Execute();
if (g_timescripts->integer) {
str string;
string = "Execute Running: ";
string += str(gi.Milliseconds() - startMs / 1000.f);
string += " file: ";
string += fileName;
string += " codepos: ";
string += sourcePosString;
gi.DebugPrintf("%s\n", string.c_str());
}
}
} catch (const ScriptException& e) {
gi.Error(ERR_DROP, "%s", e.string.c_str());
}
level.setTime(startTime);
level.m_LoopProtection = true;
}
void ScriptMaster::SetTime(int time)
{
timerList.SetTime(time);
}
void ScriptMaster::AddTiming(ScriptThread *thread, int time)
{
timerList.AddElement(thread, time);
}
void ScriptMaster::RemoveTiming(ScriptThread *thread)
{
timerList.RemoveElement(thread);
}
ScriptClass *ScriptMaster::CurrentScriptClass(void)
{
return CurrentScriptThread()->GetScriptClass();
}
void ScriptMaster::CloseGameScript(void)
{
con_map_enum<const_str, GameScript *> en(m_GameScripts);
GameScript **g;
Container<GameScript *> gameScripts;
for (g = en.NextValue(); g != NULL; g = en.NextValue()) {
gameScripts.AddObject(*g);
}
for (int i = gameScripts.NumObjects(); i > 0; i--) {
delete gameScripts.ObjectAt(i);
}
m_GameScripts.clear();
}
GameScript *ScriptMaster::GetGameScriptInternal(str& filename)
{
void *sourceBuffer = NULL;
int sourceLength;
char filepath[MAX_QPATH];
GameScript *scr;
if (filename.length() >= MAX_QPATH) {
gi.Error(ERR_DROP, "Script filename '%s' exceeds maximum length of %d\n", filename.c_str(), MAX_QPATH);
}
Q_strncpyz(filepath, filename.c_str(), sizeof(filepath));
gi.FS_CanonicalFilename(filepath);
filename = filepath;
scr = m_GameScripts[StringDict.findKeyIndex(filename)];
if (scr != NULL) {
return scr;
}
scr = new GameScript(filename);
m_GameScripts[StringDict.addKeyIndex(filename)] = scr;
if (GetCompiledScript(scr)) {
scr->m_Filename = Director.AddString(filename);
return scr;
}
sourceLength = gi.FS_ReadFile(filename.c_str(), &sourceBuffer, true);
if (sourceLength == -1) {
throw ScriptException("Can't find '%s'\n", filename.c_str());
}
scr->Load(sourceBuffer, sourceLength);
gi.FS_FreeFile(sourceBuffer);
if (!scr->successCompile) {
throw ScriptException("Script '%s' was not properly loaded", filename.c_str());
}
return scr;
}
GameScript *ScriptMaster::GetGameScript(const_str filename, qboolean recompile)
{
return GetGameScript(Director.GetString(filename), recompile);
}
GameScript *ScriptMaster::GetGameScript(str filename, qboolean recompile)
{
const_str s = StringDict.findKeyIndex(filename);
GameScript *scr = m_GameScripts[s];
int i;
if (scr != NULL && !recompile) {
if (!scr->successCompile) {
throw ScriptException("Script '%s' was not properly loaded\n", filename.c_str());
}
return scr;
} else {
if (scr && recompile) {
Container<ScriptClass *> list;
MEM_BlockAlloc_enum<ScriptClass> en = ScriptClass_allocator;
ScriptClass *scriptClass;
m_GameScripts[s] = NULL;
for (scriptClass = en.NextElement(); scriptClass != NULL; scriptClass = en.NextElement()) {
if (scriptClass->GetScript() == scr) {
list.AddObject(scriptClass);
}
}
for (i = 1; i <= list.NumObjects(); i++) {
delete list.ObjectAt(i);
}
delete scr;
}
return GetGameScriptInternal(filename);
}
}
GameScript *ScriptMaster::GetScript(const_str filename, qboolean recompile)
{
try {
return GetGameScript(filename, recompile);
} catch (ScriptException& exc) {
gi.DPrintf("ScriptMaster::GetScript: %s\n", exc.string.c_str());
}
return NULL;
}
GameScript *ScriptMaster::GetScript(str filename, qboolean recompile)
{
try {
return GetGameScript(filename, recompile);
} catch (ScriptException& exc) {
gi.DPrintf("ScriptMaster::GetScript: %s\n", exc.string.c_str());
}
return NULL;
}
void ScriptMaster::ArchiveString(Archiver& arc, const_str& s)
{
str s2;
byte b;
if (arc.Loading()) {
arc.ArchiveByte(&b);
if (b) {
arc.ArchiveString(&s2);
s = AddString(s2);
} else {
s = 0;
}
} else {
if (s) {
b = 1;
arc.ArchiveByte(&b);
s2 = Director.GetString(s);
arc.ArchiveString(&s2);
} else {
b = 0;
arc.ArchiveByte(&b);
}
}
}
void ScriptMaster::Archive(Archiver& arc)
{
int count;
int i, j;
ScriptClass *c;
int num;
ScriptVM *scriptVM;
ScriptVM *prevScriptVM;
if (arc.Saving()) {
count = (int)ScriptClass_allocator.Count();
arc.ArchiveInteger(&count);
MEM_BlockAlloc_enum<ScriptClass> en = ScriptClass_allocator;
for (c = en.NextElement(); c != NULL; c = en.NextElement()) {
c->ArchiveInternal(arc);
num = 0;
for (scriptVM = c->m_Threads; scriptVM != NULL; scriptVM = scriptVM->next) {
num++;
}
arc.ArchiveInteger(&num);
for (scriptVM = c->m_Threads; scriptVM != NULL; scriptVM = scriptVM->next) {
scriptVM->m_Thread->ArchiveInternal(arc);
}
}
} else {
arc.ArchiveInteger(&count);
for (i = 0; i < count; i++) {
c = new ScriptClass();
c->ArchiveInternal(arc);
arc.ArchiveInteger(&num);
c->m_Threads = NULL;
prevScriptVM = NULL;
for (j = 0; j < num; j++) {
scriptVM = new ScriptVM;
scriptVM->m_Thread = new ScriptThread;
scriptVM->m_Thread->m_ScriptVM = scriptVM;
scriptVM->m_ScriptClass = c;
scriptVM->next = NULL;
if (prevScriptVM) {
prevScriptVM->next = scriptVM;
} else {
c->m_Threads = scriptVM;
}
prevScriptVM = scriptVM;
scriptVM->m_Thread->ArchiveInternal(arc);
}
}
}
timerList.Archive(arc);
m_menus.Archive(arc);
}
GameScript *ScriptMaster::GetTempScript(const char *data)
{
GameScript *scr = new GameScript;
scr->Load((void *)data, strlen(data));
if (!scr->successCompile) {
return NULL;
}
return scr;
}
void ScriptMaster::AddMenu(str name)
{
m_menus.AddUniqueObject(name);
}
void ScriptMaster::RemoveMenu(str name)
{
if (m_menus.IndexOfObject(name)) {
m_menus.RemoveObject(name);
}
}
void ScriptMaster::LoadMenus(void)
{
for (int i = 1; i <= m_menus.NumObjects(); i++) {
Showmenu(m_menus.ObjectAt(i), true);
}
}
ScriptThread *ScriptMaster::PreviousThread(void)
{
return m_PreviousThread;
}
ScriptThread *ScriptMaster::CurrentThread(void)
{
return m_CurrentThread;
}
ScriptThread *ScriptMaster::CurrentScriptThread(void)
{
if (!m_CurrentThread) {
ScriptError("current thread is NULL");
}
return m_CurrentThread;
}
const_str ScriptMaster::AddString(const char *s)
{
return StringDict.addKeyIndex(s);
}
const_str ScriptMaster::AddString(str& s)
{
return StringDict.addKeyIndex(s);
}
const_str ScriptMaster::GetString(const char *s)
{
const_str cs = StringDict.findKeyIndex(s);
return cs ? cs : STRING_EMPTY;
}
const_str ScriptMaster::GetString(str s)
{
return GetString(s.c_str());
}
str& ScriptMaster::GetString(const_str s)
{
return StringDict[s];
}
void ScriptMaster::Pause()
{
iPaused++;
}
void ScriptMaster::Unpause()
{
iPaused--;
if (iPaused == 0) {
ExecuteRunning();
}
}
void ScriptMaster::AllowPause(bool allow)
{
if (allow) {
iPaused = 0;
} else {
iPaused = -1;
}
}
void ScriptMaster::PrintStatus(void)
{
str status;
int iThreadNum = 0;
int iThreadRunning = 0;
int iThreadWaiting = 0;
int iThreadSuspended = 0;
MEM_BlockAlloc_enum<ScriptClass> en = ScriptClass_allocator;
ScriptClass *scriptClass;
char szBuffer[MAX_STRING_TOKENS];
status = "num state label script \n";
status += "------- ---------- --------------- ---------------\n";
for (scriptClass = en.NextElement(); scriptClass != NULL; scriptClass = en.NextElement()) {
ScriptVM *vm;
for (vm = scriptClass->m_Threads; vm != NULL; vm = vm->next) {
Com_sprintf(szBuffer, sizeof(szBuffer), "%.7d", iThreadNum);
status += szBuffer + str(" ");
switch (vm->ThreadState()) {
case THREAD_RUNNING:
Com_sprintf(szBuffer, sizeof(szBuffer), "%8s", "running");
iThreadRunning++;
break;
case THREAD_WAITING:
Com_sprintf(szBuffer, sizeof(szBuffer), "%8s", "waiting");
iThreadWaiting++;
break;
case THREAD_SUSPENDED:
Com_sprintf(szBuffer, sizeof(szBuffer), "%8s", "suspended");
iThreadSuspended++;
break;
}
status += szBuffer;
Com_sprintf(szBuffer, sizeof(szBuffer), "%15s", vm->Label().c_str());
status += szBuffer + str(" ");
Com_sprintf(szBuffer, sizeof(szBuffer), "%15s", vm->Filename().c_str());
status += szBuffer;
status += "\n";
iThreadNum++;
}
}
status += "------- ---------- --------------- ---------------\n";
status += str(m_GameScripts.size()) + " total scripts compiled\n";
status += str(iThreadNum) + " total threads ( " + str(iThreadRunning) + " running thread(s), " + str(iThreadWaiting)
+ " waiting thread(s), " + str(iThreadSuspended) + " suspended thread(s) )\n";
gi.Printf(status.c_str());
}
void ScriptMaster::PrintThread(int iThreadNum)
{
int iThread = 0;
ScriptVM *vm;
bool bFoundThread = false;
str status;
MEM_BlockAlloc_enum<ScriptClass> en = ScriptClass_allocator;
ScriptClass *scriptClass;
for (scriptClass = en.NextElement(); scriptClass != NULL; scriptClass = en.NextElement()) {
for (vm = scriptClass->m_Threads; vm != NULL; vm = vm->next) {
if (iThread == iThreadNum) {
bFoundThread = true;
break;
}
iThread++;
}
if (bFoundThread) {
break;
}
}
if (!bFoundThread) {
gi.Printf("Can't find thread id %i.\n", iThreadNum);
}
status = "-------------------------\n";
status += "num: " + str(iThreadNum) + "\n";
switch (vm->ThreadState()) {
case THREAD_RUNNING:
status += "state: running\n";
break;
case THREAD_WAITING:
status += "state: waiting\n";
break;
case THREAD_SUSPENDED:
status += "state: suspended\n";
break;
}
status += "script: '" + vm->Filename() + "'\n";
status += "label: '" + vm->Label() + "'\n";
status += "waittill: ";
if (!vm->m_Thread->m_WaitForList) {
status += "(none)\n";
} else {
con_set_enum<const_str, ConList> en = *vm->m_Thread->m_WaitForList;
con_set<const_str, ConList>::Entry *entry;
int i = 0;
for (entry = en.NextElement(); entry != NULL; entry = en.NextElement()) {
str& name = Director.GetString(entry->GetKey());
if (i > 0) {
status += ", ";
}
status += "'" + name + "'";
for (int j = 1; j <= entry->value.NumObjects(); j++) {
Listener *l = entry->value.ObjectAt(j);
if (j > 1) {
status += ", ";
}
if (l) {
status += " on " + str(l->getClassname());
} else {
status += " on (null)";
}
}
i++;
}
status += "\n";
}
gi.Printf(status.c_str());
}