- moved most remaining playsim code into its proper folder.
This commit is contained in:
parent
9045615a7a
commit
2a16fb9d28
69 changed files with 51 additions and 45 deletions
197
src/playsim/fragglescript/t_cmd.cpp
Normal file
197
src/playsim/fragglescript/t_cmd.cpp
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-2017 Christoph Oelckers
|
||||
//
|
||||
// This program 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Emulation for selected Legacy console commands
|
||||
// Unfortunately Legacy allows full access of FS to the console
|
||||
// so everything that gets used by some map has to be emulated...
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "p_local.h"
|
||||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "sc_man.h"
|
||||
#include "g_level.h"
|
||||
#include "r_renderer.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void FS_Gimme(const char * what)
|
||||
{
|
||||
char buffer[80];
|
||||
|
||||
// This is intentionally limited to the few items
|
||||
// it can handle in Legacy.
|
||||
if (!strnicmp(what, "health", 6)) what="health";
|
||||
else if (!strnicmp(what, "ammo", 4)) what="ammo";
|
||||
else if (!strnicmp(what, "armor", 5)) what="greenarmor";
|
||||
else if (!strnicmp(what, "keys", 4)) what="keys";
|
||||
else if (!strnicmp(what, "weapons", 7)) what="weapons";
|
||||
else if (!strnicmp(what, "chainsaw", 8)) what="chainsaw";
|
||||
else if (!strnicmp(what, "shotgun", 7)) what="shotgun";
|
||||
else if (!strnicmp(what, "supershotgun", 12)) what="supershotgun";
|
||||
else if (!strnicmp(what, "rocket", 6)) what="rocketlauncher";
|
||||
else if (!strnicmp(what, "plasma", 6)) what="plasmarifle";
|
||||
else if (!strnicmp(what, "bfg", 3)) what="BFG9000";
|
||||
else if (!strnicmp(what, "chaingun", 8)) what="chaingun";
|
||||
else if (!strnicmp(what, "berserk", 7)) what="Berserk";
|
||||
else if (!strnicmp(what, "map", 3)) what="Allmap";
|
||||
else if (!strnicmp(what, "fullmap", 7)) what="Allmap";
|
||||
else return;
|
||||
|
||||
mysnprintf(buffer, countof(buffer), "give %.72s", what);
|
||||
AddCommandString(buffer);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FS_MapCmd(FLevelLocals *Level, FScanner &sc)
|
||||
{
|
||||
char nextmap[9];
|
||||
int NextSkill = -1;
|
||||
int flags = CHANGELEVEL_RESETINVENTORY|CHANGELEVEL_RESETHEALTH;
|
||||
if (dmflags & DF_NO_MONSTERS)
|
||||
flags |= CHANGELEVEL_NOMONSTERS;
|
||||
sc.MustGetString();
|
||||
strncpy (nextmap, sc.String, 8);
|
||||
nextmap[8]=0;
|
||||
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (sc.Compare("-skill"))
|
||||
{
|
||||
sc.MustGetNumber();
|
||||
NextSkill = clamp<int>(sc.Number-1, 0, AllSkills.Size()-1);
|
||||
}
|
||||
else if (sc.Compare("-monsters"))
|
||||
{
|
||||
sc.MustGetNumber();
|
||||
if (sc.Number)
|
||||
flags &= ~CHANGELEVEL_NOMONSTERS;
|
||||
else
|
||||
flags |= CHANGELEVEL_NOMONSTERS;
|
||||
}
|
||||
else if (sc.Compare("-noresetplayers"))
|
||||
{
|
||||
flags &= ~(CHANGELEVEL_RESETINVENTORY|CHANGELEVEL_RESETHEALTH);
|
||||
}
|
||||
}
|
||||
Level->ChangeLevel(nextmap, 0, flags, NextSkill);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FS_EmulateCmd(FLevelLocals *Level, char * string)
|
||||
{
|
||||
FScanner sc;
|
||||
sc.OpenMem("RUNCMD", string, (int)strlen(string));
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (sc.Compare("GIMME"))
|
||||
{
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (!sc.Compare(";")) FS_Gimme(sc.String);
|
||||
else break;
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("ALLOWJUMP"))
|
||||
{
|
||||
sc.MustGetNumber();
|
||||
if (sc.Number) dmflags = dmflags & ~DF_NO_JUMP;
|
||||
else dmflags=dmflags | DF_NO_JUMP;
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (sc.Compare(";")) break;
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("gravity"))
|
||||
{
|
||||
sc.MustGetFloat();
|
||||
Level->gravity=(float)(sc.Float*800);
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (sc.Compare(";")) break;
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("viewheight"))
|
||||
{
|
||||
sc.MustGetFloat();
|
||||
double playerviewheight = sc.Float;
|
||||
for (auto p : Level->Players)
|
||||
{
|
||||
// No, this is not correct. But this is the way Legacy WADs expect it to be handled!
|
||||
if (p->mo != nullptr) p->mo->FloatVar(NAME_ViewHeight) = playerviewheight;
|
||||
p->viewheight = playerviewheight;
|
||||
p->Uncrouch();
|
||||
}
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (sc.Compare(";")) break;
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("map"))
|
||||
{
|
||||
FS_MapCmd(Level, sc);
|
||||
}
|
||||
else if (sc.Compare("gr_fogdensity"))
|
||||
{
|
||||
sc.MustGetNumber();
|
||||
// Using this disables most MAPINFO fog options!
|
||||
Level->fogdensity = sc.Number * 70 / 400;
|
||||
Level->outsidefogdensity = 0;
|
||||
Level->skyfog = 0;
|
||||
Level->info->outsidefog = 0;
|
||||
}
|
||||
else if (sc.Compare("gr_fogcolor"))
|
||||
{
|
||||
sc.MustGetString();
|
||||
Level->fadeto = (uint32_t)strtoull(sc.String, NULL, 16);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Skip unhandled commands
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (sc.Compare(";")) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/playsim/fragglescript/t_fs.h
Normal file
15
src/playsim/fragglescript/t_fs.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#ifndef T_FS_H
|
||||
#define T_FS_H
|
||||
|
||||
// global FS interface
|
||||
|
||||
struct MapData;
|
||||
class AActor;
|
||||
|
||||
void T_PreprocessScripts(FLevelLocals *Level);
|
||||
void T_LoadScripts(FLevelLocals *Level, MapData * map);
|
||||
void T_AddSpawnedThing(FLevelLocals *Level, AActor * );
|
||||
bool T_RunScript(FLevelLocals *l, int snum, AActor * t_trigger);
|
||||
|
||||
#endif
|
||||
4237
src/playsim/fragglescript/t_func.cpp
Normal file
4237
src/playsim/fragglescript/t_func.cpp
Normal file
File diff suppressed because it is too large
Load diff
317
src/playsim/fragglescript/t_load.cpp
Normal file
317
src/playsim/fragglescript/t_load.cpp
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2002-2017 Christoph Oelckers
|
||||
//
|
||||
// This program 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// FraggleScript loader
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
#include "w_wad.h"
|
||||
#include "g_level.h"
|
||||
#include "s_sound.h"
|
||||
#include "r_sky.h"
|
||||
#include "t_script.h"
|
||||
#include "cmdlib.h"
|
||||
#include "gi.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "xlat/xlat.h"
|
||||
#include "maploader/maploader.h"
|
||||
|
||||
class FScriptLoader
|
||||
{
|
||||
enum
|
||||
{
|
||||
RT_SCRIPT,
|
||||
RT_INFO,
|
||||
RT_OTHER,
|
||||
} readtype;
|
||||
|
||||
|
||||
int drownflag;
|
||||
bool HasScripts;
|
||||
bool IgnoreInfo;
|
||||
FLevelLocals *Level;
|
||||
|
||||
void ParseInfoCmd(char *line, FString &scriptsrc);
|
||||
public:
|
||||
FScriptLoader(FLevelLocals *l)
|
||||
{
|
||||
Level = l;
|
||||
}
|
||||
bool ParseInfo(MapData * map);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Process the lump to strip all unneeded information from it
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void FScriptLoader::ParseInfoCmd(char *line, FString &scriptsrc)
|
||||
{
|
||||
char *temp;
|
||||
|
||||
// clear any control chars
|
||||
for(temp=line; *temp; temp++) if (*temp<32) *temp=32;
|
||||
|
||||
if(readtype != RT_SCRIPT) // not for scripts
|
||||
{
|
||||
temp = line+strlen(line)-1;
|
||||
|
||||
// strip spaces at the beginning and end of the line
|
||||
while(*temp == ' ') *temp-- = 0;
|
||||
while(*line == ' ') line++;
|
||||
|
||||
if(!*line) return;
|
||||
|
||||
if((line[0] == '/' && line[1] == '/') || // comment
|
||||
line[0] == '#' || line[0] == ';') return;
|
||||
}
|
||||
|
||||
if(*line == '[') // a new section seperator
|
||||
{
|
||||
line++;
|
||||
|
||||
if(!strnicmp(line, "scripts", 7))
|
||||
{
|
||||
readtype = RT_SCRIPT;
|
||||
HasScripts = true; // has scripts
|
||||
}
|
||||
else if (!strnicmp(line, "level info", 10))
|
||||
{
|
||||
readtype = RT_INFO;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (readtype==RT_SCRIPT)
|
||||
{
|
||||
scriptsrc << line << '\n';
|
||||
}
|
||||
else if (readtype==RT_INFO)
|
||||
{
|
||||
// Read the usable parts of the level info header
|
||||
// and ignore the rest.
|
||||
FScanner sc;
|
||||
sc.OpenMem("LEVELINFO", line, (int)strlen(line));
|
||||
sc.SetCMode(true);
|
||||
sc.MustGetString();
|
||||
if (sc.Compare("levelname"))
|
||||
{
|
||||
char * beg = strchr(line, '=')+1;
|
||||
while (*beg<=' ') beg++;
|
||||
char * comm = strstr(beg, "//");
|
||||
if (comm) *comm=0;
|
||||
Level->LevelName = beg;
|
||||
}
|
||||
else if (sc.Compare("partime"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetNumber();
|
||||
Level->partime=sc.Number;
|
||||
}
|
||||
else if (sc.Compare("music"))
|
||||
{
|
||||
bool FS_ChangeMusic(const char * string);
|
||||
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetString();
|
||||
if (!FS_ChangeMusic(sc.String))
|
||||
{
|
||||
S_ChangeMusic(Level->Music, Level->musicorder);
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("skyname"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetString();
|
||||
|
||||
Level->skytexture1 = Level->skytexture2 = TexMan.GetTextureID (sc.String, ETextureType::Wall, FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_ReturnFirst);
|
||||
InitSkyMap (Level);
|
||||
}
|
||||
else if (sc.Compare("interpic"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetString();
|
||||
Level->info->ExitPic = sc.String;
|
||||
}
|
||||
else if (sc.Compare("gravity"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetNumber();
|
||||
Level->gravity=sc.Number*8.f;
|
||||
}
|
||||
else if (sc.Compare("nextlevel"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetString();
|
||||
Level->NextMap = sc.String;
|
||||
}
|
||||
else if (sc.Compare("nextsecret"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetString();
|
||||
Level->NextSecretMap = sc.String;
|
||||
}
|
||||
else if (sc.Compare("drown"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetNumber();
|
||||
drownflag=!!sc.Number;
|
||||
}
|
||||
else if (sc.Compare("consolecmd"))
|
||||
{
|
||||
char * beg = strchr(line, '=')+1;
|
||||
while (*beg<' ') beg++;
|
||||
char * comm = strstr(beg, "//");
|
||||
if (comm) *comm=0;
|
||||
FS_EmulateCmd(Level, beg);
|
||||
}
|
||||
else if (sc.Compare("ignore"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetNumber();
|
||||
IgnoreInfo=!!sc.Number;
|
||||
}
|
||||
// Ignore anything unknows
|
||||
sc.Close();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Loads the scripts for the current map
|
||||
// Initializes all FS data
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool FScriptLoader::ParseInfo(MapData * map)
|
||||
{
|
||||
char *lump;
|
||||
char *rover;
|
||||
char *startofline;
|
||||
int lumpsize;
|
||||
bool fsglobal=false;
|
||||
FString scriptsrc;
|
||||
|
||||
// Global initializazion if not done yet.
|
||||
static bool done=false;
|
||||
|
||||
// Load the script lump
|
||||
IgnoreInfo = false;
|
||||
lumpsize = map->Size(0);
|
||||
if (lumpsize==0)
|
||||
{
|
||||
// Try a global FS lump
|
||||
int lumpnum=Wads.CheckNumForName("FSGLOBAL");
|
||||
if (lumpnum<0) return false;
|
||||
lumpsize=Wads.LumpLength(lumpnum);
|
||||
if (lumpsize==0) return false;
|
||||
fsglobal=true;
|
||||
lump=new char[lumpsize+3];
|
||||
Wads.ReadLump(lumpnum,lump);
|
||||
}
|
||||
else
|
||||
{
|
||||
lump=new char[lumpsize+3];
|
||||
map->Read(0, lump);
|
||||
}
|
||||
// Append a new line. The parser likes to crash when the last character is a valid token.
|
||||
lump[lumpsize]='\n';
|
||||
lump[lumpsize+1]='\r';
|
||||
lump[lumpsize+2]=0;
|
||||
lumpsize+=2;
|
||||
|
||||
rover = startofline = lump;
|
||||
HasScripts=false;
|
||||
drownflag=-1;
|
||||
|
||||
readtype = RT_OTHER;
|
||||
while(rover < lump+lumpsize)
|
||||
{
|
||||
if(*rover == '\n') // end of line
|
||||
{
|
||||
*rover = 0; // make it an end of string (0)
|
||||
if (!IgnoreInfo) ParseInfoCmd(startofline, scriptsrc);
|
||||
startofline = rover+1; // next line
|
||||
*rover = '\n'; // back to end of line
|
||||
}
|
||||
rover++;
|
||||
}
|
||||
if (HasScripts)
|
||||
{
|
||||
if (Level->FraggleScriptThinker)
|
||||
{
|
||||
I_Error("Only one FraggleThinker is allowed to exist at a time.\nCheck your code.");
|
||||
}
|
||||
|
||||
auto th = Level->CreateThinker<DFraggleThinker>();
|
||||
th->LevelScript->data = copystring(scriptsrc.GetChars());
|
||||
Level->FraggleScriptThinker = th;
|
||||
|
||||
if (drownflag==-1) drownflag = (Level->maptype != MAPTYPE_DOOM || fsglobal);
|
||||
if (!drownflag) Level->airsupply=0; // Legacy doesn't to water damage so we need to check if it has to be disabled here.
|
||||
}
|
||||
|
||||
|
||||
delete[] lump;
|
||||
return HasScripts;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Starts the level info parser
|
||||
// and patches the global linedef translation table
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void T_LoadScripts(FLevelLocals *Level, MapData *map)
|
||||
{
|
||||
FScriptLoader parser(Level);
|
||||
bool HasScripts = parser.ParseInfo(map);
|
||||
|
||||
// Hack for Legacy compatibility: Since 272 is normally an MBF sky transfer we have to patch it.
|
||||
// It could be done with an additional translator but that would be sub-optimal for the user.
|
||||
// To handle this the default translator defines the proper Legacy type at index 270.
|
||||
// This code then then swaps 270 and 272 - but only if this is either Doom or Heretic and
|
||||
// the default translator is being used.
|
||||
// Custom translators will not be patched.
|
||||
if ((gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Heretic) && Level->info->Translator.IsEmpty() &&
|
||||
Level->maptype == MAPTYPE_DOOM && Level->Translator->SimpleLineTranslations.Size() > 272 && Level->Translator->SimpleLineTranslations[272 - 2*HasScripts].special == FS_Execute)
|
||||
{
|
||||
std::swap(Level->Translator->SimpleLineTranslations[270], Level->Translator->SimpleLineTranslations[272]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Adds an actor to the list of spawned things
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void T_AddSpawnedThing(FLevelLocals *Level, AActor * ac)
|
||||
{
|
||||
if (Level->FraggleScriptThinker)
|
||||
{
|
||||
auto &SpawnedThings = Level->FraggleScriptThinker->SpawnedThings;
|
||||
SpawnedThings.Push(GC::ReadBarrier(ac));
|
||||
}
|
||||
}
|
||||
636
src/playsim/fragglescript/t_oper.cpp
Normal file
636
src/playsim/fragglescript/t_oper.cpp
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Operators
|
||||
//
|
||||
// Handler code for all the operators. The 'other half'
|
||||
// of the parsing.
|
||||
//
|
||||
// By Simon Howard
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
/* includes ************************/
|
||||
#include "t_script.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
|
||||
#define evaluate_leftnright(a, b, c) {\
|
||||
EvaluateExpression(left, (a), (b)-1); \
|
||||
EvaluateExpression(right, (b)+1, (c)); }\
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FParser::operator_t FParser::operators[]=
|
||||
{
|
||||
{"=", &FParser::OPequals, backward},
|
||||
{"||", &FParser::OPor, forward},
|
||||
{"&&", &FParser::OPand, forward},
|
||||
{"|", &FParser::OPor_bin, forward},
|
||||
{"&", &FParser::OPand_bin, forward},
|
||||
{"==", &FParser::OPcmp, forward},
|
||||
{"!=", &FParser::OPnotcmp, forward},
|
||||
{"<", &FParser::OPlessthan, forward},
|
||||
{">", &FParser::OPgreaterthan, forward},
|
||||
{"<=", &FParser::OPlessthanorequal, forward},
|
||||
{">=", &FParser::OPgreaterthanorequal, forward},
|
||||
|
||||
{"+", &FParser::OPplus, forward},
|
||||
{"-", &FParser::OPminus, forward},
|
||||
{"*", &FParser::OPmultiply, forward},
|
||||
{"/", &FParser::OPdivide, forward},
|
||||
{"%", &FParser::OPremainder, forward},
|
||||
{"~", &FParser::OPnot_bin, forward}, // haleyjd
|
||||
{"!", &FParser::OPnot, forward},
|
||||
{"++", &FParser::OPincrement, forward},
|
||||
{"--", &FParser::OPdecrement, forward},
|
||||
{".", &FParser::OPstructure, forward},
|
||||
};
|
||||
|
||||
int FParser::num_operators = sizeof(FParser::operators) / sizeof(FParser::operator_t);
|
||||
|
||||
/***************** logic *********************/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPequals(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
DFsVariable *var;
|
||||
|
||||
var = Script->FindVariable(Tokens[start], Level->FraggleScriptThinker->GlobalScript);
|
||||
|
||||
if(var)
|
||||
{
|
||||
EvaluateExpression(result, n+1, stop);
|
||||
var->SetValue(Level, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[start]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPor(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
int exprtrue = false;
|
||||
|
||||
// if first is true, do not evaluate the second
|
||||
|
||||
EvaluateExpression(result, start, n-1);
|
||||
|
||||
if(intvalue(result))
|
||||
exprtrue = true;
|
||||
else
|
||||
{
|
||||
EvaluateExpression(result, n+1, stop);
|
||||
exprtrue = !!intvalue(result);
|
||||
}
|
||||
|
||||
result.type = svt_int;
|
||||
result.value.i = exprtrue;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPand(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
int exprtrue = true;
|
||||
// if first is false, do not eval second
|
||||
|
||||
EvaluateExpression(result, start, n-1);
|
||||
|
||||
if(!intvalue(result) )
|
||||
exprtrue = false;
|
||||
else
|
||||
{
|
||||
EvaluateExpression(result, n+1, stop);
|
||||
exprtrue = !!intvalue(result);
|
||||
}
|
||||
|
||||
result.type = svt_int;
|
||||
result.value.i = exprtrue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPcmp(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
result.type = svt_int; // always an int returned
|
||||
|
||||
if(left.type == svt_string && right.type == svt_string)
|
||||
{
|
||||
result.value.i = !strcmp(left.string, right.string);
|
||||
return;
|
||||
}
|
||||
|
||||
// haleyjd: direct mobj comparison when both are mobj
|
||||
if(left.type == svt_mobj && right.type == svt_mobj)
|
||||
{
|
||||
// we can safely assume reference equivalency for
|
||||
// AActor's in all cases since they are static for the
|
||||
// duration of a level
|
||||
result.value.i = (left.value.mobj == right.value.mobj);
|
||||
return;
|
||||
}
|
||||
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
{
|
||||
result.value.i = (fixedvalue(left) == fixedvalue(right));
|
||||
return;
|
||||
}
|
||||
|
||||
result.value.i = (intvalue(left) == intvalue(right));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPnotcmp(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
OPcmp(result, start, n, stop);
|
||||
result.type = svt_int;
|
||||
result.value.i = !result.value.i;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPlessthan(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
result.type = svt_int;
|
||||
|
||||
// haleyjd: 8-17
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
result.value.i = (fixedvalue(left) < fixedvalue(right));
|
||||
else
|
||||
result.value.i = (intvalue(left) < intvalue(right));
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPgreaterthan(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
// haleyjd: 8-17
|
||||
result.type = svt_int;
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
result.value.i = (fixedvalue(left) > fixedvalue(right));
|
||||
else
|
||||
result.value.i = (intvalue(left) > intvalue(right));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPnot(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
EvaluateExpression(result, n+1, stop);
|
||||
|
||||
result.value.i = !intvalue(result);
|
||||
result.type = svt_int;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPplus(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
if (left.type == svt_string)
|
||||
{
|
||||
if (right.type == svt_string)
|
||||
{
|
||||
result.string.Format("%s%s", left.string.GetChars(), right.string.GetChars());
|
||||
}
|
||||
else if (right.type == svt_fixed)
|
||||
{
|
||||
result.string.Format("%s%4.4f", left.string.GetChars(), floatvalue(right));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.string.Format("%s%i", left.string.GetChars(), intvalue(right));
|
||||
}
|
||||
result.type = svt_string;
|
||||
}
|
||||
// haleyjd: 8-17
|
||||
else if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
{
|
||||
result.type = svt_fixed;
|
||||
result.value.f = fixedvalue(left) + fixedvalue(right);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) + intvalue(right);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPminus(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
// do they mean minus as in '-1' rather than '2-1'?
|
||||
if(start == n)
|
||||
{
|
||||
// kinda hack, hehe
|
||||
EvaluateExpression(right, n+1, stop);
|
||||
}
|
||||
else
|
||||
{
|
||||
evaluate_leftnright(start, n, stop);
|
||||
}
|
||||
|
||||
// haleyjd: 8-17
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
{
|
||||
result.type = svt_fixed;
|
||||
result.value.f = fixedvalue(left) - fixedvalue(right);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) - intvalue(right);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPmultiply(svalue_t &result,int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
// haleyjd: 8-17
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
{
|
||||
result.setDouble(floatvalue(left) * floatvalue(right));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) * intvalue(right);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPdivide(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
// haleyjd: 8-17
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
{
|
||||
auto fr = floatvalue(right);
|
||||
|
||||
if(fr == 0)
|
||||
script_error("divide by zero\n");
|
||||
else
|
||||
{
|
||||
result.setDouble(floatvalue(left) / fr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ir = intvalue(right);
|
||||
|
||||
if(!ir)
|
||||
script_error("divide by zero\n");
|
||||
else
|
||||
{
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) / ir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPremainder(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
int ir;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
if(!(ir = intvalue(right)))
|
||||
script_error("divide by zero\n");
|
||||
else
|
||||
{
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) % ir;
|
||||
}
|
||||
}
|
||||
|
||||
/********** binary operators **************/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPor_bin(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) | intvalue(right);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPand_bin(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
result.type = svt_int;
|
||||
result.value.i = intvalue(left) & intvalue(right);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPnot_bin(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
EvaluateExpression(result, n+1, stop);
|
||||
|
||||
result.value.i = ~intvalue(result);
|
||||
result.type = svt_int;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPincrement(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
if(start == n) // ++n
|
||||
{
|
||||
DFsVariable *var;
|
||||
|
||||
var = Script->FindVariable(Tokens[stop], Level->FraggleScriptThinker->GlobalScript);
|
||||
if(!var)
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[stop]);
|
||||
}
|
||||
var->GetValue(result);
|
||||
|
||||
// haleyjd
|
||||
if(var->type != svt_fixed)
|
||||
{
|
||||
result.value.i = intvalue(result) + 1;
|
||||
result.type = svt_int;
|
||||
var->SetValue(Level, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.setDouble(floatvalue(result)+1);
|
||||
var->SetValue(Level, result);
|
||||
}
|
||||
}
|
||||
else if(stop == n) // n++
|
||||
{
|
||||
svalue_t newvalue;
|
||||
DFsVariable *var;
|
||||
|
||||
var = Script->FindVariable(Tokens[start], Level->FraggleScriptThinker->GlobalScript);
|
||||
if(!var)
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[start]);
|
||||
}
|
||||
var->GetValue(result);
|
||||
|
||||
// haleyjd
|
||||
if(var->type != svt_fixed)
|
||||
{
|
||||
newvalue.type = svt_int;
|
||||
newvalue.value.i = intvalue(result) + 1;
|
||||
var->SetValue(Level, newvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
newvalue.setDouble(floatvalue(result)+1);
|
||||
var->SetValue(Level, newvalue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
script_error("incorrect arguments to ++ operator\n");
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPdecrement(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
if(start == n) // ++n
|
||||
{
|
||||
DFsVariable *var;
|
||||
|
||||
var = Script->FindVariable(Tokens[stop], Level->FraggleScriptThinker->GlobalScript);
|
||||
if(!var)
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[stop]);
|
||||
}
|
||||
var->GetValue(result);
|
||||
|
||||
// haleyjd
|
||||
if(var->type != svt_fixed)
|
||||
{
|
||||
result.value.i = intvalue(result) - 1;
|
||||
result.type = svt_int;
|
||||
var->SetValue(Level, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.setDouble(floatvalue(result)-1);
|
||||
result.type = svt_fixed;
|
||||
var->SetValue(Level, result);
|
||||
}
|
||||
}
|
||||
else if(stop == n) // n++
|
||||
{
|
||||
svalue_t newvalue;
|
||||
DFsVariable *var;
|
||||
|
||||
var = Script->FindVariable(Tokens[start], Level->FraggleScriptThinker->GlobalScript);
|
||||
if(!var)
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[start]);
|
||||
}
|
||||
var->GetValue(result);
|
||||
|
||||
// haleyjd
|
||||
if(var->type != svt_fixed)
|
||||
{
|
||||
newvalue.type = svt_int;
|
||||
newvalue.value.i = intvalue(result) - 1;
|
||||
var->SetValue(Level, newvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
newvalue.setDouble(floatvalue(result)-1);
|
||||
var->SetValue(Level, newvalue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
script_error("incorrect arguments to ++ operator\n");
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPlessthanorequal(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
result.type = svt_int;
|
||||
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
result.value.i = (fixedvalue(left) <= fixedvalue(right));
|
||||
else
|
||||
result.value.i = (intvalue(left) <= intvalue(right));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FParser::OPgreaterthanorequal(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
svalue_t left, right;
|
||||
|
||||
evaluate_leftnright(start, n, stop);
|
||||
|
||||
result.type = svt_int;
|
||||
|
||||
if(left.type == svt_fixed || right.type == svt_fixed)
|
||||
result.value.i = (fixedvalue(left) >= fixedvalue(right));
|
||||
else
|
||||
result.value.i = (intvalue(left) >= intvalue(right));
|
||||
}
|
||||
|
||||
741
src/playsim/fragglescript/t_parse.cpp
Normal file
741
src/playsim/fragglescript/t_parse.cpp
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
// Copyright(C) 2002-2008 Christoph Oelckers
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Parsing.
|
||||
//
|
||||
// Takes lines of code, or groups of lines and runs them.
|
||||
// The main core of FraggleScript
|
||||
//
|
||||
// By Simon Howard
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
/* includes ************************/
|
||||
#include <stdarg.h>
|
||||
#include "t_script.h"
|
||||
#include "v_text.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
CVAR(Bool, script_debug, false, 0)
|
||||
|
||||
/************ Divide into tokens **************/
|
||||
#define isnum(c) ( ((c)>='0' && (c)<='9') || (c)=='.')
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// NextToken: end this token, go onto the next
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::NextToken()
|
||||
{
|
||||
if(Tokens[NumTokens-1][0] || TokenType[NumTokens-1]==string_)
|
||||
{
|
||||
NumTokens++;
|
||||
Tokens[NumTokens-1] = Tokens[NumTokens-2] + strlen(Tokens[NumTokens-2]) + 1;
|
||||
Tokens[NumTokens-1][0] = 0;
|
||||
}
|
||||
|
||||
// get to the next token, ignoring spaces, newlines,
|
||||
// useless chars, comments etc
|
||||
|
||||
while(1)
|
||||
{
|
||||
// empty whitespace
|
||||
if(*Rover && (*Rover==' ' || *Rover<32))
|
||||
{
|
||||
while((*Rover==' ' || *Rover<32) && *Rover) Rover++;
|
||||
}
|
||||
// end-of-script?
|
||||
if(!*Rover)
|
||||
{
|
||||
if(Tokens[0][0])
|
||||
{
|
||||
// line contains text, but no semicolon: an error
|
||||
script_error("missing ';'\n");
|
||||
}
|
||||
// empty line, end of command-list
|
||||
return;
|
||||
}
|
||||
break; // otherwise
|
||||
}
|
||||
|
||||
if(NumTokens>1 && *Rover == '(' && TokenType[NumTokens-2] == name_)
|
||||
TokenType[NumTokens-2] = function;
|
||||
|
||||
if(*Rover == '{' || *Rover == '}')
|
||||
{
|
||||
if(*Rover == '{')
|
||||
{
|
||||
BraceType = bracket_open;
|
||||
Section = Script->FindSectionStart(Rover);
|
||||
}
|
||||
else // closing brace
|
||||
{
|
||||
BraceType = bracket_close;
|
||||
Section = Script->FindSectionEnd(Rover);
|
||||
}
|
||||
if(!Section)
|
||||
{
|
||||
script_error("section not found!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(*Rover == ':') // label
|
||||
{
|
||||
// ignore the label : reset
|
||||
NumTokens = 1;
|
||||
Tokens[0][0] = 0; TokenType[NumTokens-1] = name_;
|
||||
Rover++; // ignore
|
||||
}
|
||||
else if(*Rover == '\"')
|
||||
{
|
||||
TokenType[NumTokens-1] = string_;
|
||||
if(TokenType[NumTokens-2] == string_) NumTokens--; // join strings
|
||||
Rover++;
|
||||
}
|
||||
else
|
||||
{
|
||||
TokenType[NumTokens-1] = isop(*Rover) ? operator_ : isnum(*Rover) ? number : name_;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// return an escape sequence (prefixed by a '\')
|
||||
// do not use all C escape sequences
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static char escape_sequence(char c)
|
||||
{
|
||||
if(c == 'n') return '\n';
|
||||
if(c == '\\') return '\\';
|
||||
if(c == '"') return '"';
|
||||
if(c == '?') return '?';
|
||||
if(c == 'a') return '\a'; // alert beep
|
||||
if(c == 't') return '\t'; //tab
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// add_char: add one character to the current token
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void add_char(char *tokn, char c)
|
||||
{
|
||||
char *out = tokn + strlen(tokn);
|
||||
|
||||
out[0] = c;
|
||||
out[1] = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// get_tokens.
|
||||
// Take a string, break it into tokens.
|
||||
//
|
||||
// individual tokens are stored inside the tokens[] array
|
||||
// tokentype is also used to hold the type for each token:
|
||||
//
|
||||
// name: a piece of text which starts with an alphabet letter.
|
||||
// probably a variable name. Some are converted into
|
||||
// function types later on in find_brackets
|
||||
// number: a number. like '12' or '1337'
|
||||
// operator: an operator such as '&&' or '+'. All FraggleScript
|
||||
// operators are either one character, or two character
|
||||
// (if 2 character, 2 of the same char or ending in '=')
|
||||
// string: a text string that was enclosed in quote "" marks in
|
||||
// the original text
|
||||
// unset: shouldn't ever end up being set really.
|
||||
// function: a function name (found in second stage parsing)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *FParser::GetTokens(char *s)
|
||||
{
|
||||
char *tokn = NULL;
|
||||
|
||||
Rover = s;
|
||||
NumTokens = 1;
|
||||
Tokens[0][0] = 0; TokenType[NumTokens-1] = name_;
|
||||
|
||||
Section = NULL; // default to no section found
|
||||
|
||||
NextToken();
|
||||
LineStart = Rover; // save the start
|
||||
|
||||
if(*Rover)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
tokn = Tokens[NumTokens-1];
|
||||
if(Section)
|
||||
{
|
||||
// a { or } section brace has been found
|
||||
break; // stop parsing now
|
||||
}
|
||||
else if(TokenType[NumTokens-1] != string_)
|
||||
{
|
||||
if(*Rover == ';') break; // check for end of command ';'
|
||||
}
|
||||
|
||||
switch(TokenType[NumTokens-1])
|
||||
{
|
||||
case unset:
|
||||
case string_:
|
||||
while(*Rover != '\"') // dedicated loop for speed
|
||||
{
|
||||
if(*Rover == '\\') // escape sequences
|
||||
{
|
||||
Rover++;
|
||||
if (*Rover>='0' && *Rover<='9')
|
||||
{
|
||||
add_char(tokn, TEXTCOLOR_ESCAPE);
|
||||
add_char(tokn, *Rover+'A'-'0');
|
||||
}
|
||||
else add_char(tokn, escape_sequence(*Rover));
|
||||
}
|
||||
else
|
||||
add_char(tokn, *Rover);
|
||||
Rover++;
|
||||
}
|
||||
Rover++;
|
||||
NextToken(); // end of this token
|
||||
continue;
|
||||
|
||||
case operator_:
|
||||
// all 2-character operators either end in '=' or
|
||||
// are 2 of the same character
|
||||
// do not allow 2-characters for brackets '(' ')'
|
||||
// which are still being considered as operators
|
||||
|
||||
// operators are only 2-char max, do not need
|
||||
// a seperate loop
|
||||
|
||||
if((*tokn && *Rover != '=' && *Rover!=*tokn) ||
|
||||
*tokn == '(' || *tokn == ')')
|
||||
{
|
||||
// end of operator
|
||||
NextToken();
|
||||
continue;
|
||||
}
|
||||
add_char(tokn, *Rover);
|
||||
break;
|
||||
|
||||
case number:
|
||||
|
||||
// haleyjd: 8-17
|
||||
// add while number chars are read
|
||||
|
||||
while(isnum(*Rover)) // dedicated loop
|
||||
add_char(tokn, *Rover++);
|
||||
NextToken();
|
||||
continue;
|
||||
|
||||
case name_:
|
||||
|
||||
// add the chars
|
||||
|
||||
while(!isop(*Rover)) // dedicated loop
|
||||
add_char(tokn, *Rover++);
|
||||
NextToken();
|
||||
continue;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Rover++;
|
||||
}
|
||||
}
|
||||
|
||||
// check for empty last token
|
||||
|
||||
if(!tokn || !tokn[0])
|
||||
{
|
||||
NumTokens = NumTokens - 1;
|
||||
}
|
||||
|
||||
Rover++;
|
||||
return Rover;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PrintTokens: add one character to the current token
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::PrintTokens() // DEBUG
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NumTokens; i++)
|
||||
{
|
||||
Printf("\n'%s' \t\t --", Tokens[i]);
|
||||
switch (TokenType[i])
|
||||
{
|
||||
case string_:
|
||||
Printf("string");
|
||||
break;
|
||||
case operator_:
|
||||
Printf("operator");
|
||||
break;
|
||||
case name_:
|
||||
Printf("name");
|
||||
break;
|
||||
case number:
|
||||
Printf("number");
|
||||
break;
|
||||
case unset:
|
||||
Printf("duh");
|
||||
break;
|
||||
case function:
|
||||
Printf("function name");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Printf("\n");
|
||||
if (Section)
|
||||
Printf("current section: offset %i\n", Section->start_index);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Parses a block of script code
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::Run(char *rover, char *data, char *end)
|
||||
{
|
||||
Rover = rover;
|
||||
try
|
||||
{
|
||||
PrevSection = NULL; // clear it
|
||||
|
||||
while(*Rover) // go through the script executing each statement
|
||||
{
|
||||
// past end of script?
|
||||
if(Rover > end)
|
||||
break;
|
||||
|
||||
PrevSection = Section; // store from prev. statement
|
||||
|
||||
// get the line and tokens
|
||||
GetTokens(Rover);
|
||||
|
||||
if(!NumTokens)
|
||||
{
|
||||
if(Section) // no tokens but a brace
|
||||
{
|
||||
// possible } at end of loop:
|
||||
// refer to spec.c
|
||||
spec_brace();
|
||||
}
|
||||
|
||||
continue; // continue to next statement
|
||||
}
|
||||
|
||||
if(script_debug) PrintTokens(); // debug
|
||||
RunStatement(); // run the statement
|
||||
}
|
||||
}
|
||||
catch (const CFsError &err)
|
||||
{
|
||||
ErrorMessage(err.msg);
|
||||
}
|
||||
catch (const CFsTerminator &)
|
||||
{
|
||||
// The script has signalled that it wants to be terminated in an orderly fashion.
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// decide what to do with it
|
||||
//
|
||||
// NB this stuff is a bit hardcoded:
|
||||
// it could be nicer really but i'm
|
||||
// aiming for speed
|
||||
//
|
||||
// if() and while() will be mistaken for functions
|
||||
// during token processing
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::RunStatement()
|
||||
{
|
||||
if(TokenType[0] == function)
|
||||
{
|
||||
if(!strcmp(Tokens[0], "if"))
|
||||
{
|
||||
Script->lastiftrue = spec_if();
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "elseif"))
|
||||
{
|
||||
if(!PrevSection ||
|
||||
(PrevSection->type != st_if &&
|
||||
PrevSection->type != st_elseif))
|
||||
{
|
||||
script_error("elseif statement without if\n");
|
||||
return;
|
||||
}
|
||||
Script->lastiftrue = spec_elseif(Script->lastiftrue);
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "else"))
|
||||
{
|
||||
if(!PrevSection ||
|
||||
(PrevSection->type != st_if &&
|
||||
PrevSection->type != st_elseif))
|
||||
{
|
||||
script_error("else statement without if\n");
|
||||
return;
|
||||
}
|
||||
spec_else(Script->lastiftrue);
|
||||
Script->lastiftrue = true;
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "while"))
|
||||
{
|
||||
spec_while();
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "for"))
|
||||
{
|
||||
spec_for();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(TokenType[0] == name_)
|
||||
{
|
||||
// NB: goto is a function so is not here
|
||||
|
||||
// Allow else without '()'
|
||||
if (!strcmp(Tokens[0], "else"))
|
||||
{
|
||||
if(!PrevSection ||
|
||||
(PrevSection->type != st_if &&
|
||||
PrevSection->type != st_elseif))
|
||||
{
|
||||
script_error("else statement without if\n");
|
||||
return;
|
||||
}
|
||||
spec_else(Script->lastiftrue);
|
||||
Script->lastiftrue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// if a variable declaration, return now
|
||||
if(spec_variable()) return;
|
||||
}
|
||||
|
||||
// just a plain expression
|
||||
svalue_t scratch;
|
||||
EvaluateExpression(scratch,0, NumTokens-1);
|
||||
}
|
||||
|
||||
/***************** Evaluating Expressions ************************/
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// find a token, ignoring things in brackets
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FParser::FindOperator(int start, int stop, const char *value)
|
||||
{
|
||||
int i;
|
||||
int bracketlevel = 0;
|
||||
|
||||
for(i=start; i<=stop; i++)
|
||||
{
|
||||
// only interested in operators
|
||||
if(TokenType[i] != operator_) continue;
|
||||
|
||||
// use bracketlevel to check the number of brackets
|
||||
// which we are inside
|
||||
bracketlevel += Tokens[i][0]=='(' ? 1 :
|
||||
Tokens[i][0]==')' ? -1 : 0;
|
||||
|
||||
// only check when we are not in brackets
|
||||
if(!bracketlevel && !strcmp(value, Tokens[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// go through tokens the same as find_operator, but backwards
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FParser::FindOperatorBackwards(int start, int stop, const char *value)
|
||||
{
|
||||
int i;
|
||||
int bracketlevel = 0;
|
||||
|
||||
for(i=stop; i>=start; i--) // check backwards
|
||||
{
|
||||
// operators only
|
||||
|
||||
if(TokenType[i] != operator_) continue;
|
||||
|
||||
// use bracketlevel to check the number of brackets
|
||||
// which we are inside
|
||||
|
||||
bracketlevel += Tokens[i][0]=='(' ? -1 :
|
||||
Tokens[i][0]==')' ? 1 : 0;
|
||||
|
||||
// only check when we are not in brackets
|
||||
// if we find what we want, return it
|
||||
|
||||
if(!bracketlevel && !strcmp(value, Tokens[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// simple_evaluate is used once evalute_expression gets to the level
|
||||
// where it is evaluating just one token
|
||||
//
|
||||
// converts number tokens into svalue_ts and returns
|
||||
// the same with string tokens
|
||||
// name tokens are considered to be variables and
|
||||
// attempts are made to find the value of that variable
|
||||
// command tokens are executed (does not return a svalue_t)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::SimpleEvaluate(svalue_t &returnvar, int n)
|
||||
{
|
||||
DFsVariable *var;
|
||||
|
||||
switch(TokenType[n])
|
||||
{
|
||||
case string_:
|
||||
returnvar.type = svt_string;
|
||||
returnvar.string = Tokens[n];
|
||||
break;
|
||||
|
||||
case number:
|
||||
if(strchr(Tokens[n], '.'))
|
||||
{
|
||||
returnvar.setDouble(atof(Tokens[n]));
|
||||
}
|
||||
else
|
||||
{
|
||||
returnvar.type = svt_int;
|
||||
returnvar.value.i = atoi(Tokens[n]);
|
||||
}
|
||||
break;
|
||||
|
||||
case name_:
|
||||
var = Script->FindVariable(Tokens[n], Level->FraggleScriptThinker->GlobalScript);
|
||||
if(!var)
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[n]);
|
||||
}
|
||||
else var->GetValue(returnvar);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// pointless_brackets checks to see if there are brackets surrounding
|
||||
// an expression. eg. "(2+4)" is the same as just "2+4"
|
||||
//
|
||||
// because of the recursive nature of evaluate_expression, this function is
|
||||
// neccesary as evaluating expressions such as "2*(2+4)" will inevitably
|
||||
// lead to evaluating "(2+4)"
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::PointlessBrackets(int *start, int *stop)
|
||||
{
|
||||
int bracket_level, i;
|
||||
|
||||
// check that the start and end are brackets
|
||||
|
||||
while(Tokens[*start][0] == '(' && Tokens[*stop][0] == ')')
|
||||
{
|
||||
|
||||
bracket_level = 0;
|
||||
|
||||
// confirm there are pointless brackets..
|
||||
// if they are, bracket_level will only get to 0
|
||||
// at the last token
|
||||
// check up to <*stop rather than <=*stop to ignore
|
||||
// the last token
|
||||
|
||||
for(i = *start; i<*stop; i++)
|
||||
{
|
||||
if(TokenType[i] != operator_) continue; // ops only
|
||||
bracket_level += (Tokens[i][0] == '(');
|
||||
bracket_level -= (Tokens[i][0] == ')');
|
||||
if(bracket_level == 0) return; // stop if braces stop before end
|
||||
}
|
||||
|
||||
// move both brackets in
|
||||
|
||||
*start = *start + 1;
|
||||
*stop = *stop - 1;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// evaluate_expresion is the basic function used to evaluate
|
||||
// a FraggleScript expression.
|
||||
// start and stop denote the tokens which are to be evaluated.
|
||||
//
|
||||
// works by recursion: it finds operators in the expression
|
||||
// (checking for each in turn), then splits the expression into
|
||||
// 2 parts, left and right of the operator found.
|
||||
// The handler function for that particular operator is then
|
||||
// called, which in turn calls evaluate_expression again to
|
||||
// evaluate each side. When it reaches the level of being asked
|
||||
// to evaluate just 1 token, it calls simple_evaluate
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::EvaluateExpression(svalue_t &result, int start, int stop)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
// possible pointless brackets
|
||||
if(TokenType[start] == operator_ && TokenType[stop] == operator_)
|
||||
PointlessBrackets(&start, &stop);
|
||||
|
||||
if(start == stop) // only 1 thing to evaluate
|
||||
{
|
||||
SimpleEvaluate(result, start);
|
||||
return;
|
||||
}
|
||||
|
||||
// go through each operator in order of precedence
|
||||
for(i=0; i<num_operators; i++)
|
||||
{
|
||||
// check backwards for the token. it has to be
|
||||
// done backwards for left-to-right reading: eg so
|
||||
// 5-3-2 is (5-3)-2 not 5-(3-2)
|
||||
|
||||
if (operators[i].direction==forward)
|
||||
{
|
||||
n = FindOperatorBackwards(start, stop, operators[i].string);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = FindOperator(start, stop, operators[i].string);
|
||||
}
|
||||
|
||||
if( n != -1)
|
||||
{
|
||||
// call the operator function and evaluate this chunk of tokens
|
||||
(this->*operators[i].handler)(result, start, n, stop);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(TokenType[start] == function)
|
||||
{
|
||||
EvaluateFunction(result, start, stop);
|
||||
return;
|
||||
}
|
||||
|
||||
// error ?
|
||||
{
|
||||
FString tempstr;
|
||||
|
||||
for(i=start; i<=stop; i++) tempstr << Tokens[i] << ' ';
|
||||
script_error("couldnt evaluate expression: %s\n",tempstr.GetChars());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// intercepts an error message and inserts script/line information
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FS_Error(const char *error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char errortext[MAX_ERRORTEXT];
|
||||
|
||||
va_start(argptr, error);
|
||||
myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr);
|
||||
va_end(argptr);
|
||||
throw CFraggleScriptError(errortext);
|
||||
}
|
||||
|
||||
|
||||
void FParser::ErrorMessage(FString msg)
|
||||
{
|
||||
int linenum = 0;
|
||||
|
||||
// find the line number
|
||||
if(Rover >= Script->data && Rover <= Script->data+Script->len)
|
||||
{
|
||||
char *temp;
|
||||
for(temp = Script->data; temp<LineStart; temp++)
|
||||
if(*temp == '\n') linenum++; // count EOLs
|
||||
}
|
||||
|
||||
//lineinfo.Format("Script %d, line %d: ", Script->scriptnum, linenum);
|
||||
FS_Error("Script %d, line %d: %s", Script->scriptnum, linenum, msg.GetChars());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// throws an error message
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void script_error(const char *s, ...)
|
||||
{
|
||||
FString composed;
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
composed.VFormat(s, args);
|
||||
throw CFsError(composed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// EOF
|
||||
438
src/playsim/fragglescript/t_prepro.cpp
Normal file
438
src/playsim/fragglescript/t_prepro.cpp
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Preprocessor.
|
||||
//
|
||||
// The preprocessor must be called when the script is first loaded.
|
||||
// It performs 2 functions:
|
||||
// 1: blank out comments (which could be misinterpreted)
|
||||
// 2: makes a list of all the sections held within {} braces
|
||||
// 3: 'dry' runs the script: goes thru each statement and
|
||||
// sets the types of all the DFsSection's in the script
|
||||
// 4: Saves locations of all goto() labels
|
||||
//
|
||||
// the system of DFsSection's is pretty horrible really, but it works
|
||||
// and its probably the only way i can think of of saving scripts
|
||||
// half-way thru running
|
||||
//
|
||||
// By Simon Howard
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
/* includes ************************/
|
||||
|
||||
#include "t_script.h"
|
||||
#include "w_wad.h"
|
||||
#include "serializer.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// {} sections
|
||||
//
|
||||
// during preprocessing all of the {} sections
|
||||
// are found. these are stored in a hash table
|
||||
// according to their offset in the script.
|
||||
// functions here deal with creating new sections
|
||||
// and finding them from a given offset.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
IMPLEMENT_CLASS(DFsSection, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(DFsSection)
|
||||
IMPLEMENT_POINTER(next)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsSection::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize(arc);
|
||||
arc("type", type)
|
||||
("start_index", start_index)
|
||||
("end_index", end_index)
|
||||
("loop_index", loop_index)
|
||||
("next", next);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *DFsScript::SectionStart(const DFsSection *sec)
|
||||
{
|
||||
return data + sec->start_index;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *DFsScript::SectionEnd(const DFsSection *sec)
|
||||
{
|
||||
return data + sec->end_index;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *DFsScript::SectionLoop(const DFsSection *sec)
|
||||
{
|
||||
return data + sec->loop_index;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::ClearSections()
|
||||
{
|
||||
for(int i=0;i<SECTIONSLOTS;i++)
|
||||
{
|
||||
DFsSection * var = sections[i];
|
||||
while(var)
|
||||
{
|
||||
DFsSection *next = var->next;
|
||||
var->Destroy();
|
||||
var = next;
|
||||
}
|
||||
sections[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// create section
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsSection *DFsScript::NewSection(const char *brace)
|
||||
{
|
||||
int n = section_hash(brace);
|
||||
DFsSection *newsec = Create<DFsSection>();
|
||||
|
||||
newsec->start_index = MakeIndex(brace);
|
||||
newsec->next = sections[n];
|
||||
sections[n] = newsec;
|
||||
GC::WriteBarrier(this, newsec);
|
||||
return newsec;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// find a Section from the location of the starting { brace
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsSection *DFsScript::FindSectionStart(const char *brace)
|
||||
{
|
||||
int n = section_hash(brace);
|
||||
DFsSection *current = sections[n];
|
||||
|
||||
// use the hash table: check the appropriate hash chain
|
||||
|
||||
while(current)
|
||||
{
|
||||
if(SectionStart(current) == brace) return current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return NULL; // not found
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// find a Section from the location of the closing } brace
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsSection *DFsScript::FindSectionEnd(const char *brace)
|
||||
{
|
||||
int n;
|
||||
|
||||
// hash table is no use, they are hashed according to
|
||||
// the offset of the starting brace
|
||||
|
||||
// we have to go through every entry to find from the
|
||||
// ending brace
|
||||
|
||||
for(n=0; n<SECTIONSLOTS; n++) // check all sections in all chains
|
||||
{
|
||||
DFsSection *current = sections[n];
|
||||
|
||||
while(current)
|
||||
{
|
||||
if(SectionEnd(current) == brace) return current; // found it
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
return NULL; // not found
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// preproocessor main loop
|
||||
//
|
||||
// This works by recursion. when a { opening
|
||||
// brace is found, another instance of the
|
||||
// function is called for the data inside
|
||||
// the {} section.
|
||||
// At the same time, the sections are noted
|
||||
// down and hashed. Goto() labels are noted
|
||||
// down, and comments are blanked out
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *DFsScript::ProcessFindChar(char *datap, char find)
|
||||
{
|
||||
while(*datap)
|
||||
{
|
||||
if(*datap==find) return datap;
|
||||
if(*datap=='\"') // found a quote: ignore stuff in it
|
||||
{
|
||||
datap++;
|
||||
while(*datap && *datap != '\"')
|
||||
{
|
||||
// escape sequence ?
|
||||
if(*datap=='\\') datap++;
|
||||
datap++;
|
||||
}
|
||||
// error: end of script in a constant
|
||||
if(!*datap) return NULL;
|
||||
}
|
||||
|
||||
// comments: blank out
|
||||
|
||||
if(*datap=='/' && *(datap+1)=='*') // /* -- */ comment
|
||||
{
|
||||
while(*datap && (*datap != '*' || *(datap+1) != '/') )
|
||||
{
|
||||
*datap=' '; datap++;
|
||||
}
|
||||
if(*datap)
|
||||
*datap = *(datap+1) = ' '; // blank the last bit
|
||||
else
|
||||
{
|
||||
// script terminated in comment
|
||||
script_error("script terminated inside comment\n");
|
||||
}
|
||||
}
|
||||
if(*datap=='/' && *(datap+1)=='/') // // -- comment
|
||||
{
|
||||
while(*datap != '\n')
|
||||
{
|
||||
*datap=' '; datap++; // blank out
|
||||
}
|
||||
}
|
||||
|
||||
/********** labels ****************/
|
||||
|
||||
// labels are also found during the
|
||||
// preprocessing. these are of the form
|
||||
//
|
||||
// label_name:
|
||||
//
|
||||
// and are used for the goto function.
|
||||
// goto labels are stored as variables.
|
||||
|
||||
if(*datap==':' && scriptnum != -1) // not in global scripts
|
||||
{
|
||||
char *labelptr = datap-1;
|
||||
|
||||
while(!isop(*labelptr)) labelptr--;
|
||||
|
||||
FString labelname(labelptr+1, strcspn(labelptr+1, ":"));
|
||||
|
||||
if (labelname.Len() == 0)
|
||||
{
|
||||
Printf(PRINT_BOLD,"Script %d: ':' encountrered in incorrect position!\n",scriptnum);
|
||||
}
|
||||
|
||||
DFsVariable *newlabel = NewVariable(labelname, svt_label);
|
||||
newlabel->value.i = MakeIndex(labelptr);
|
||||
}
|
||||
|
||||
if(*datap=='{') // { -- } sections: add 'em
|
||||
{
|
||||
DFsSection *newsec = NewSection(datap);
|
||||
|
||||
newsec->type = st_empty;
|
||||
// find the ending } and save
|
||||
char * theend = ProcessFindChar(datap+1, '}');
|
||||
if(!theend)
|
||||
{ // brace not found
|
||||
// This is fatal because it will cause a crash later
|
||||
// if the game isn't terminated.
|
||||
I_Error("Script %d: section error: no ending brace\n", scriptnum);
|
||||
}
|
||||
|
||||
newsec->end_index = MakeIndex(theend);
|
||||
// continue from the end of the section
|
||||
datap = theend;
|
||||
}
|
||||
datap++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// second stage parsing
|
||||
//
|
||||
// second stage preprocessing considers the script
|
||||
// in terms of tokens rather than as plain data.
|
||||
//
|
||||
// we 'dry' run the script: go thru each statement and
|
||||
// collect types for Sections
|
||||
//
|
||||
// this is an important thing to do, it cannot be done
|
||||
// at runtime for 2 reasons:
|
||||
// 1. gotos() jumping inside loops will pass thru
|
||||
// the end of the loop
|
||||
// 2. savegames. loading a script saved inside a
|
||||
// loop will let it pass thru the loop
|
||||
//
|
||||
// this is basically a cut-down version of the normal
|
||||
// parsing loop.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::DryRunScript(FLevelLocals *Level)
|
||||
{
|
||||
char *end = data + len;
|
||||
char *rover = data;
|
||||
|
||||
// allocate space for the tokens
|
||||
FParser parse(Level, this);
|
||||
try
|
||||
{
|
||||
while(rover < end && *rover)
|
||||
{
|
||||
rover = parse.GetTokens(rover);
|
||||
|
||||
if(!parse.NumTokens) continue;
|
||||
|
||||
if(parse.Section && parse.TokenType[0] == function)
|
||||
{
|
||||
if(!strcmp(parse.Tokens[0], "if"))
|
||||
{
|
||||
parse.Section->type = st_if;
|
||||
continue;
|
||||
}
|
||||
else if(!strcmp(parse.Tokens[0], "elseif")) // haleyjd: SoM's else code
|
||||
{
|
||||
parse.Section->type = st_elseif;
|
||||
continue;
|
||||
}
|
||||
else if(!strcmp(parse.Tokens[0], "else"))
|
||||
{
|
||||
parse.Section->type = st_else;
|
||||
continue;
|
||||
}
|
||||
else if(!strcmp(parse.Tokens[0], "while") ||
|
||||
!strcmp(parse.Tokens[0], "for"))
|
||||
{
|
||||
parse.Section->type = st_loop;
|
||||
parse.Section->loop_index = MakeIndex(parse.LineStart);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CFsError err)
|
||||
{
|
||||
parse.ErrorMessage(err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// main preprocess function
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::Preprocess(FLevelLocals *Level)
|
||||
{
|
||||
len = (int)strlen(data);
|
||||
ProcessFindChar(data, 0); // fill in everything
|
||||
DryRunScript(Level);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FraggleScript allows 'including' of other lumps.
|
||||
// we divert input from the current script (normally
|
||||
// levelscript) to a seperate lump. This of course
|
||||
// first needs to be preprocessed to remove comments
|
||||
// etc.
|
||||
//
|
||||
// parse an 'include' lump
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::ParseInclude(FLevelLocals *Level, char *lumpname)
|
||||
{
|
||||
int lumpnum;
|
||||
char *lump;
|
||||
|
||||
if((lumpnum = Wads.CheckNumForName(lumpname)) == -1)
|
||||
{
|
||||
I_Error("include lump '%s' not found!\n", lumpname);
|
||||
return;
|
||||
}
|
||||
|
||||
int lumplen=Wads.LumpLength(lumpnum);
|
||||
lump=new char[lumplen+10];
|
||||
Wads.ReadLump(lumpnum,lump);
|
||||
|
||||
lump[lumplen]=0;
|
||||
|
||||
// preprocess the include
|
||||
// we assume that it does not include sections or labels or
|
||||
// other nasty things
|
||||
ProcessFindChar(lump, 0);
|
||||
|
||||
// now parse the lump
|
||||
FParser parse(Level, this);
|
||||
parse.Run(lump, lump, lump+lumplen);
|
||||
|
||||
// free the lump
|
||||
delete[] lump;
|
||||
}
|
||||
|
||||
672
src/playsim/fragglescript/t_script.cpp
Normal file
672
src/playsim/fragglescript/t_script.cpp
Normal file
|
|
@ -0,0 +1,672 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
// Copyright(C) 2005-2008 Christoph Oelckers
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// scripting.
|
||||
//
|
||||
// delayed scripts, running scripts, console cmds etc in here
|
||||
// the interface between FraggleScript and the rest of the game
|
||||
//
|
||||
// By Simon Howard
|
||||
//
|
||||
// (completely redone and cleaned up in 2008 by Christoph Oelckers)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "t_script.h"
|
||||
#include "a_keys.h"
|
||||
#include "d_player.h"
|
||||
#include "p_spec.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "serializer.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
#define IMPLEMENT_16_POINTERS(v, i) \
|
||||
IMPLEMENT_POINTER(v[i]) \
|
||||
IMPLEMENT_POINTER(v[i+1]) \
|
||||
IMPLEMENT_POINTER(v[i+2]) \
|
||||
IMPLEMENT_POINTER(v[i+3]) \
|
||||
IMPLEMENT_POINTER(v[i+4]) \
|
||||
IMPLEMENT_POINTER(v[i+5]) \
|
||||
IMPLEMENT_POINTER(v[i+6]) \
|
||||
IMPLEMENT_POINTER(v[i+7]) \
|
||||
IMPLEMENT_POINTER(v[i+8]) \
|
||||
IMPLEMENT_POINTER(v[i+9]) \
|
||||
IMPLEMENT_POINTER(v[i+10]) \
|
||||
IMPLEMENT_POINTER(v[i+11]) \
|
||||
IMPLEMENT_POINTER(v[i+12]) \
|
||||
IMPLEMENT_POINTER(v[i+13]) \
|
||||
IMPLEMENT_POINTER(v[i+14]) \
|
||||
IMPLEMENT_POINTER(v[i+15]) \
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
IMPLEMENT_CLASS(DFsScript, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(DFsScript)
|
||||
IMPLEMENT_POINTER(parent)
|
||||
IMPLEMENT_POINTER(trigger)
|
||||
IMPLEMENT_16_POINTERS(sections, 0)
|
||||
IMPLEMENT_POINTER(sections[16])
|
||||
IMPLEMENT_16_POINTERS(variables, 0)
|
||||
IMPLEMENT_16_POINTERS(children, 0)
|
||||
IMPLEMENT_16_POINTERS(children, 16)
|
||||
IMPLEMENT_16_POINTERS(children, 32)
|
||||
IMPLEMENT_16_POINTERS(children, 48)
|
||||
IMPLEMENT_16_POINTERS(children, 64)
|
||||
IMPLEMENT_16_POINTERS(children, 80)
|
||||
IMPLEMENT_16_POINTERS(children, 96)
|
||||
IMPLEMENT_16_POINTERS(children, 112)
|
||||
IMPLEMENT_16_POINTERS(children, 128)
|
||||
IMPLEMENT_16_POINTERS(children, 144)
|
||||
IMPLEMENT_16_POINTERS(children, 160)
|
||||
IMPLEMENT_16_POINTERS(children, 176)
|
||||
IMPLEMENT_16_POINTERS(children, 192)
|
||||
IMPLEMENT_16_POINTERS(children, 208)
|
||||
IMPLEMENT_16_POINTERS(children, 224)
|
||||
IMPLEMENT_16_POINTERS(children, 240)
|
||||
IMPLEMENT_POINTER(children[256])
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::ClearChildren()
|
||||
{
|
||||
int j;
|
||||
for(j=0;j<MAXSCRIPTS;j++) if (children[j])
|
||||
{
|
||||
children[j]->Destroy();
|
||||
children[j]=nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsScript::DFsScript()
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<SECTIONSLOTS; i++) sections[i] = nullptr;
|
||||
for(i=0; i<VARIABLESLOTS; i++) variables[i] = nullptr;
|
||||
for(i=0; i<MAXSCRIPTS; i++) children[i] = nullptr;
|
||||
|
||||
data = nullptr;
|
||||
scriptnum = -1;
|
||||
len = 0;
|
||||
parent = nullptr;
|
||||
trigger = nullptr;
|
||||
lastiftrue = false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This is here to delete the locally allocated buffer in case this
|
||||
// gets forcibly destroyed
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsScript::~DFsScript()
|
||||
{
|
||||
if (data != nullptr) delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::OnDestroy()
|
||||
{
|
||||
ClearVariables(true);
|
||||
ClearSections();
|
||||
ClearChildren();
|
||||
parent = nullptr;
|
||||
if (data != nullptr) delete [] data;
|
||||
data = nullptr;
|
||||
parent = nullptr;
|
||||
trigger = nullptr;
|
||||
Super::OnDestroy();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize(arc);
|
||||
|
||||
arc("data", data)
|
||||
("scriptnum", scriptnum)
|
||||
("len", len)
|
||||
("parent", parent)
|
||||
("trigger", trigger)
|
||||
("lastiftrue", lastiftrue)
|
||||
.Array("sections", sections, SECTIONSLOTS)
|
||||
.Array("variables", variables, VARIABLESLOTS)
|
||||
.Array("children", children, MAXSCRIPTS);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// run_script
|
||||
//
|
||||
// the function called by t_script.c
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::ParseScript(char *position, DFraggleThinker *th)
|
||||
{
|
||||
if (position == nullptr)
|
||||
{
|
||||
lastiftrue = false;
|
||||
position = data;
|
||||
}
|
||||
|
||||
// check for valid position
|
||||
if(position < data || position > data+len)
|
||||
{
|
||||
Printf("script %d: trying to continue from point outside script!\n", scriptnum);
|
||||
return;
|
||||
}
|
||||
|
||||
th->trigger_obj = trigger; // set trigger variable.
|
||||
|
||||
try
|
||||
{
|
||||
FParser parse(th->Level, this);
|
||||
parse.Run(position, data, data + len);
|
||||
}
|
||||
catch (CFraggleScriptError &err)
|
||||
{
|
||||
Printf ("%s\n", err.GetMessage());
|
||||
}
|
||||
|
||||
// dont clear global vars!
|
||||
if(scriptnum != -1) ClearVariables(); // free variables
|
||||
|
||||
// haleyjd
|
||||
lastiftrue = false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Running Scripts
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
IMPLEMENT_CLASS(DRunningScript, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(DRunningScript)
|
||||
IMPLEMENT_POINTER(prev)
|
||||
IMPLEMENT_POINTER(next)
|
||||
IMPLEMENT_POINTER(trigger)
|
||||
IMPLEMENT_16_POINTERS(variables, 0)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DRunningScript::DRunningScript(AActor *trigger, DFsScript *owner, int index)
|
||||
{
|
||||
prev = next = nullptr;
|
||||
script = owner;
|
||||
GC::WriteBarrier(this, script);
|
||||
save_point = index;
|
||||
wait_type = wt_none;
|
||||
wait_data = 0;
|
||||
|
||||
this->trigger = trigger;
|
||||
if (owner == nullptr)
|
||||
{
|
||||
for(int i=0; i< VARIABLESLOTS; i++) variables[i] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// save the script variables
|
||||
for(int i=0; i<VARIABLESLOTS; i++)
|
||||
{
|
||||
variables[i] = owner->variables[i];
|
||||
|
||||
if (index == 0) // we are starting another Script:
|
||||
{
|
||||
// remove all the variables from the script variable list
|
||||
// we only start with the basic labels
|
||||
while(variables[i] && variables[i]->type != svt_label)
|
||||
variables[i] = variables[i]->next;
|
||||
}
|
||||
else // a script is being halted
|
||||
{
|
||||
// remove all the variables from the script variable list
|
||||
// to prevent them being removed when the script stops
|
||||
while(owner->variables[i] && owner->variables[i]->type != svt_label)
|
||||
owner->variables[i] = owner->variables[i]->next;
|
||||
|
||||
GC::WriteBarrier(owner, owner->variables[i]);
|
||||
}
|
||||
|
||||
GC::WriteBarrier(this, variables[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DRunningScript::OnDestroy()
|
||||
{
|
||||
int i;
|
||||
DFsVariable *current, *next;
|
||||
|
||||
for(i=0; i<VARIABLESLOTS; i++)
|
||||
{
|
||||
current = variables[i];
|
||||
|
||||
// go thru this chain
|
||||
while(current)
|
||||
{
|
||||
next = current->next; // save for after freeing
|
||||
current->Destroy();
|
||||
current = next; // go to next in chain
|
||||
}
|
||||
variables[i] = nullptr;
|
||||
}
|
||||
Super::OnDestroy();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DRunningScript::Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize(arc);
|
||||
arc("script", script)
|
||||
("save_point", save_point)
|
||||
("wait_type", wait_type)
|
||||
("wait_data", wait_data)
|
||||
("prev", prev)
|
||||
("next", next)
|
||||
("trigger", trigger)
|
||||
.Array("variables", variables, VARIABLESLOTS);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// The main thinker
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
IMPLEMENT_CLASS(DFraggleThinker, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(DFraggleThinker)
|
||||
IMPLEMENT_POINTER(RunningScripts)
|
||||
IMPLEMENT_POINTER(LevelScript)
|
||||
IMPLEMENT_POINTER(GlobalScript)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This thinker is a little unusual from all the rest, because it
|
||||
// needs to construct some non-serializable data.
|
||||
//
|
||||
// This cannot be done in Construct, but requires an actual constructor,
|
||||
// so that even a deserialized ionstance is fully set up.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFraggleThinker::DFraggleThinker()
|
||||
{
|
||||
GlobalScript = Create<DFsScript>();
|
||||
GC::WriteBarrier(this, GlobalScript);
|
||||
InitFunctions();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFraggleThinker::Construct()
|
||||
{
|
||||
RunningScripts = Create<DRunningScript>();
|
||||
GC::WriteBarrier(this, RunningScripts);
|
||||
LevelScript = Create<DFsScript>();
|
||||
LevelScript->parent = nullptr;
|
||||
GC::WriteBarrier(this, LevelScript);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFraggleThinker::OnDestroy()
|
||||
{
|
||||
DRunningScript *p = RunningScripts;
|
||||
while (p)
|
||||
{
|
||||
DRunningScript *q = p;
|
||||
p = p->next;
|
||||
q->prev = q->next = nullptr;
|
||||
q->Destroy();
|
||||
}
|
||||
RunningScripts = nullptr;
|
||||
|
||||
GlobalScript->Destroy();
|
||||
GlobalScript = nullptr;
|
||||
|
||||
LevelScript->Destroy();
|
||||
LevelScript = nullptr;
|
||||
|
||||
SpawnedThings.Clear();
|
||||
Super::OnDestroy();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFraggleThinker::Serialize(FSerializer &arc)
|
||||
{
|
||||
// The global script is not serialized because the data it points to is not serializable.
|
||||
Super::Serialize(arc);
|
||||
arc("levelscript", LevelScript)
|
||||
("runningscripts", RunningScripts)
|
||||
("spawnedthings", SpawnedThings);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PAUSING SCRIPTS
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool DFraggleThinker::wait_finished(DRunningScript *script)
|
||||
{
|
||||
switch(script->wait_type)
|
||||
{
|
||||
case wt_none: return true; // uh? hehe
|
||||
case wt_scriptwait: // waiting for script to finish
|
||||
{
|
||||
DRunningScript *current;
|
||||
for(current = RunningScripts->next; current; current = current->next)
|
||||
{
|
||||
if(current == script) continue; // ignore this script
|
||||
if(current->script->scriptnum == script->wait_data)
|
||||
return false; // script still running
|
||||
}
|
||||
return true; // can continue now
|
||||
}
|
||||
|
||||
case wt_delay: // just count down
|
||||
{
|
||||
return --script->wait_data <= 0;
|
||||
}
|
||||
|
||||
case wt_tagwait:
|
||||
{
|
||||
int secnum;
|
||||
auto itr = Level->GetSectorTagIterator(script->wait_data);
|
||||
while ((secnum = itr.Next()) >= 0)
|
||||
{
|
||||
sector_t *sec = &Level->sectors[secnum];
|
||||
if(sec->floordata || sec->ceilingdata || sec->lightingdata)
|
||||
return false; // not finished
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case wt_scriptwaitpre: // haleyjd - wait for script to start
|
||||
{
|
||||
DRunningScript *current;
|
||||
for(current = RunningScripts->next; current; current=current->next)
|
||||
{
|
||||
if(current == script) continue; // ignore this script
|
||||
if(current->script->scriptnum == script->wait_data)
|
||||
return true; // script is now running
|
||||
}
|
||||
return false; // no running instances found
|
||||
}
|
||||
|
||||
default: return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFraggleThinker::Tick()
|
||||
{
|
||||
DRunningScript *current, *next;
|
||||
int i;
|
||||
|
||||
current = RunningScripts->next;
|
||||
|
||||
while(current)
|
||||
{
|
||||
if(wait_finished(current))
|
||||
{
|
||||
// copy out the script variables from the
|
||||
// runningscript
|
||||
|
||||
for(i=0; i<VARIABLESLOTS; i++)
|
||||
{
|
||||
current->script->variables[i] = current->variables[i];
|
||||
GC::WriteBarrier(current->script, current->variables[i]);
|
||||
current->variables[i] = nullptr;
|
||||
}
|
||||
current->script->trigger = current->trigger; // copy trigger
|
||||
|
||||
// unhook from chain
|
||||
current->prev->next = current->next;
|
||||
GC::WriteBarrier(current->prev, current->next);
|
||||
if(current->next)
|
||||
{
|
||||
current->next->prev = current->prev;
|
||||
GC::WriteBarrier(current->next, current->prev);
|
||||
}
|
||||
next = current->next; // save before freeing
|
||||
|
||||
// continue the script
|
||||
current->script->ParseScript (current->script->data + current->save_point, this);
|
||||
|
||||
// free
|
||||
current->Destroy();
|
||||
}
|
||||
else
|
||||
next = current->next;
|
||||
current = next; // continue to next in chain
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// We have to mark the SpawnedThings array manually because it's not
|
||||
// in the list of declared pointers.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t DFraggleThinker::PropagateMark()
|
||||
{
|
||||
for(unsigned i=0;i<SpawnedThings.Size();i++)
|
||||
{
|
||||
GC::Mark(SpawnedThings[i]);
|
||||
}
|
||||
return Super::PropagateMark();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Again we have to handle the SpawnedThings array manually because
|
||||
// it's not in the list of declared pointers.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t DFraggleThinker::PointerSubstitution (DObject *old, DObject *notOld)
|
||||
{
|
||||
size_t changed = Super::PointerSubstitution(old, notOld);
|
||||
for(unsigned i=0;i<SpawnedThings.Size();i++)
|
||||
{
|
||||
if (SpawnedThings[i] == static_cast<AActor*>(old))
|
||||
{
|
||||
SpawnedThings[i] = static_cast<AActor*>(notOld);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Adds a running script to the list of running scripts
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFraggleThinker::AddRunningScript(DRunningScript *runscr)
|
||||
{
|
||||
runscr->next = RunningScripts->next;
|
||||
GC::WriteBarrier(runscr, RunningScripts->next);
|
||||
|
||||
runscr->prev = RunningScripts;
|
||||
GC::WriteBarrier(runscr, RunningScripts);
|
||||
|
||||
runscr->prev->next = runscr;
|
||||
GC::WriteBarrier(runscr->prev, runscr);
|
||||
|
||||
if(runscr->next)
|
||||
{
|
||||
runscr->next->prev = runscr;
|
||||
GC::WriteBarrier(runscr->next, runscr);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void T_PreprocessScripts(FLevelLocals *Level)
|
||||
{
|
||||
DFraggleThinker *th = Level->FraggleScriptThinker;
|
||||
if (th)
|
||||
{
|
||||
// run the levelscript first
|
||||
// get the other scripts
|
||||
|
||||
// levelscript started by player 0 'superplayer'
|
||||
th->LevelScript->trigger = Level->Players[0]->mo;
|
||||
|
||||
th->LevelScript->Preprocess(Level);
|
||||
th->LevelScript->ParseScript(nullptr, th);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool T_RunScript(FLevelLocals *Level, int snum, AActor * t_trigger)
|
||||
{
|
||||
DFraggleThinker *th = Level->FraggleScriptThinker;
|
||||
if (th)
|
||||
{
|
||||
// [CO] It is far too dangerous to start the script right away.
|
||||
// Better queue it for execution for the next time
|
||||
// the runningscripts are checked.
|
||||
|
||||
if(snum < 0 || snum >= MAXSCRIPTS) return false;
|
||||
|
||||
DFsScript *script = th->LevelScript->children[snum];
|
||||
if(!script) return false;
|
||||
|
||||
DRunningScript *runscr = Create<DRunningScript>(t_trigger, script, 0);
|
||||
// hook into chain at start
|
||||
th->AddRunningScript(runscr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This isn't network safe. FraggleScript as a whole most likely isn't...
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD(fpuke)
|
||||
{
|
||||
int argc = argv.argc();
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
Printf (" fpuke <script>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
T_RunScript(players[consoleplayer].mo->Level, atoi(argv[1]), players[consoleplayer].mo);
|
||||
}
|
||||
}
|
||||
729
src/playsim/fragglescript/t_script.h
Normal file
729
src/playsim/fragglescript/t_script.h
Normal file
|
|
@ -0,0 +1,729 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
// Copyright(C) 2002-2008 Christoph Oelckers
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef __T_SCRIPT_H__
|
||||
#define __T_SCRIPT_H__
|
||||
|
||||
#include "p_setup.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "m_fixed.h"
|
||||
#include "actor.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// This pragma saves 8kb of wasted code.
|
||||
#pragma pointers_to_members( full_generality, single_inheritance )
|
||||
#endif
|
||||
|
||||
class DFraggleThinker;
|
||||
|
||||
|
||||
class CFraggleScriptError : public CDoomError
|
||||
{
|
||||
public:
|
||||
CFraggleScriptError() : CDoomError() {}
|
||||
CFraggleScriptError(const char *message) : CDoomError(message) {}
|
||||
};
|
||||
|
||||
|
||||
class DRunningScript;
|
||||
|
||||
|
||||
|
||||
inline bool isop(int c)
|
||||
{
|
||||
return !( ( (c)<='Z' && (c)>='A') || ( (c)<='z' && (c)>='a') ||
|
||||
( (c)<='9' && (c)>='0') || ( (c)=='_') );
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
enum
|
||||
{
|
||||
svt_string,
|
||||
svt_int,
|
||||
svt_mobj, // a map object
|
||||
svt_function, // functions are stored as variables
|
||||
svt_label, // labels for goto calls are variables
|
||||
svt_const, // const
|
||||
svt_fixed, // haleyjd: fixed-point int - 8-17 std
|
||||
svt_pInt, // pointer to game int
|
||||
svt_pMobj, // pointer to game mobj
|
||||
svt_linespec, // line special (can be used as both function and constant)
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
typedef int fsfix;
|
||||
|
||||
struct svalue_t
|
||||
{
|
||||
int type;
|
||||
|
||||
FString string;
|
||||
union
|
||||
{
|
||||
int i;
|
||||
fsfix f; // haleyjd: 8-17
|
||||
AActor *mobj;
|
||||
} value;
|
||||
|
||||
svalue_t()
|
||||
{
|
||||
type = svt_int;
|
||||
value.i = 0;
|
||||
}
|
||||
|
||||
svalue_t(const svalue_t & other)
|
||||
{
|
||||
type = other.type;
|
||||
string = other.string;
|
||||
value = other.value;
|
||||
}
|
||||
|
||||
void setInt(int ip)
|
||||
{
|
||||
value.i = ip;
|
||||
type = svt_int;
|
||||
}
|
||||
|
||||
void setFixed(fsfix fp)
|
||||
{
|
||||
value.f = fp;
|
||||
type = svt_fixed;
|
||||
}
|
||||
|
||||
void setDouble(double dp)
|
||||
{
|
||||
value.f = fsfix(dp * 65536);
|
||||
type = svt_fixed;
|
||||
}
|
||||
};
|
||||
|
||||
int intvalue(const svalue_t & v);
|
||||
fsfix fixedvalue(const svalue_t & v);
|
||||
double floatvalue(const svalue_t & v);
|
||||
const char *stringvalue(const svalue_t & v);
|
||||
AActor *actorvalue(FLevelLocals *Level, const svalue_t &svalue);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// varoius defines collected in a nicer manner
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
enum
|
||||
{
|
||||
VARIABLESLOTS = 16,
|
||||
SECTIONSLOTS = 17,
|
||||
T_MAXTOKENS = 256,
|
||||
TOKENLENGTH = 128,
|
||||
MAXARGS = 128,
|
||||
MAXSCRIPTS = 257,
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// One variable
|
||||
//
|
||||
//==========================================================================
|
||||
struct FParser;
|
||||
|
||||
struct DFsVariable : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DFsVariable, DObject)
|
||||
HAS_OBJECT_POINTERS
|
||||
|
||||
public:
|
||||
FString Name;
|
||||
TObjPtr<DFsVariable*> next; // for hashing
|
||||
|
||||
int type; // svt_string or svt_int: same as in svalue_t
|
||||
FString string;
|
||||
TObjPtr<AActor*> actor;
|
||||
|
||||
union value_t
|
||||
{
|
||||
int32_t i;
|
||||
fsfix fixed; // haleyjd: fixed-point
|
||||
|
||||
// the following are only used in the global script so we don't need to bother with them
|
||||
// when serializing variables.
|
||||
int *pI; // pointer to game int
|
||||
AActor **pMobj; // pointer to game obj
|
||||
void (FParser::*handler)(); // for functions
|
||||
const FLineSpecial *ls;
|
||||
} value;
|
||||
|
||||
public:
|
||||
|
||||
DFsVariable(const char *_name = "");
|
||||
|
||||
void GetValue(svalue_t &result);
|
||||
void SetValue(FLevelLocals *Level, const svalue_t &newvalue);
|
||||
void Serialize(FSerializer &ar);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// hash the variables for speed: this is the hashkey
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
inline int variable_hash(const char *n)
|
||||
{
|
||||
return
|
||||
(n[0]? ( ( n[0] + n[1] +
|
||||
(n[1] ? n[2] +
|
||||
(n[2] ? n[3] : 0) : 0) ) % VARIABLESLOTS ) :0);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sections
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
enum // section types
|
||||
{
|
||||
st_empty, // empty {} braces
|
||||
st_if,
|
||||
st_elseif,
|
||||
st_else,
|
||||
st_loop,
|
||||
};
|
||||
|
||||
struct DFsSection : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DFsSection, DObject)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
int type;
|
||||
int start_index;
|
||||
int end_index;
|
||||
int loop_index;
|
||||
TObjPtr<DFsSection*> next; // for hashing
|
||||
|
||||
DFsSection()
|
||||
{
|
||||
next = nullptr;
|
||||
}
|
||||
|
||||
void Serialize(FSerializer &ar);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Tokens
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
enum tokentype_t
|
||||
{
|
||||
name_, // a name, eg 'count1' or 'frag'
|
||||
number,
|
||||
operator_,
|
||||
string_,
|
||||
unset,
|
||||
function // function name
|
||||
};
|
||||
|
||||
enum // brace types: where current_section is a { or }
|
||||
{
|
||||
bracket_open,
|
||||
bracket_close
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Errors
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class CFsError
|
||||
{
|
||||
public:
|
||||
// trying to throw strings crashes VC++ badly so we have to use a static buffer. :(
|
||||
char msg[2048];
|
||||
|
||||
CFsError(const FString &in)
|
||||
{
|
||||
strncpy(msg, in, 2047);
|
||||
msg[2047]=0;
|
||||
}
|
||||
};
|
||||
|
||||
// throw this object to regularly terminate a script's execution.
|
||||
class CFsTerminator
|
||||
{
|
||||
int fill;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Scripts
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class DFsScript : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DFsScript, DObject)
|
||||
HAS_OBJECT_POINTERS
|
||||
|
||||
public:
|
||||
// script data
|
||||
|
||||
char *data;
|
||||
int scriptnum; // this script's number
|
||||
int len;
|
||||
|
||||
// {} sections
|
||||
|
||||
TObjPtr<DFsSection*> sections[SECTIONSLOTS];
|
||||
|
||||
// variables:
|
||||
|
||||
TObjPtr<DFsVariable*> variables[VARIABLESLOTS];
|
||||
|
||||
// ptr to the parent script
|
||||
// the parent script is the script above this level
|
||||
// eg. individual linetrigger scripts are children
|
||||
// of the levelscript, which is a child of the
|
||||
// global_script
|
||||
TObjPtr<DFsScript*> parent;
|
||||
|
||||
// haleyjd: 8-17
|
||||
// child scripts.
|
||||
// levelscript holds ptrs to all of the level's scripts
|
||||
// here.
|
||||
TObjPtr<DFsScript*> children[MAXSCRIPTS];
|
||||
|
||||
|
||||
TObjPtr<AActor*> trigger; // object which triggered this script
|
||||
|
||||
bool lastiftrue; // haleyjd: whether last "if" statement was
|
||||
// true or false
|
||||
|
||||
DFsScript();
|
||||
~DFsScript();
|
||||
void OnDestroy() override;
|
||||
void Serialize(FSerializer &ar);
|
||||
|
||||
DFsVariable *NewVariable(const char *name, int vtype);
|
||||
void NewFunction(const char *name, void (FParser::*handler)());
|
||||
|
||||
DFsVariable *VariableForName(const char *name);
|
||||
DFsVariable *FindVariable(const char *name, DFsScript *global);
|
||||
void ClearVariables(bool complete= false);
|
||||
DFsVariable *NewLabel(char *labelptr);
|
||||
char *LabelValue(const svalue_t &v);
|
||||
|
||||
char *SectionStart(const DFsSection *sec);
|
||||
char *SectionEnd(const DFsSection *sec);
|
||||
char *SectionLoop(const DFsSection *sec);
|
||||
void ClearSections();
|
||||
void ClearChildren();
|
||||
|
||||
int MakeIndex(const char *p) { return int(p-data); }
|
||||
|
||||
// preprocessor
|
||||
int section_hash(const char *b) { return MakeIndex(b) % SECTIONSLOTS; }
|
||||
DFsSection *NewSection(const char *brace);
|
||||
DFsSection *FindSectionStart(const char *brace);
|
||||
DFsSection *FindSectionEnd(const char *brace);
|
||||
char *ProcessFindChar(char *data, char find);
|
||||
void DryRunScript(FLevelLocals *Level);
|
||||
void Preprocess(FLevelLocals *Level);
|
||||
void ParseInclude(FLevelLocals *Level, char *lumpname);
|
||||
void ParseScript(char *rover, DFraggleThinker *th);
|
||||
void ParseData(char *rover, char *data, char *end);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// The script parser
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct FParser
|
||||
{
|
||||
struct operator_t
|
||||
{
|
||||
const char *string;
|
||||
void (FParser::*handler)(svalue_t &, int, int, int); // left, mid, right
|
||||
int direction;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
forward,
|
||||
backward
|
||||
};
|
||||
|
||||
static operator_t operators[];
|
||||
static int num_operators;
|
||||
|
||||
|
||||
char *LineStart;
|
||||
char *Rover;
|
||||
|
||||
char *Tokens[T_MAXTOKENS];
|
||||
tokentype_t TokenType[T_MAXTOKENS];
|
||||
int NumTokens;
|
||||
FLevelLocals *Level;
|
||||
DFsScript *Script; // the current script
|
||||
DFsSection *Section;
|
||||
DFsSection *PrevSection;
|
||||
int BraceType;
|
||||
|
||||
int t_argc; // number of arguments
|
||||
svalue_t *t_argv; // arguments
|
||||
svalue_t t_return; // returned value
|
||||
FString t_func; // name of current function
|
||||
|
||||
FParser(FLevelLocals *l, DFsScript *scr)
|
||||
{
|
||||
Level = l;
|
||||
LineStart = NULL;
|
||||
Rover = NULL;
|
||||
Tokens[0] = new char[scr->len+32]; // 32 for safety. FS seems to need a few bytes more than the script's actual length.
|
||||
NumTokens = 0;
|
||||
Script = scr;
|
||||
Section = PrevSection = NULL;
|
||||
BraceType = 0;
|
||||
}
|
||||
|
||||
~FParser()
|
||||
{
|
||||
if (Tokens[0]) delete [] Tokens[0];
|
||||
}
|
||||
|
||||
void NextToken();
|
||||
char *GetTokens(char *s);
|
||||
void PrintTokens();
|
||||
void ErrorMessage(FString msg);
|
||||
|
||||
void Run(char *rover, char *data, char *end);
|
||||
void RunStatement();
|
||||
int FindOperator(int start, int stop, const char *value);
|
||||
int FindOperatorBackwards(int start, int stop, const char *value);
|
||||
void SimpleEvaluate(svalue_t &, int n);
|
||||
void PointlessBrackets(int *start, int *stop);
|
||||
void EvaluateExpression(svalue_t &, int start, int stop);
|
||||
void EvaluateFunction(svalue_t &, int start, int stop);
|
||||
|
||||
void OPequals(svalue_t &, int, int, int); // =
|
||||
|
||||
void OPplus(svalue_t &, int, int, int); // +
|
||||
void OPminus(svalue_t &, int, int, int); // -
|
||||
void OPmultiply(svalue_t &, int, int, int); // *
|
||||
void OPdivide(svalue_t &, int, int, int); // /
|
||||
void OPremainder(svalue_t &, int, int, int); // %
|
||||
|
||||
void OPor(svalue_t &, int, int, int); // ||
|
||||
void OPand(svalue_t &, int, int, int); // &&
|
||||
void OPnot(svalue_t &, int, int, int); // !
|
||||
|
||||
void OPor_bin(svalue_t &, int, int, int); // |
|
||||
void OPand_bin(svalue_t &, int, int, int); // &
|
||||
void OPnot_bin(svalue_t &, int, int, int); // ~
|
||||
|
||||
void OPcmp(svalue_t &, int, int, int); // ==
|
||||
void OPnotcmp(svalue_t &, int, int, int); // !=
|
||||
void OPlessthan(svalue_t &, int, int, int); // <
|
||||
void OPgreaterthan(svalue_t &, int, int, int); // >
|
||||
|
||||
void OPincrement(svalue_t &, int, int, int); // ++
|
||||
void OPdecrement(svalue_t &, int, int, int); // --
|
||||
|
||||
void OPstructure(svalue_t &, int, int, int); // in t_vari.c
|
||||
|
||||
void OPlessthanorequal(svalue_t &, int, int, int); // <=
|
||||
void OPgreaterthanorequal(svalue_t &, int, int, int); // >=
|
||||
|
||||
void spec_brace();
|
||||
bool spec_if();
|
||||
bool spec_elseif(bool lastif);
|
||||
void spec_else(bool lastif);
|
||||
void spec_for();
|
||||
void spec_while();
|
||||
void CreateVariable(int newvar_type, DFsScript *newvar_script, int start, int stop);
|
||||
void ParseVarLine(int newvar_type, DFsScript *newvar_script, int start);
|
||||
bool spec_variable();
|
||||
void spec_script();
|
||||
|
||||
DFsSection *looping_section();
|
||||
FString GetFormatString(int startarg);
|
||||
bool CheckArgs(int cnt);
|
||||
|
||||
PClassActor * T_GetMobjType(svalue_t arg);
|
||||
int T_GetPlayerNum(const svalue_t &arg);
|
||||
AActor *T_GetPlayerActor(const svalue_t &arg);
|
||||
|
||||
AActor* actorvalue(const svalue_t &svalue)
|
||||
{
|
||||
return ::actorvalue(Level, svalue);
|
||||
}
|
||||
|
||||
void SF_Print();
|
||||
void SF_Rnd();
|
||||
void SF_Continue();
|
||||
void SF_Break();
|
||||
void SF_Goto();
|
||||
void SF_Return();
|
||||
void SF_Include();
|
||||
void SF_Input();
|
||||
void SF_Beep();
|
||||
void SF_Clock();
|
||||
void SF_ExitLevel();
|
||||
void SF_Tip();
|
||||
void SF_TimedTip();
|
||||
void SF_PlayerTip();
|
||||
void SF_Message();
|
||||
void SF_PlayerMsg();
|
||||
void SF_PlayerInGame();
|
||||
void SF_PlayerName();
|
||||
void SF_PlayerObj();
|
||||
void SF_StartScript(); // FPUKE needs to access this
|
||||
void SF_ScriptRunning();
|
||||
void SF_Wait();
|
||||
void SF_TagWait();
|
||||
void SF_ScriptWait();
|
||||
void SF_ScriptWaitPre(); // haleyjd: new wait types
|
||||
void SF_Player();
|
||||
void SF_Spawn();
|
||||
void SF_RemoveObj();
|
||||
void SF_KillObj();
|
||||
void SF_ObjX();
|
||||
void SF_ObjY();
|
||||
void SF_ObjZ();
|
||||
void SF_ObjAngle();
|
||||
void SF_Teleport();
|
||||
void SF_SilentTeleport();
|
||||
void SF_DamageObj();
|
||||
void SF_ObjSector();
|
||||
void SF_ObjHealth();
|
||||
void SF_ObjFlag();
|
||||
void SF_PushThing();
|
||||
void SF_ReactionTime();
|
||||
void SF_MobjTarget();
|
||||
void SF_MobjMomx();
|
||||
void SF_MobjMomy();
|
||||
void SF_MobjMomz();
|
||||
void SF_PointToAngle();
|
||||
void SF_PointToDist();
|
||||
void SF_SetCamera();
|
||||
void SF_ClearCamera();
|
||||
void SF_StartSound();
|
||||
void SF_StartSectorSound();
|
||||
void SF_FloorHeight();
|
||||
void SF_MoveFloor();
|
||||
void SF_CeilingHeight();
|
||||
void SF_MoveCeiling();
|
||||
void SF_LightLevel();
|
||||
void SF_FadeLight();
|
||||
void SF_FloorTexture();
|
||||
void SF_SectorColormap();
|
||||
void SF_CeilingTexture();
|
||||
void SF_ChangeHubLevel();
|
||||
void SF_StartSkill();
|
||||
void SF_OpenDoor();
|
||||
void SF_CloseDoor();
|
||||
void SF_RunCommand();
|
||||
void SF_LineTrigger();
|
||||
void SF_ChangeMusic();
|
||||
void SF_SetLineBlocking();
|
||||
void SF_SetLineMonsterBlocking();
|
||||
void SF_SetLineTexture();
|
||||
void SF_Max();
|
||||
void SF_Min();
|
||||
void SF_Abs();
|
||||
void SF_Gameskill();
|
||||
void SF_Gamemode();
|
||||
void SF_IsPlayerObj();
|
||||
void SF_PlayerKeys();
|
||||
void SF_PlayerAmmo();
|
||||
void SF_MaxPlayerAmmo();
|
||||
void SF_PlayerWeapon();
|
||||
void SF_PlayerSelectedWeapon();
|
||||
void SF_GiveInventory();
|
||||
void SF_TakeInventory();
|
||||
void SF_CheckInventory();
|
||||
void SF_SetWeapon();
|
||||
void SF_MoveCamera();
|
||||
void SF_ObjAwaken();
|
||||
void SF_AmbientSound();
|
||||
void SF_ExitSecret();
|
||||
void SF_MobjValue();
|
||||
void SF_StringValue();
|
||||
void SF_IntValue();
|
||||
void SF_FixedValue();
|
||||
void SF_SpawnExplosion();
|
||||
void SF_RadiusAttack();
|
||||
void SF_SetObjPosition();
|
||||
void SF_TestLocation();
|
||||
void SF_HealObj(); //no pain sound
|
||||
void SF_ObjDead();
|
||||
void SF_SpawnMissile();
|
||||
void SF_MapThingNumExist();
|
||||
void SF_MapThings();
|
||||
void SF_ObjState();
|
||||
void SF_LineFlag();
|
||||
void SF_PlayerAddFrag();
|
||||
void SF_SkinColor();
|
||||
void SF_PlayDemo();
|
||||
void SF_CheckCVar();
|
||||
void SF_Resurrect();
|
||||
void SF_LineAttack();
|
||||
void SF_ObjType();
|
||||
void SF_Sin();
|
||||
void SF_ASin();
|
||||
void SF_Cos();
|
||||
void SF_ACos();
|
||||
void SF_Tan();
|
||||
void SF_ATan();
|
||||
void SF_Exp();
|
||||
void SF_Log();
|
||||
void SF_Sqrt();
|
||||
void SF_Floor();
|
||||
void SF_Pow();
|
||||
void SF_NewHUPic();
|
||||
void SF_DeleteHUPic();
|
||||
void SF_ModifyHUPic();
|
||||
void SF_SetHUPicDisplay();
|
||||
void SF_SetCorona();
|
||||
void SF_Ls();
|
||||
void SF_LevelNum();
|
||||
void SF_MobjRadius();
|
||||
void SF_MobjHeight();
|
||||
void SF_ThingCount();
|
||||
void SF_SetColor();
|
||||
void SF_SpawnShot2();
|
||||
void SF_KillInSector();
|
||||
void SF_SectorType();
|
||||
void SF_SetLineTrigger();
|
||||
void SF_ChangeTag();
|
||||
void SF_WallGlow();
|
||||
void RunLineSpecial(const FLineSpecial *);
|
||||
|
||||
DRunningScript *SaveCurrentScript();
|
||||
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Running scripts
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
enum waittype_e
|
||||
{
|
||||
wt_none, // not waiting
|
||||
wt_delay, // wait for a set amount of time
|
||||
wt_tagwait, // wait for sector to stop moving
|
||||
wt_scriptwait, // wait for script to finish
|
||||
wt_scriptwaitpre, // haleyjd - wait for script to start
|
||||
};
|
||||
|
||||
class DRunningScript : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DRunningScript, DObject)
|
||||
HAS_OBJECT_POINTERS
|
||||
|
||||
public:
|
||||
DRunningScript(AActor *trigger=NULL, DFsScript *owner = NULL, int index = 0) ;
|
||||
void OnDestroy() override;
|
||||
void Serialize(FSerializer &arc);
|
||||
|
||||
TObjPtr<DFsScript*> script;
|
||||
|
||||
// where we are
|
||||
int save_point;
|
||||
|
||||
int wait_type;
|
||||
int wait_data; // data for wait: tagnum, counter, script number etc
|
||||
|
||||
// saved variables
|
||||
TObjPtr<DFsVariable*> variables[VARIABLESLOTS];
|
||||
|
||||
TObjPtr<DRunningScript*> prev, next; // for chain
|
||||
TObjPtr<AActor*> trigger;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// This thinker eliminates the need to call the Fragglescript functions from the main code
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
class DFraggleThinker : public DThinker
|
||||
{
|
||||
DECLARE_CLASS(DFraggleThinker, DThinker)
|
||||
HAS_OBJECT_POINTERS
|
||||
public:
|
||||
static const int DEFAULT_STAT = STAT_SCRIPTS;
|
||||
int zoom = 1;
|
||||
AActor *trigger_obj; // this is a transient pointer not being subjected to GC.
|
||||
TObjPtr<DFsScript*> GlobalScript;
|
||||
TObjPtr<DFsScript*> LevelScript;
|
||||
TObjPtr<DRunningScript*> RunningScripts;
|
||||
TArray<TObjPtr<AActor*> > SpawnedThings;
|
||||
|
||||
DFraggleThinker(); // This class needs a real constructor because it has non-serializable content.
|
||||
void Construct();
|
||||
void OnDestroy() override;
|
||||
|
||||
|
||||
void Serialize(FSerializer & arc);
|
||||
void Tick();
|
||||
void InitFunctions();
|
||||
size_t PropagateMark();
|
||||
size_t PointerSubstitution (DObject *old, DObject *notOld);
|
||||
bool wait_finished(DRunningScript *script);
|
||||
void AddRunningScript(DRunningScript *runscr);
|
||||
|
||||
static TObjPtr<DFraggleThinker*> ActiveThinker;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Global stuff
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "t_fs.h"
|
||||
|
||||
void script_error(const char *s, ...) GCCPRINTF(1,2);
|
||||
void FS_EmulateCmd(FLevelLocals *l, char * string);
|
||||
|
||||
#endif
|
||||
|
||||
616
src/playsim/fragglescript/t_spec.cpp
Normal file
616
src/playsim/fragglescript/t_spec.cpp
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// 'Special' stuff
|
||||
//
|
||||
// if(), int statements, etc.
|
||||
//
|
||||
// By Simon Howard
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "t_script.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ending brace found in parsing
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::spec_brace()
|
||||
{
|
||||
if(BraceType != bracket_close) // only deal with closing } braces
|
||||
return;
|
||||
|
||||
// if() requires nothing to be done
|
||||
if(Section->type == st_if || Section->type == st_else)
|
||||
return;
|
||||
|
||||
// if a loop, jump back to the start of the loop
|
||||
if(Section->type == st_loop)
|
||||
{
|
||||
Rover = Script->SectionLoop(Section);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 'if' statement -- haleyjd: changed to bool for else/elseif
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FParser::spec_if()
|
||||
{
|
||||
int endtoken;
|
||||
svalue_t eval;
|
||||
|
||||
|
||||
if((endtoken = FindOperator(0, NumTokens-1, ")")) == -1)
|
||||
{
|
||||
script_error("parse error in if statement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2 to skip past the 'if' and '('
|
||||
EvaluateExpression(eval, 2, endtoken-1);
|
||||
bool ifresult = !!intvalue(eval);
|
||||
|
||||
if(Section && BraceType == bracket_open && endtoken == NumTokens-1)
|
||||
{
|
||||
// {} braces
|
||||
if(!ifresult) // skip to end of section
|
||||
Rover = Script->SectionEnd(Section) + 1;
|
||||
}
|
||||
else if(ifresult) // if() without {} braces
|
||||
{
|
||||
// nothing to do ?
|
||||
if(endtoken != NumTokens-1)
|
||||
EvaluateExpression(eval, endtoken+1, NumTokens-1);
|
||||
}
|
||||
|
||||
return ifresult;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 'elseif' statement
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FParser::spec_elseif(bool lastif)
|
||||
{
|
||||
int endtoken;
|
||||
svalue_t eval;
|
||||
|
||||
if((endtoken = FindOperator(0, NumTokens-1, ")")) == -1)
|
||||
{
|
||||
script_error("parse error in elseif statement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(lastif)
|
||||
{
|
||||
Rover = Script->SectionEnd(Section) + 1;
|
||||
return true;
|
||||
}
|
||||
// 2 to skip past the 'elseif' and '('
|
||||
EvaluateExpression(eval, 2, endtoken-1);
|
||||
bool ifresult = !!intvalue(eval);
|
||||
|
||||
if(Section && BraceType == bracket_open
|
||||
&& endtoken == NumTokens-1)
|
||||
{
|
||||
// {} braces
|
||||
if(!ifresult) // skip to end of section
|
||||
Rover = Script->SectionEnd(Section) + 1;
|
||||
}
|
||||
else if(ifresult) // elseif() without {} braces
|
||||
{
|
||||
// nothing to do ?
|
||||
if(endtoken != NumTokens-1)
|
||||
EvaluateExpression(eval, endtoken+1, NumTokens-1);
|
||||
}
|
||||
|
||||
return ifresult;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 'else' statement
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::spec_else(bool lastif)
|
||||
{
|
||||
if(lastif)
|
||||
Rover = Script->SectionEnd(Section) + 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// while() loop
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::spec_while()
|
||||
{
|
||||
int endtoken;
|
||||
svalue_t eval;
|
||||
|
||||
if(!Section)
|
||||
{
|
||||
script_error("no {} section given for loop\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if( (endtoken = FindOperator(0, NumTokens-1, ")")) == -1)
|
||||
{
|
||||
script_error("parse error in loop statement\n");
|
||||
return;
|
||||
}
|
||||
|
||||
EvaluateExpression(eval, 2, endtoken-1);
|
||||
|
||||
// skip if no longer valid
|
||||
if(!intvalue(eval)) Rover = Script->SectionEnd(Section) + 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// for() loop
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::spec_for()
|
||||
{
|
||||
svalue_t eval;
|
||||
int start;
|
||||
int comma1, comma2; // token numbers of the seperating commas
|
||||
|
||||
if(!Section)
|
||||
{
|
||||
script_error("need {} delimiters for for()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// is a valid section
|
||||
|
||||
start = 2; // skip "for" and "(": start on third token(2)
|
||||
|
||||
// find the seperating commas first
|
||||
|
||||
if( (comma1 = FindOperator(start, NumTokens-1, ",")) == -1
|
||||
|| (comma2 = FindOperator(comma1+1, NumTokens-1, ",")) == -1)
|
||||
{
|
||||
script_error("incorrect arguments to for()\n"); // haleyjd:
|
||||
return; // said if()
|
||||
}
|
||||
|
||||
// are we looping back from a previous loop?
|
||||
if(Section == PrevSection)
|
||||
{
|
||||
// do the loop 'action' (third argument)
|
||||
EvaluateExpression(eval, comma2+1, NumTokens-2);
|
||||
|
||||
// check if we should run the loop again (second argument)
|
||||
EvaluateExpression(eval, comma1+1, comma2-1);
|
||||
if(!intvalue(eval))
|
||||
{
|
||||
// stop looping
|
||||
Rover = Script->SectionEnd(Section) + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// first time: starting the loop
|
||||
// just evaluate the starting expression (first arg)
|
||||
EvaluateExpression(eval, start, comma1-1);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Variable Creation
|
||||
//
|
||||
// called for each individual variable in a statement
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::CreateVariable(int newvar_type, DFsScript *newvar_script, int start, int stop)
|
||||
{
|
||||
if(TokenType[start] != name_)
|
||||
{
|
||||
script_error("invalid name for variable: '%s'\n", Tokens[start]);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if already exists, only checking
|
||||
// the current script
|
||||
if(newvar_script->VariableForName (Tokens[start]))
|
||||
{
|
||||
// In Eternity this was fatal and in Legacy it was ignored
|
||||
// So make this a warning.
|
||||
Printf("FS: redefined symbol: '%s'\n", Tokens[start]);
|
||||
return; // already one
|
||||
}
|
||||
|
||||
// haleyjd: disallow mobj references in the hub script --
|
||||
// they cause dangerous dangling references and are of no
|
||||
// potential use
|
||||
if(newvar_script != Script && newvar_type == svt_mobj)
|
||||
{
|
||||
script_error("cannot create mobj reference in hub script\n");
|
||||
return;
|
||||
}
|
||||
|
||||
newvar_script->NewVariable (Tokens[start], newvar_type);
|
||||
|
||||
if(stop != start)
|
||||
{
|
||||
svalue_t scratch;
|
||||
EvaluateExpression(scratch, start, stop);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// divide a statement (without type prefix) into individual
|
||||
// variables to create them using create_variable
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::ParseVarLine(int newvar_type, DFsScript *newvar_script, int start)
|
||||
{
|
||||
int starttoken = start, endtoken;
|
||||
|
||||
while(1)
|
||||
{
|
||||
endtoken = FindOperator(starttoken, NumTokens-1, ",");
|
||||
if(endtoken == -1) break;
|
||||
CreateVariable(newvar_type, newvar_script, starttoken, endtoken-1);
|
||||
starttoken = endtoken+1; //start next after end of this one
|
||||
}
|
||||
// dont forget the last one
|
||||
CreateVariable(newvar_type, newvar_script, starttoken, NumTokens-1);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// variable definition
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FParser::spec_variable()
|
||||
{
|
||||
int start = 0;
|
||||
|
||||
int newvar_type = -1; // init to -1
|
||||
DFsScript *newvar_script = Script; // use current script
|
||||
|
||||
// check for 'hub' keyword to make a hub variable
|
||||
if(!strcmp(Tokens[start], "hub"))
|
||||
{
|
||||
// The hub script doesn't work so it's probably safest to store the variable locally.
|
||||
//newvar_script = &hub_script;
|
||||
start++; // skip first token
|
||||
}
|
||||
|
||||
// now find variable type
|
||||
if(!strcmp(Tokens[start], "const"))
|
||||
{
|
||||
newvar_type = svt_const;
|
||||
start++;
|
||||
}
|
||||
else if(!strcmp(Tokens[start], "string"))
|
||||
{
|
||||
newvar_type = svt_string;
|
||||
start++;
|
||||
}
|
||||
else if(!strcmp(Tokens[start], "int"))
|
||||
{
|
||||
newvar_type = svt_int;
|
||||
start++;
|
||||
}
|
||||
else if(!strcmp(Tokens[start], "mobj"))
|
||||
{
|
||||
newvar_type = svt_mobj;
|
||||
start++;
|
||||
}
|
||||
else if(!strcmp(Tokens[start], "fixed") || !strcmp(Tokens[start], "float"))
|
||||
{
|
||||
newvar_type = svt_fixed;
|
||||
start++;
|
||||
}
|
||||
else if(!strcmp(Tokens[start], "script")) // check for script creation
|
||||
{
|
||||
spec_script();
|
||||
return true; // used tokens
|
||||
}
|
||||
|
||||
// are we creating a new variable?
|
||||
if(newvar_type != -1)
|
||||
{
|
||||
ParseVarLine(newvar_type, newvar_script, start);
|
||||
return true; // used tokens
|
||||
}
|
||||
|
||||
return false; // not used: try normal parsing
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ADD SCRIPT
|
||||
//
|
||||
// when the level is first loaded, all the
|
||||
// scripts are simply stored in the levelscript.
|
||||
// before the level starts, this script is
|
||||
// preprocessed and run like any other. This allows
|
||||
// the individual scripts to be derived from the
|
||||
// levelscript. When the interpreter detects the
|
||||
// 'script' keyword this function is called
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::spec_script()
|
||||
{
|
||||
int scriptnum;
|
||||
int datasize;
|
||||
DFsScript *newscript;
|
||||
|
||||
scriptnum = 0;
|
||||
|
||||
if(!Section)
|
||||
{
|
||||
script_error("need seperators for newscript\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// presume that the first token is "newscript"
|
||||
|
||||
if(NumTokens < 2)
|
||||
{
|
||||
script_error("need newscript number\n");
|
||||
return;
|
||||
}
|
||||
|
||||
svalue_t result;
|
||||
EvaluateExpression(result, 1, NumTokens-1);
|
||||
scriptnum = intvalue(result);
|
||||
|
||||
if(scriptnum < 0)
|
||||
{
|
||||
script_error("invalid newscript number\n");
|
||||
return;
|
||||
}
|
||||
|
||||
newscript = Create<DFsScript>();
|
||||
|
||||
// add to scripts list of parent
|
||||
Script->children[scriptnum] = newscript;
|
||||
GC::WriteBarrier(Script, newscript);
|
||||
|
||||
// copy newscript data
|
||||
// workout newscript size: -2 to ignore { and }
|
||||
datasize = (Section->end_index - Section->start_index - 2);
|
||||
|
||||
// alloc extra 10 for safety
|
||||
newscript->data = (char *)malloc(datasize+10);
|
||||
|
||||
// copy from parent newscript (levelscript)
|
||||
// ignore first char which is {
|
||||
memcpy(newscript->data, Script->SectionStart(Section) + 1, datasize);
|
||||
|
||||
// tack on a 0 to end the string
|
||||
newscript->data[datasize] = '\0';
|
||||
|
||||
newscript->scriptnum = scriptnum;
|
||||
newscript->parent = Script; // remember parent
|
||||
|
||||
// preprocess the newscript now
|
||||
newscript->Preprocess(Level);
|
||||
|
||||
// we dont want to run the newscript, only add it
|
||||
// jump past the newscript in parsing
|
||||
|
||||
Rover = Script->SectionEnd(Section) + 1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// evaluate_function: once parse.c is pretty
|
||||
// sure it has a function to run it calls
|
||||
// this. evaluate_function makes sure that
|
||||
// it is a function call first, then evaluates all
|
||||
// the arguments given to the function.
|
||||
// these are built into an argc/argv-style
|
||||
// list. the function 'handler' is then called.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::EvaluateFunction(svalue_t &result, int start, int stop)
|
||||
{
|
||||
DFsVariable *func = NULL;
|
||||
int startpoint, endpoint;
|
||||
|
||||
// the arguments need to be built locally in case of
|
||||
// function returns as function arguments eg
|
||||
// print("here is a random number: ", rnd() );
|
||||
|
||||
int argc;
|
||||
svalue_t argv[MAXARGS];
|
||||
|
||||
if(TokenType[start] != function || TokenType[stop] != operator_
|
||||
|| Tokens[stop][0] != ')' )
|
||||
{
|
||||
script_error("misplaced closing paren\n");
|
||||
}
|
||||
|
||||
// all the functions are stored in the global script
|
||||
else if( !(func = Level->FraggleScriptThinker->GlobalScript->VariableForName (Tokens[start])) )
|
||||
{
|
||||
script_error("no such function: '%s'\n",Tokens[start]);
|
||||
}
|
||||
|
||||
else if(func->type != svt_function && func->type != svt_linespec)
|
||||
{
|
||||
script_error("'%s' not a function\n", Tokens[start]);
|
||||
}
|
||||
|
||||
// build the argument list
|
||||
// use a C command-line style system rather than
|
||||
// a system using a fixed length list
|
||||
|
||||
argc = 0;
|
||||
endpoint = start + 2; // ignore the function name and first bracket
|
||||
|
||||
while(endpoint < stop)
|
||||
{
|
||||
startpoint = endpoint;
|
||||
endpoint = FindOperator(startpoint, stop-1, ",");
|
||||
|
||||
// check for -1: no more ','s
|
||||
if(endpoint == -1)
|
||||
{ // evaluate the last expression
|
||||
endpoint = stop;
|
||||
}
|
||||
if(endpoint-1 < startpoint)
|
||||
break;
|
||||
|
||||
EvaluateExpression(argv[argc], startpoint, endpoint-1);
|
||||
endpoint++; // skip the ','
|
||||
argc++;
|
||||
}
|
||||
|
||||
// store the arguments in the global arglist
|
||||
t_argc = argc;
|
||||
t_argv = argv;
|
||||
|
||||
// haleyjd: return values can propagate to void functions, so
|
||||
// t_return needs to be cleared now
|
||||
|
||||
t_return.type = svt_int;
|
||||
t_return.value.i = 0;
|
||||
|
||||
// now run the function
|
||||
if (func->type == svt_function)
|
||||
{
|
||||
(this->*func->value.handler)();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunLineSpecial(func->value.ls);
|
||||
}
|
||||
|
||||
// return the returned value
|
||||
result = t_return;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// structure dot (.) operator
|
||||
// there are not really any structs in FraggleScript, it's
|
||||
// just a different way of calling a function that looks
|
||||
// nicer. ie
|
||||
// a.b() = a.b = b(a)
|
||||
// a.b(c) = b(a,c)
|
||||
//
|
||||
// this function is just based on the one above
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::OPstructure(svalue_t &result, int start, int n, int stop)
|
||||
{
|
||||
DFsVariable *func = NULL;
|
||||
|
||||
// the arguments need to be built locally in case of
|
||||
// function returns as function arguments eg
|
||||
// print("here is a random number: ", rnd() );
|
||||
|
||||
int argc;
|
||||
svalue_t argv[MAXARGS];
|
||||
|
||||
// all the functions are stored in the global script
|
||||
if( !(func = Level->FraggleScriptThinker->GlobalScript->VariableForName (Tokens[n+1])) )
|
||||
{
|
||||
script_error("no such function: '%s'\n",Tokens[n+1]);
|
||||
}
|
||||
|
||||
else if(func->type != svt_function)
|
||||
{
|
||||
script_error("'%s' not a function\n", Tokens[n+1]);
|
||||
}
|
||||
|
||||
// build the argument list
|
||||
|
||||
// add the left part as first arg
|
||||
|
||||
EvaluateExpression(argv[0], start, n-1);
|
||||
argc = 1; // start on second argv
|
||||
|
||||
if(stop != n+1) // can be a.b not a.b()
|
||||
{
|
||||
int startpoint, endpoint;
|
||||
|
||||
// ignore the function name and first bracket
|
||||
endpoint = n + 3;
|
||||
|
||||
while(endpoint < stop)
|
||||
{
|
||||
startpoint = endpoint;
|
||||
endpoint = FindOperator(startpoint, stop-1, ",");
|
||||
|
||||
// check for -1: no more ','s
|
||||
if(endpoint == -1)
|
||||
{ // evaluate the last expression
|
||||
endpoint = stop;
|
||||
}
|
||||
if(endpoint-1 < startpoint)
|
||||
break;
|
||||
|
||||
EvaluateExpression(argv[argc], startpoint, endpoint-1);
|
||||
endpoint++; // skip the ','
|
||||
argc++;
|
||||
}
|
||||
}
|
||||
|
||||
// store the arguments in the global arglist
|
||||
t_argc = argc;
|
||||
t_argv = argv;
|
||||
t_func = func->Name;
|
||||
|
||||
// haleyjd: return values can propagate to void functions, so
|
||||
// t_return needs to be cleared now
|
||||
|
||||
t_return.type = svt_int;
|
||||
t_return.value.i = 0;
|
||||
|
||||
// now run the function
|
||||
(this->*func->value.handler)();
|
||||
|
||||
// return the returned value
|
||||
result = t_return;
|
||||
}
|
||||
|
||||
|
||||
435
src/playsim/fragglescript/t_variable.cpp
Normal file
435
src/playsim/fragglescript/t_variable.cpp
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
// Copyright(C) 2002-2008 Christoph Oelckers
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Variables.
|
||||
//
|
||||
// Variable code: create new variables, look up variables, get value,
|
||||
// set value
|
||||
//
|
||||
// variables are stored inside the individual scripts, to allow for
|
||||
// 'local' and 'global' variables. This way, individual scripts cannot
|
||||
// access variables in other scripts. However, 'global' variables can
|
||||
// be made which can be accessed by all scripts. These are stored inside
|
||||
// a dedicated DFsScript which exists only to hold all of these global
|
||||
// variables.
|
||||
//
|
||||
// functions are also stored as variables, these are kept in the global
|
||||
// script so they can be accessed by all scripts. function variables
|
||||
// cannot be set or changed inside the scripts themselves.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "t_script.h"
|
||||
#include "a_pickups.h"
|
||||
#include "serializer.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int intvalue(const svalue_t &v)
|
||||
{
|
||||
return (v.type == svt_string ? atoi(v.string) :
|
||||
v.type == svt_fixed ? (int)(v.value.f / 65536.) :
|
||||
v.type == svt_mobj ? -1 : v.value.i );
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
fsfix fixedvalue(const svalue_t &v)
|
||||
{
|
||||
return (v.type == svt_fixed ? v.value.f :
|
||||
v.type == svt_string ? (fsfix)(atof(v.string) * 65536.) :
|
||||
v.type == svt_mobj ? -65536 : v.value.i * 65536 );
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
double floatvalue(const svalue_t &v)
|
||||
{
|
||||
return
|
||||
v.type == svt_string ? atof(v.string) :
|
||||
v.type == svt_fixed ? v.value.f / 65536. :
|
||||
v.type == svt_mobj ? -1. : (double)v.value.i;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// sf: string value of an svalue_t
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const char *stringvalue(const svalue_t & v)
|
||||
{
|
||||
static char buffer[256];
|
||||
|
||||
switch(v.type)
|
||||
{
|
||||
case svt_string:
|
||||
return v.string;
|
||||
|
||||
case svt_mobj:
|
||||
// return the class name
|
||||
return (const char *)v.value.mobj->GetClass()->TypeName;
|
||||
|
||||
case svt_fixed:
|
||||
{
|
||||
double val = v.value.f / 65536.;
|
||||
mysnprintf(buffer, countof(buffer), "%g", val);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
case svt_int:
|
||||
default:
|
||||
mysnprintf(buffer, countof(buffer), "%i", v.value.i);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
AActor* actorvalue(FLevelLocals *Level, const svalue_t &svalue)
|
||||
{
|
||||
int intval;
|
||||
|
||||
if(svalue.type == svt_mobj)
|
||||
{
|
||||
// Inventory items in the player's inventory have to be considered non-present.
|
||||
if (svalue.value.mobj == NULL || !svalue.value.mobj->IsMapActor())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return svalue.value.mobj;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &SpawnedThings = Level->FraggleScriptThinker->SpawnedThings;
|
||||
// this requires some creativity. We use the intvalue
|
||||
// as the thing number of a thing in the level
|
||||
intval = intvalue(svalue);
|
||||
|
||||
if(intval < 0 || intval >= (int)SpawnedThings.Size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
// Inventory items in the player's inventory have to be considered non-present.
|
||||
if (SpawnedThings[intval] == nullptr || !SpawnedThings[intval]->IsMapActor())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SpawnedThings[intval];
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
IMPLEMENT_CLASS(DFsVariable, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(DFsVariable)
|
||||
IMPLEMENT_POINTER(next)
|
||||
IMPLEMENT_POINTER(actor)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsVariable::DFsVariable(const char * _name)
|
||||
{
|
||||
Name=_name;
|
||||
type=svt_int;
|
||||
actor = nullptr;
|
||||
value.i=0;
|
||||
next = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// returns an svalue_t holding the current
|
||||
// value of a particular variable.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsVariable::GetValue(svalue_t &returnvar)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case svt_pInt:
|
||||
returnvar.type = svt_int;
|
||||
returnvar.value.i = *value.pI;
|
||||
break;
|
||||
|
||||
case svt_pMobj:
|
||||
returnvar.type = svt_mobj;
|
||||
returnvar.value.mobj = *value.pMobj;
|
||||
break;
|
||||
|
||||
case svt_mobj:
|
||||
returnvar.type = type;
|
||||
returnvar.value.mobj = actor;
|
||||
break;
|
||||
|
||||
case svt_linespec:
|
||||
returnvar.type = svt_int;
|
||||
returnvar.value.i = value.ls->number;
|
||||
break;
|
||||
|
||||
case svt_string:
|
||||
returnvar.type = type;
|
||||
returnvar.string = string;
|
||||
break;
|
||||
|
||||
default:
|
||||
// copy the value (also handles fixed)
|
||||
returnvar.type = type;
|
||||
returnvar.value.i = value.i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// set a variable to a value from an svalue_t
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsVariable::SetValue(FLevelLocals *Level, const svalue_t &newvalue)
|
||||
{
|
||||
if(type == svt_const)
|
||||
{
|
||||
// const adapts to the value it is set to
|
||||
type = newvalue.type;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case svt_int:
|
||||
value.i = intvalue(newvalue);
|
||||
break;
|
||||
|
||||
case svt_string:
|
||||
if (newvalue.type == svt_string)
|
||||
{
|
||||
string = newvalue.string;
|
||||
}
|
||||
else
|
||||
{
|
||||
string = stringvalue(newvalue);
|
||||
}
|
||||
break;
|
||||
|
||||
case svt_fixed:
|
||||
value.fixed = fixedvalue(newvalue);
|
||||
break;
|
||||
|
||||
case svt_mobj:
|
||||
actor = actorvalue(Level, newvalue);
|
||||
break;
|
||||
|
||||
case svt_pInt:
|
||||
*value.pI = intvalue(newvalue);
|
||||
break;
|
||||
|
||||
case svt_pMobj:
|
||||
*value.pMobj = actorvalue(Level, newvalue);
|
||||
break;
|
||||
|
||||
case svt_function:
|
||||
script_error("attempt to set function to a value\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
script_error("invalid variable type\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Archive one script variable
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsVariable::Serialize(FSerializer & ar)
|
||||
{
|
||||
Super::Serialize(ar);
|
||||
ar("name", Name)
|
||||
("type", type)
|
||||
("string", string)
|
||||
("actor", actor)
|
||||
("value", value.i)
|
||||
("next", next);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// From here: variable related functions inside DFsScript
|
||||
//
|
||||
//==========================================================================
|
||||
//==========================================================================
|
||||
//
|
||||
// create a new variable in a particular script.
|
||||
// returns a pointer to the new variable.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsVariable *DFsScript::NewVariable(const char *name, int vtype)
|
||||
{
|
||||
DFsVariable *newvar = Create<DFsVariable>(name);
|
||||
newvar->type = vtype;
|
||||
|
||||
int n = variable_hash(name);
|
||||
newvar->next = variables[n];
|
||||
variables[n] = newvar;
|
||||
GC::WriteBarrier(this, newvar);
|
||||
return newvar;
|
||||
}
|
||||
|
||||
|
||||
void DFsScript::NewFunction(const char *name, void (FParser::*handler)() )
|
||||
{
|
||||
NewVariable (name, svt_function)->value.handler = handler;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// search a particular script for a variable, which
|
||||
// is returned if it exists
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsVariable *DFsScript::VariableForName(const char *name)
|
||||
{
|
||||
int n = variable_hash(name);
|
||||
DFsVariable *current = variables[n];
|
||||
|
||||
while(current)
|
||||
{
|
||||
if(!strcmp(name, current->Name)) // found it?
|
||||
return current;
|
||||
current = current->next; // check next in chain
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// find_variable checks through the current script, level script
|
||||
// and global script to try to find the variable of the name wanted
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DFsVariable *DFsScript::FindVariable(const char *name, DFsScript *GlobalScript)
|
||||
{
|
||||
DFsVariable *var;
|
||||
DFsScript *current = this;
|
||||
|
||||
while(current)
|
||||
{
|
||||
// check this script
|
||||
if ((var = current->VariableForName(name)))
|
||||
return var;
|
||||
|
||||
// Since the global script cannot be serialized, we cannot store a pointer to it, because this cannot be safely restored during deserialization.
|
||||
// To compensate we need to check the relationship explicitly here.
|
||||
if (current->parent == nullptr && current != GlobalScript)
|
||||
current = GlobalScript;
|
||||
else
|
||||
current = current->parent; // try the parent of this one
|
||||
}
|
||||
|
||||
return NULL; // no variable
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// free all the variables in a given script
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFsScript::ClearVariables(bool complete)
|
||||
{
|
||||
int i;
|
||||
DFsVariable *current, *next;
|
||||
|
||||
for(i=0; i<VARIABLESLOTS; i++)
|
||||
{
|
||||
current = variables[i];
|
||||
|
||||
// go thru this chain
|
||||
while(current)
|
||||
{
|
||||
// labels are added before variables, during
|
||||
// preprocessing, so will be at the end of the chain
|
||||
// we can be sure there are no more variables to free
|
||||
if(current->type == svt_label && !complete) break;
|
||||
|
||||
next = current->next; // save for after freeing
|
||||
|
||||
current->Destroy();
|
||||
current = next; // go to next in chain
|
||||
}
|
||||
// start of labels or NULL
|
||||
variables[i] = current;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *DFsScript::LabelValue(const svalue_t &v)
|
||||
{
|
||||
if (v.type == svt_label) return data + v.value.i;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue