- Update scripting branch to trunk.

SVN r3758 (scripting)
This commit is contained in:
Randy Heit 2012-07-14 03:04:41 +00:00
commit 562cf04db2
614 changed files with 63691 additions and 31256 deletions

204
src/fragglescript/t_cmd.cpp Normal file
View file

@ -0,0 +1,204 @@
/*
** t_cmd.cpp
** 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...
**
**---------------------------------------------------------------------------
** Copyright 2005 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <string.h>
#include <stdio.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"
//==========================================================================
//
//
//
//==========================================================================
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(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);
}
}
G_ChangeLevel(nextmap, 0, flags, NextSkill);
}
//==========================================================================
//
//
//
//==========================================================================
void FS_EmulateCmd(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();
fixed_t playerviewheight = (fixed_t)(sc.Float*FRACUNIT);
for(int i=0;i<MAXPLAYERS;i++)
{
// No, this is not correct. But this is the way Legacy WADs expect it to be handled!
if (players[i].mo != NULL) players[i].mo->ViewHeight = playerviewheight;
players[i].Uncrouch();
}
while (sc.GetString())
{
if (sc.Compare(";")) break;
}
}
else if (sc.Compare("map"))
{
FS_MapCmd(sc);
}
else if (sc.Compare("gr_fogdensity"))
{
sc.MustGetNumber();
// Using this disables most MAPINFO fog options!
Renderer->SetFogParams(sc.Number*70/400, 0xff000000, 0, 0);
}
else if (sc.Compare("gr_fogcolor"))
{
sc.MustGetString();
level.fadeto = strtol(sc.String, NULL, 16);
}
else
{
// Skip unhandled commands
while (sc.GetString())
{
if (sc.Compare(";")) break;
}
}
}
}

14
src/fragglescript/t_fs.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef T_FS_H
#define T_FS_H
// global FS interface
struct MapData;
class AActor;
void T_PreprocessScripts();
void T_LoadScripts(MapData * map);
void T_AddSpawnedThing(AActor * );
#endif

View file

@ -0,0 +1,203 @@
/*
** t_fspic.cpp
** Fragglescript HUD pics (incomplete and untested!)
**
**---------------------------------------------------------------------------
** Copyright 2005 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "t_script.h"
#include "doomtype.h"
#include "p_local.h"
#include "farchive.h"
#include "sbar.h"
#include "v_video.h"
struct FHudPic
{
FTextureID texturenum;
int xpos;
int ypos;
bool draw;
void Serialize(FArchive & arc)
{
arc << xpos << ypos << draw << texturenum;
}
};
//======================================================================
//
//======================================================================
class DHUDPicManager : public DHUDMessage
{
// This is no real hudmessage but this way I don't need any external code to handle this
// because the hudmessage and thinker code handles everything automatically
DECLARE_CLASS(DHUDPicManager, DHUDMessage)
float basetrans;
public:
TArray<FHudPic> piclist;
DHUDPicManager();
~DHUDPicManager() {}
void Serialize(FArchive & ar);
virtual void DoDraw (int linenum, int x, int y, int hudheight, float translucent);
} ;
IMPLEMENT_CLASS(DHUDPicManager)
//======================================================================
//
//======================================================================
DHUDPicManager::DHUDPicManager()
{
HUDWidth=HUDHeight=0;
basetrans=0.8f;
//SetID(0xffffffff);
NumLines=1;
HoldTics=0; // stay forever!
//logtoconsole=false;
}
//======================================================================
//
//======================================================================
void DHUDPicManager::Serialize(FArchive & ar)
{
Super::Serialize(ar);
short count=piclist.Size();
ar << count << basetrans;
if (ar.IsLoading()) piclist.Resize(count);
for(int i=0;i<count;i++) piclist[i].Serialize(ar);
}
//======================================================================
//
//======================================================================
void DHUDPicManager::DoDraw (int linenum, int x, int y, int hudheight, float translucent)
{
for(unsigned int i=0; i<piclist.Size();i++) if (piclist[i].texturenum.isValid() && piclist[i].draw)
{
FTexture * tex = TexMan[piclist[i].texturenum];
if (tex) screen->DrawTexture(tex, piclist[i].xpos, piclist[i].ypos, DTA_320x200, true,
DTA_Alpha, (fixed_t)(translucent*basetrans*FRACUNIT), TAG_DONE);
}
}
//======================================================================
//
//======================================================================
static TArray<FHudPic> & GetPicList()
{
//TThinkerIterator<DHUDPicManager> it;
DHUDPicManager * pm=NULL;//it.Next();
if (!pm) pm=new DHUDPicManager;
return pm->piclist;
}
//======================================================================
//
// External interface
//
//======================================================================
//======================================================================
//
//======================================================================
int HU_GetFSPic(FTextureID texturenum, int xpos, int ypos)
{
TArray<FHudPic> &piclist=GetPicList();
unsigned int i;
for(i=0;i<piclist.Size();i++) if (piclist[i].texturenum.isValid()) continue;
if (i==piclist.Size()) i=piclist.Reserve(1);
FHudPic * pic=&piclist[i];
piclist[i].texturenum = texturenum;
piclist[i].xpos = xpos;
piclist[i].ypos = ypos;
piclist[i].draw = false;
return i;
}
//======================================================================
//
//======================================================================
int HU_DeleteFSPic(unsigned handle)
{
TArray<FHudPic> &piclist=GetPicList();
if(handle >= piclist.Size()) return -1;
piclist[handle].texturenum.SetInvalid();
return 0;
}
//======================================================================
//
//======================================================================
int HU_ModifyFSPic(unsigned handle, FTextureID texturenum, int xpos, int ypos)
{
TArray<FHudPic> &piclist=GetPicList();
if(handle >= piclist.Size()) return -1;
if(!piclist[handle].texturenum.isValid()) return -1;
piclist[handle].texturenum = texturenum;
piclist[handle].xpos = xpos;
piclist[handle].ypos = ypos;
return 0;
}
//======================================================================
//
//======================================================================
int HU_FSDisplay(unsigned handle, bool newval)
{
TArray<FHudPic> &piclist=GetPicList();
if(handle >= piclist.Size()) return -1;
if(!piclist[handle].texturenum.isValid()) return -1;
piclist[handle].draw = newval;
return 0;
}

4845
src/fragglescript/t_func.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,367 @@
/*
** t_load.cpp
** FraggleScript loader
**
**---------------------------------------------------------------------------
** Copyright 2002-2005 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "w_wad.h"
#include "tarray.h"
#include "g_level.h"
#include "sc_man.h"
#include "s_sound.h"
#include "r_sky.h"
#include "t_script.h"
#include "cmdlib.h"
#include "p_lnspec.h"
#include "gi.h"
#include "xlat/xlat.h"
void T_Init();
class FScriptLoader
{
enum
{
RT_SCRIPT,
RT_INFO,
RT_OTHER,
} readtype;
int drownflag;
bool HasScripts;
bool IgnoreInfo;
void ParseInfoCmd(char *line, FString &scriptsrc);
public:
bool ParseInfo(MapData * map);
};
struct FFsOptions : public FOptionalMapinfoData
{
FFsOptions()
{
identifier = "fragglescript";
nocheckposition = false;
}
virtual FOptionalMapinfoData *Clone() const
{
FFsOptions *newopt = new FFsOptions;
newopt->identifier = identifier;
newopt->nocheckposition = nocheckposition;
return newopt;
}
bool nocheckposition;
};
DEFINE_MAP_OPTION(fs_nocheckposition, false)
{
FFsOptions *opt = info->GetOptData<FFsOptions>("fragglescript");
parse.ParseAssign();
if (parse.CheckAssign())
{
parse.sc.MustGetNumber();
opt->nocheckposition = !!parse.sc.Number;
}
else
{
opt->nocheckposition = true;
}
}
//-----------------------------------------------------------------------------
//
// 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();
strncpy(level.skypic1, sc.String, 8);
strncpy(level.skypic2, sc.String, 8);
level.skypic1[8]=level.skypic2[8]=0;
sky2texture = sky1texture = TexMan.GetTexture (sc.String, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
R_InitSkyMap ();
}
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();
strncpy(level.nextmap, sc.String, 8);
level.nextmap[8]=0;
}
else if (sc.Compare("nextsecret"))
{
sc.MustGetStringName("=");
sc.MustGetString();
strncpy(level.secretmap, sc.String, 8);
level.secretmap[8]=0;
}
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(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)
{
new DFraggleThinker;
DFraggleThinker::ActiveThinker->LevelScript->data = copystring(scriptsrc.GetChars());
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.
FFsOptions *opt = level.info->GetOptData<FFsOptions>("fragglescript", false);
if (opt != NULL)
{
DFraggleThinker::ActiveThinker->nocheckposition = opt->nocheckposition;
}
}
delete[] lump;
return HasScripts;
}
//-----------------------------------------------------------------------------
//
// Starts the level info parser
// and patches the global linedef translation table
//
//-----------------------------------------------------------------------------
void T_LoadScripts(MapData *map)
{
FScriptLoader parser;
T_Init();
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 && SimpleLineTranslations[272 - 2*HasScripts].special == FS_Execute)
{
FLineTrans t = SimpleLineTranslations[270];
SimpleLineTranslations[270] = SimpleLineTranslations[272];
SimpleLineTranslations[272] = t;
}
}
//-----------------------------------------------------------------------------
//
// Adds an actor to the list of spawned things
//
//-----------------------------------------------------------------------------
void T_AddSpawnedThing(AActor * ac)
{
if (DFraggleThinker::ActiveThinker)
{
TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
SpawnedThings.Push(GC::ReadBarrier(ac));
}
}

View file

@ -0,0 +1,653 @@
// 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
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
/* includes ************************/
#include "t_script.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]);
if(var)
{
EvaluateExpression(result, n+1, stop);
var->SetValue (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.type = svt_fixed;
result.value.f = FixedMul(fixedvalue(left), fixedvalue(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)
{
fixed_t fr;
if((fr = fixedvalue(right)) == 0)
script_error("divide by zero\n");
else
{
result.type = svt_fixed;
result.value.f = FixedDiv(fixedvalue(left), fr);
}
}
else
{
int ir;
if(!(ir = intvalue(right)))
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]);
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 (result);
}
else
{
result.value.f = fixedvalue(result) + FRACUNIT;
result.type = svt_fixed;
var->SetValue (result);
}
}
else if(stop == n) // n++
{
svalue_t newvalue;
DFsVariable *var;
var = Script->FindVariable(Tokens[start]);
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 (newvalue);
}
else
{
newvalue.type = svt_fixed;
newvalue.value.f = fixedvalue(result) + FRACUNIT;
var->SetValue (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]);
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 (result);
}
else
{
result.value.f = fixedvalue(result) - FRACUNIT;
result.type = svt_fixed;
var->SetValue (result);
}
}
else if(stop == n) // n++
{
svalue_t newvalue;
DFsVariable *var;
var = Script->FindVariable(Tokens[start]);
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 (newvalue);
}
else
{
newvalue.type = svt_fixed;
newvalue.value.f = fixedvalue(result) - FRACUNIT;
var->SetValue (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));
}

View file

@ -0,0 +1,746 @@
// 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
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
/* includes ************************/
#include <stdarg.h>
#include "t_script.h"
#include "s_sound.h"
#include "v_text.h"
#include "c_cvars.h"
#include "i_system.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)
{
I_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.type = svt_fixed;
returnvar.value.f = (fixed_t)(atof(Tokens[n]) * FRACUNIT);
}
else
{
returnvar.type = svt_int;
returnvar.value.i = atoi(Tokens[n]);
}
break;
case name_:
var = Script->FindVariable(Tokens[n]);
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 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);
I_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

View file

@ -0,0 +1,445 @@
// 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
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
/* includes ************************/
#include "t_script.h"
#include "i_system.h"
#include "w_wad.h"
#include "farchive.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_POINTY_CLASS(DFsSection)
DECLARE_POINTER(next)
END_POINTERS
//==========================================================================
//
//
//
//==========================================================================
void DFsSection::Serialize(FArchive &ar)
{
Super::Serialize(ar);
ar << type << start_index << end_index << loop_index << 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] = NULL;
}
}
//==========================================================================
//
// create section
//
//==========================================================================
DFsSection *DFsScript::NewSection(const char *brace)
{
int n = section_hash(brace);
DFsSection *newsec = new 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()
{
char *end = data + len;
char *rover = data;
// allocate space for the tokens
FParser parse(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()
{
len = (int)strlen(data);
ProcessFindChar(data, 0); // fill in everything
DryRunScript();
}
//==========================================================================
//
// 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(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(this);
parse.Run(lump, lump, lump+lumplen);
// free the lump
delete[] lump;
}

View file

@ -0,0 +1,725 @@
// 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)
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
#include "t_script.h"
#include "p_lnspec.h"
#include "a_keys.h"
#include "d_player.h"
#include "p_spec.h"
#include "c_dispatch.h"
#include "i_system.h"
#include "doomerrors.h"
#include "doomstat.h"
#include "farchive.h"
//==========================================================================
//
// global variables
// These two are the last remaining ones:
// - The global script contains static data so it must be global
// - The trigger is referenced by a global variable. However, it is set
// each time a script is started so that's not a problem.
//
//==========================================================================
DFsScript *global_script;
AActor *trigger_obj;
//==========================================================================
//
//
//
//==========================================================================
#define DECLARE_16_POINTERS(v, i) \
DECLARE_POINTER(v[i]) \
DECLARE_POINTER(v[i+1]) \
DECLARE_POINTER(v[i+2]) \
DECLARE_POINTER(v[i+3]) \
DECLARE_POINTER(v[i+4]) \
DECLARE_POINTER(v[i+5]) \
DECLARE_POINTER(v[i+6]) \
DECLARE_POINTER(v[i+7]) \
DECLARE_POINTER(v[i+8]) \
DECLARE_POINTER(v[i+9]) \
DECLARE_POINTER(v[i+10]) \
DECLARE_POINTER(v[i+11]) \
DECLARE_POINTER(v[i+12]) \
DECLARE_POINTER(v[i+13]) \
DECLARE_POINTER(v[i+14]) \
DECLARE_POINTER(v[i+15]) \
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_POINTY_CLASS(DFsScript)
DECLARE_POINTER(parent)
DECLARE_POINTER(trigger)
DECLARE_16_POINTERS(sections, 0)
DECLARE_POINTER(sections[16])
DECLARE_16_POINTERS(variables, 0)
DECLARE_16_POINTERS(children, 0)
DECLARE_16_POINTERS(children, 16)
DECLARE_16_POINTERS(children, 32)
DECLARE_16_POINTERS(children, 48)
DECLARE_16_POINTERS(children, 64)
DECLARE_16_POINTERS(children, 80)
DECLARE_16_POINTERS(children, 96)
DECLARE_16_POINTERS(children, 112)
DECLARE_16_POINTERS(children, 128)
DECLARE_16_POINTERS(children, 144)
DECLARE_16_POINTERS(children, 160)
DECLARE_16_POINTERS(children, 176)
DECLARE_16_POINTERS(children, 192)
DECLARE_16_POINTERS(children, 208)
DECLARE_16_POINTERS(children, 224)
DECLARE_16_POINTERS(children, 240)
DECLARE_POINTER(children[256])
END_POINTERS
//==========================================================================
//
//
//
//==========================================================================
void DFsScript::ClearChildren()
{
int j;
for(j=0;j<MAXSCRIPTS;j++) if (children[j])
{
children[j]->Destroy();
children[j]=NULL;
}
}
//==========================================================================
//
//
//
//==========================================================================
DFsScript::DFsScript()
{
int i;
for(i=0; i<SECTIONSLOTS; i++) sections[i] = NULL;
for(i=0; i<VARIABLESLOTS; i++) variables[i] = NULL;
for(i=0; i<MAXSCRIPTS; i++) children[i] = NULL;
data = NULL;
scriptnum = -1;
len = 0;
parent = NULL;
trigger = NULL;
lastiftrue = false;
}
//==========================================================================
//
//
//
//==========================================================================
void DFsScript::Destroy()
{
ClearVariables(true);
ClearSections();
ClearChildren();
parent = NULL;
if (data != NULL) delete [] data;
data = NULL;
parent = NULL;
trigger = NULL;
Super::Destroy();
}
//==========================================================================
//
//
//
//==========================================================================
void DFsScript::Serialize(FArchive &arc)
{
Super::Serialize(arc);
// don't save a reference to the global script
if (parent == global_script) parent = NULL;
arc << data << scriptnum << len << parent << trigger << lastiftrue;
for(int i=0; i< SECTIONSLOTS; i++) arc << sections[i];
for(int i=0; i< VARIABLESLOTS; i++) arc << variables[i];
for(int i=0; i< MAXSCRIPTS; i++) arc << children[i];
if (parent == NULL) parent = global_script;
}
//==========================================================================
//
// run_script
//
// the function called by t_script.c
//
//==========================================================================
void DFsScript::ParseScript(char *position)
{
if (position == NULL)
{
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;
}
trigger_obj = trigger; // set trigger
try
{
FParser parse(this);
parse.Run(position, data, data + len);
}
catch (CRecoverableError &err)
{
Printf ("%s\n", err.GetMessage());
}
// dont clear global vars!
if(scriptnum != -1) ClearVariables(); // free variables
// haleyjd
lastiftrue = false;
}
//==========================================================================
//
// Running Scripts
//
//==========================================================================
IMPLEMENT_POINTY_CLASS(DRunningScript)
DECLARE_POINTER(prev)
DECLARE_POINTER(next)
DECLARE_POINTER(trigger)
DECLARE_16_POINTERS(variables, 0)
END_POINTERS
//==========================================================================
//
//
//
//==========================================================================
DRunningScript::DRunningScript(AActor *trigger, DFsScript *owner, int index)
{
prev = next = NULL;
script = owner;
GC::WriteBarrier(this, script);
save_point = index;
wait_type = wt_none;
wait_data = 0;
this->trigger = trigger;
if (owner == NULL)
{
for(int i=0; i< VARIABLESLOTS; i++) variables[i] = NULL;
}
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::Destroy()
{
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] = NULL;
}
Super::Destroy();
}
//==========================================================================
//
//
//
//==========================================================================
void DRunningScript::Serialize(FArchive &arc)
{
Super::Serialize(arc);
arc << script << save_point << wait_type << wait_data << prev << next << trigger;
for(int i=0; i< VARIABLESLOTS; i++) arc << variables[i];
}
//==========================================================================
//
// The main thinker
//
//==========================================================================
IMPLEMENT_POINTY_CLASS(DFraggleThinker)
DECLARE_POINTER(RunningScripts)
DECLARE_POINTER(LevelScript)
END_POINTERS
TObjPtr<DFraggleThinker> DFraggleThinker::ActiveThinker;
//==========================================================================
//
//
//
//==========================================================================
DFraggleThinker::DFraggleThinker()
: DThinker(STAT_SCRIPTS)
{
if (ActiveThinker)
{
I_Error ("Only one FraggleThinker is allowed to exist at a time.\nCheck your code.");
}
else
{
ActiveThinker = this;
RunningScripts = new DRunningScript;
LevelScript = new DFsScript;
LevelScript->parent = global_script;
GC::WriteBarrier(this, RunningScripts);
GC::WriteBarrier(this, LevelScript);
nocheckposition = false;
}
}
//==========================================================================
//
//
//
//==========================================================================
void DFraggleThinker::Destroy()
{
DRunningScript *p = RunningScripts;
while (p)
{
DRunningScript *q = p;
p = p->next;
q->prev = q->next = NULL;
q->Destroy();
}
RunningScripts = NULL;
LevelScript->Destroy();
LevelScript = NULL;
SpawnedThings.Clear();
ActiveThinker = NULL;
Super::Destroy();
}
//==========================================================================
//
//
//
//==========================================================================
void DFraggleThinker::Serialize(FArchive &arc)
{
Super::Serialize(arc);
arc << LevelScript << RunningScripts << SpawnedThings << nocheckposition;
}
//==========================================================================
//
// 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 = -1;
while ((secnum = P_FindSectorFromTag(script->wait_data, secnum)) >= 0)
{
sector_t *sec = &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] = NULL;
}
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);
// 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()
{
DFraggleThinker *th = DFraggleThinker::ActiveThinker;
if (th)
{
// run the levelscript first
// get the other scripts
// levelscript started by player 0 'superplayer'
th->LevelScript->trigger = players[0].mo;
th->LevelScript->Preprocess();
th->LevelScript->ParseScript();
}
}
//==========================================================================
//
//
//
//==========================================================================
static bool RunScript(int snum, AActor * t_trigger)
{
DFraggleThinker *th = DFraggleThinker::ActiveThinker;
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 = new DRunningScript(t_trigger, script, 0);
// hook into chain at start
th->AddRunningScript(runscr);
return true;
}
return false;
}
//==========================================================================
//
//
//
//==========================================================================
static int LS_FS_Execute (line_t *ln, AActor *it, bool backSide,
int arg0, int arg1, int arg2, int arg3, int arg4)
// FS_Execute(script#,firstsideonly,lock,msgtype)
{
if (arg1 && ln && backSide) return false;
if (arg2!=0 && !P_CheckKeys(it, arg2, !!arg3)) return false;
return RunScript(arg0,it);
}
//==========================================================================
//
//
//
//==========================================================================
void FS_Close()
{
int i;
DFsVariable *current, *next;
// we have to actually delete the global variables if we don't want
// to get them reported as memory leaks.
for(i=0; i<VARIABLESLOTS; i++)
{
current = global_script->variables[i];
while(current)
{
next = current->next; // save for after freeing
current->ObjectFlags |= OF_YesReallyDelete;
delete current;
current = next; // go to next in chain
}
}
GC::DelSoftRoot(global_script);
global_script->ObjectFlags |= OF_YesReallyDelete;
delete global_script;
}
void T_Init()
{
void init_functions();
if (global_script == NULL)
{
// I'd rather link the special here than make another source file depend on FS!
LineSpecials[FS_Execute]=LS_FS_Execute;
global_script = new DFsScript;
GC::AddSoftRoot(global_script);
init_functions();
atterm(FS_Close);
}
}
//==========================================================================
//
//
//
//==========================================================================
CCMD(fpuke)
{
int argc = argv.argc();
if (argc < 2)
{
Printf (" fpuke <script>\n");
}
else
{
RunScript(atoi(argv[1]), players[consoleplayer].mo);
}
}

View file

@ -0,0 +1,698 @@
// 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
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
#ifndef __T_SCRIPT_H__
#define __T_SCRIPT_H__
#include "p_setup.h"
#include "p_lnspec.h"
#include "m_fixed.h"
#include "actor.h"
#ifdef _MSC_VER
// This pragma saves 8kb of wasted code.
#pragma pointers_to_members( full_generality, single_inheritance )
#endif
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)
};
//==========================================================================
//
//
//
//==========================================================================
struct svalue_t
{
int type;
FString string;
union
{
int i;
fixed_t 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;
}
};
int intvalue(const svalue_t & v);
fixed_t fixedvalue(const svalue_t & v);
float floatvalue(const svalue_t & v);
const char *stringvalue(const svalue_t & v);
AActor *actorvalue(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
{
SDWORD i;
fixed_t 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(const svalue_t &newvalue);
void Serialize(FArchive &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 = NULL;
}
void Serialize(FArchive &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();
void Destroy();
void Serialize(FArchive &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);
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();
void Preprocess();
void ParseInclude(char *lumpname);
void ParseScript(char *rover = NULL);
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;
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(DFsScript *scr)
{
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);
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 Destroy();
void Serialize(FArchive &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:
TObjPtr<DFsScript> LevelScript;
TObjPtr<DRunningScript> RunningScripts;
TArray<TObjPtr<AActor> > SpawnedThings;
bool nocheckposition;
DFraggleThinker();
void Destroy();
void Serialize(FArchive & arc);
void Tick();
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, ...);
void FS_EmulateCmd(char * string);
extern AActor *trigger_obj;
extern DFsScript *global_script;
#endif

View file

@ -0,0 +1,628 @@
// 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
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
#include "t_script.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 = new 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();
// 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 = global_script->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 = global_script->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;
}

View file

@ -0,0 +1,437 @@
// 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.
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
#include "t_script.h"
#include "a_pickups.h"
#include "farchive.h"
//==========================================================================
//
//
//
//==========================================================================
int intvalue(const svalue_t &v)
{
return (v.type == svt_string ? atoi(v.string) :
v.type == svt_fixed ? (int)(v.value.f / FRACUNIT) :
v.type == svt_mobj ? -1 : v.value.i );
}
//==========================================================================
//
//
//
//==========================================================================
fixed_t fixedvalue(const svalue_t &v)
{
return (v.type == svt_fixed ? v.value.f :
v.type == svt_string ? (fixed_t)(atof(v.string) * FRACUNIT) :
v.type == svt_mobj ? -1*FRACUNIT : v.value.i * FRACUNIT );
}
//==========================================================================
//
//
//
//==========================================================================
float floatvalue(const svalue_t &v)
{
return (float)( (v.type == svt_string ? atof(v.string) :
v.type == svt_fixed ? (float)(v.value.f / (float)FRACUNIT) :
v.type == svt_mobj ? -1.f : (float)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 = ((double)v.value.f) / FRACUNIT;
mysnprintf(buffer, countof(buffer), "%g", val);
return buffer;
}
case svt_int:
default:
mysnprintf(buffer, countof(buffer), "%i", v.value.i);
return buffer;
}
}
//==========================================================================
//
//
//==========================================================================
AActor* actorvalue(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->IsKindOf(RUNTIME_CLASS(AInventory)) &&
static_cast<AInventory*>(svalue.value.mobj)->Owner != NULL)
{
return NULL;
}
return svalue.value.mobj;
}
else
{
TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->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] != NULL &&
SpawnedThings[intval]->IsKindOf(RUNTIME_CLASS(AInventory)) &&
barrier_cast<AInventory*>(SpawnedThings[intval])->Owner != NULL)
{
return NULL;
}
return SpawnedThings[intval];
}
}
//==========================================================================
//
//
//==========================================================================
IMPLEMENT_POINTY_CLASS(DFsVariable)
DECLARE_POINTER (next)
DECLARE_POINTER (actor)
END_POINTERS
//==========================================================================
//
//
//==========================================================================
DFsVariable::DFsVariable(const char * _name)
{
Name=_name;
type=svt_int;
actor = NULL;
value.i=0;
next=NULL;
}
//==========================================================================
//
// 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(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(newvalue);
break;
case svt_pInt:
*value.pI = intvalue(newvalue);
break;
case svt_pMobj:
*value.pMobj = actorvalue(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(FArchive & ar)
{
Super::Serialize(ar);
ar << Name << type << string << actor << value.i << 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 = new 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)
{
DFsVariable *var;
DFsScript *current = this;
while(current)
{
// check this script
if ((var = current->VariableForName(name)))
return var;
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;
}