From fae8ed444c54d6a441ffb30ac687f58c399991a9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 29 Jan 2011 11:09:38 +0000 Subject: [PATCH] - merged 3dfloors2 branch into trunk. SVN r3124 (trunk) --- src/CMakeLists.txt | 14 +- src/am_map.cpp | 134 +- src/compatibility.cpp | 2 +- src/fragglescript/t_cmd.cpp | 208 + src/fragglescript/t_fs.h | 14 + src/fragglescript/t_fspic.cpp | 202 + src/fragglescript/t_func.cpp | 4843 ++++++++++++++++++++ src/fragglescript/t_load.cpp | 368 ++ src/fragglescript/t_oper.cpp | 653 +++ src/fragglescript/t_parse.cpp | 746 +++ src/fragglescript/t_prepro.cpp | 445 ++ src/fragglescript/t_script.cpp | 725 +++ src/fragglescript/t_script.h | 698 +++ src/fragglescript/t_spec.cpp | 628 +++ src/fragglescript/t_variable.cpp | 437 ++ src/g_heretic/a_hereticweaps.cpp | 30 + src/g_level.h | 12 +- src/p_3dfloors.cpp | 220 +- src/p_3dfloors.h | 26 +- src/p_enemy.cpp | 19 + src/p_sectors.cpp | 2 + src/p_setup.cpp | 245 +- src/r_3dfloors.cpp | 161 + src/r_3dfloors.h | 69 + src/r_bsp.cpp | 324 +- src/r_bsp.h | 5 + src/r_defs.h | 35 +- src/r_main.cpp | 51 +- src/r_plane.cpp | 68 +- src/r_plane.h | 5 + src/r_polymost.cpp | 6 +- src/r_segs.cpp | 753 ++- src/r_segs.h | 8 +- src/r_state.h | 3 - src/r_things.cpp | 292 +- wadsrc/static/actors/shared/sharedmisc.txt | 6 + wadsrc/static/etc.h.txt | Bin 0 -> 491 bytes wadsrc/static/menudef.txt | 1 + wadsrc/static/things.h.txt | 224 + wadsrc/static/xlat/base.txt | 59 +- zdoom.vcproj | 60 + 41 files changed, 12357 insertions(+), 444 deletions(-) create mode 100644 src/fragglescript/t_cmd.cpp create mode 100644 src/fragglescript/t_fs.h create mode 100644 src/fragglescript/t_fspic.cpp create mode 100644 src/fragglescript/t_func.cpp create mode 100644 src/fragglescript/t_load.cpp create mode 100644 src/fragglescript/t_oper.cpp create mode 100644 src/fragglescript/t_parse.cpp create mode 100644 src/fragglescript/t_prepro.cpp create mode 100644 src/fragglescript/t_script.cpp create mode 100644 src/fragglescript/t_script.h create mode 100644 src/fragglescript/t_spec.cpp create mode 100644 src/fragglescript/t_variable.cpp create mode 100644 src/r_3dfloors.cpp create mode 100644 src/r_3dfloors.h create mode 100644 wadsrc/static/etc.h.txt create mode 100644 wadsrc/static/things.h.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1bfe9ab7c..940e375ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -708,6 +708,7 @@ add_executable( zdoom WIN32 p_xlat.cpp parsecontext.cpp po_man.cpp + r_3dfloors.cpp r_bsp.cpp r_data.cpp r_draw.cpp @@ -879,7 +880,18 @@ add_executable( zdoom WIN32 timidity/resample.cpp timidity/timidity.cpp xlat/parse_xlat.cpp - autozend.cpp ) + fragglescript/t_fspic.cpp + fragglescript/t_func.cpp + fragglescript/t_load.cpp + fragglescript/t_oper.cpp + fragglescript/t_parse.cpp + fragglescript/t_prepro.cpp + fragglescript/t_script.cpp + fragglescript/t_spec.cpp + fragglescript/t_variable.cpp + fragglescript/t_cmd.cpp + autozend.cpp +) set_source_files_properties( xlat/parse_xlat.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c" ) set_source_files_properties( sc_man.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h" ) diff --git a/src/am_map.cpp b/src/am_map.cpp index 7c709019b..d90735842 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -89,7 +89,7 @@ struct AMColor }; static AMColor Background, YourColor, WallColor, TSWallColor, - FDWallColor, CDWallColor, ThingColor, + FDWallColor, CDWallColor, EFWallColor, ThingColor, ThingColor_Item, ThingColor_CountItem, ThingColor_Monster, ThingColor_Friend, SpecialWallColor, SecretWallColor, GridColor, XHairColor, NotSeenColor, @@ -171,6 +171,7 @@ CVAR (Color, am_specialwallcolor, 0xffffff, CVAR_ARCHIVE); CVAR (Color, am_tswallcolor, 0x888888, CVAR_ARCHIVE); CVAR (Color, am_fdwallcolor, 0x887058, CVAR_ARCHIVE); CVAR (Color, am_cdwallcolor, 0x4c3820, CVAR_ARCHIVE); +CVAR (Color, am_efwallcolor, 0x665555, CVAR_ARCHIVE); CVAR (Color, am_thingcolor, 0xfcfcfc, CVAR_ARCHIVE); CVAR (Color, am_gridcolor, 0x8b5a2b, CVAR_ARCHIVE); CVAR (Color, am_xhaircolor, 0x808080, CVAR_ARCHIVE); @@ -909,7 +910,7 @@ static void AM_initColors (bool overlayed) ThingColor_Monster.FromCVar (am_ovthingcolor_monster); ThingColor.FromCVar (am_ovthingcolor); LockedColor.FromCVar (am_ovotherwallscolor); - FDWallColor = CDWallColor = LockedColor; + EFWallColor = FDWallColor = CDWallColor = LockedColor; TSWallColor.FromCVar (am_ovunseencolor); NotSeenColor = TSWallColor; InterTeleportColor.FromCVar (am_ovtelecolor); @@ -928,6 +929,7 @@ static void AM_initColors (bool overlayed) TSWallColor.FromCVar (am_tswallcolor); FDWallColor.FromCVar (am_fdwallcolor); CDWallColor.FromCVar (am_cdwallcolor); + EFWallColor.FromCVar (am_efwallcolor); ThingColor_Item.FromCVar (am_thingcolor_item); ThingColor_CountItem.FromCVar (am_thingcolor_citem); ThingColor_Friend.FromCVar (am_thingcolor_friend); @@ -968,7 +970,7 @@ static void AM_initColors (bool overlayed) SpecialWallColor = WallColor = DoomColors[3]; TSWallColor = DoomColors[4]; - FDWallColor = DoomColors[5]; + EFWallColor = FDWallColor = DoomColors[5]; LockedColor = CDWallColor = DoomColors[6]; ThingColor_Item = @@ -990,7 +992,7 @@ static void AM_initColors (bool overlayed) SpecialWallColor = WallColor = StrifeColors[3]; TSWallColor = StrifeColors[4]; - FDWallColor = StrifeColors[5]; + EFWallColor = FDWallColor = StrifeColors[5]; LockedColor = CDWallColor = StrifeColors[6]; ThingColor_Item = StrifeColors[10]; @@ -1012,7 +1014,7 @@ static void AM_initColors (bool overlayed) SpecialWallColor = WallColor = RavenColors[3]; TSWallColor = RavenColors[4]; - FDWallColor = RavenColors[5]; + EFWallColor = FDWallColor = RavenColors[5]; LockedColor = CDWallColor = RavenColors[6]; ThingColor = @@ -1647,6 +1649,56 @@ void AM_drawSubsectors() originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24)); // Coloring for the polygon colormap = sec->ColorMap; + + FTextureID maptex = sec->GetTexture(sector_t::floor); + +#ifdef _3DFLOORS + + if (sec->e->XFloor.ffloors.Size()) + { + secplane_t *floorplane = &sec->floorplane; + + // Look for the highest floor below the camera viewpoint. + // Check the center of the subsector's sector. Do not check each + // subsector separately because that might result in different planes for + // different subsectors of the same sector which is not wanted here. + // (Make the comparison in floating point to avoid overflows and improve performance.) + double secx; + double secy; + double cmpz = FIXED2DBL(viewz); + + if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector) + { + // For the actual camera sector use the current viewpoint as reference. + secx = FIXED2DBL(viewx); + secy = FIXED2DBL(viewy); + } + else + { + secx = FIXED2DBL(sec->soundorg[0]); + secy = FIXED2DBL(sec->soundorg[1]); + } + + for (unsigned int i = 0; i < sec->e->XFloor.ffloors.Size(); ++i) + { + F3DFloor *rover = sec->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + if (rover->flags & FF_FOG) continue; + if (rover->alpha == 0) continue; + if (rover->top.plane->ZatPoint(secx, secy) < cmpz) + { + maptex = *(rover->top.texture); + floorplane = rover->top.plane; + break; + } + } + + lightlist_t *light = P_GetPlaneLight(sec, floorplane, false); + floorlight = *light->p_lightlevel; + colormap = light->extra_colormap; + } +#endif + // If this subsector has not actually been seen yet (because you are cheating // to see it on the map), tint and desaturate it. if (!(subsectors[i].flags & SSECF_DRAWN)) @@ -1662,8 +1714,7 @@ void AM_drawSubsectors() } // Draw the polygon. - screen->FillSimplePoly( - TexMan(sec->GetTexture(sector_t::floor)), + screen->FillSimplePoly(TexMan(maptex), &points[0], points.Size(), originx, originy, scale / (FIXED2FLOAT(sec->GetXScale(sector_t::floor)) * float(1 << MAPBITS)), @@ -1777,6 +1828,69 @@ void AM_showSS() } } +#ifdef _3DFLOORS + +//============================================================================= +// +// Determines if a 3D floor boundary should be drawn +// +//============================================================================= + +bool AM_Check3DFloors(line_t *line) +{ + TArray &ff_front = line->frontsector->e->XFloor.ffloors; + TArray &ff_back = line->backsector->e->XFloor.ffloors; + + // No 3D floors so there's no boundary + if (ff_back.Size() == 0 && ff_front.Size() == 0) return false; + + int realfrontcount = 0; + int realbackcount = 0; + + for(unsigned i=0;iflags & FF_EXISTS)) continue; + if (rover->alpha == 0) continue; + realfrontcount++; + } + + for(unsigned i=0;iflags & FF_EXISTS)) continue; + if (rover->alpha == 0) continue; + realbackcount++; + } + // if the amount of 3D floors does not match there is a boundary + if (realfrontcount != realbackcount) return true; + + for(unsigned i=0;iflags & FF_EXISTS)) continue; + if (rover->alpha == 0) continue; + + bool found = false; + for(unsigned j=0;jflags & FF_EXISTS)) continue; + if (rover2->alpha == 0) continue; + if (rover->model == rover2->model && rover->flags == rover2->flags) + { + found = true; + break; + } + } + // At least one 3D floor in the front sector didn't have a match in the back sector so there is a boundary. + if (!found) return true; + } + // All 3D floors could be matched so let's not draw a boundary. + return false; +} +#endif + //============================================================================= // // Determines visible lines, draws them. @@ -1895,6 +2009,12 @@ void AM_drawWalls (bool allmap) { AM_drawMline(&l, CDWallColor); // ceiling level change } +#ifdef _3DFLOORS + else if (AM_Check3DFloors(&lines[i])) + { + AM_drawMline(&l, EFWallColor); // Extra floor border + } +#endif else if (am_cheat != 0) { AM_drawMline(&l, TSWallColor); diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 718bfecee..d9dfd083d 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -264,7 +264,7 @@ void CheckCompatibility(MapData *map) // When playing Doom IWAD levels force COMPAT_SHORTTEX and COMPATF_LIGHT. // I'm not sure if the IWAD maps actually need COMPATF_LIGHT but it certainly does not hurt. // TNT's MAP31 also needs COMPATF_STAIRINDEX but that only gets activated for TNT.WAD. - if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATSHORTTEX) && !(level.flags & LEVEL_HEXENFORMAT)) + if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATSHORTTEX) && level.maptype == MAPTYPE_DOOM) { ii_compatflags = COMPATF_SHORTTEX|COMPATF_LIGHT; if (gameinfo.flags & GI_COMPATSTAIRS) ii_compatflags |= COMPATF_STAIRINDEX; diff --git a/src/fragglescript/t_cmd.cpp b/src/fragglescript/t_cmd.cpp new file mode 100644 index 000000000..0bf88ea34 --- /dev/null +++ b/src/fragglescript/t_cmd.cpp @@ -0,0 +1,208 @@ +/* +** 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 +#include +#include "p_local.h" +#include "doomdef.h" +#include "doomstat.h" +#include "c_dispatch.h" +#include "sc_man.h" +#include "g_level.h" +#ifdef GLSUPPORT +#include "gl/gl_functions.h" +#endif + +//========================================================================== +// +// +// +//========================================================================== + +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(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;iViewHeight = 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! +#ifdef GLSUPPORT + gl_SetFogParams(sc.Number*70/400, 0xff000000, 0, 0); +#endif + } + 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; + } + } + } +} diff --git a/src/fragglescript/t_fs.h b/src/fragglescript/t_fs.h new file mode 100644 index 000000000..91f90edb3 --- /dev/null +++ b/src/fragglescript/t_fs.h @@ -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 diff --git a/src/fragglescript/t_fspic.cpp b/src/fragglescript/t_fspic.cpp new file mode 100644 index 000000000..8c6a8db3b --- /dev/null +++ b/src/fragglescript/t_fspic.cpp @@ -0,0 +1,202 @@ +/* +** 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" + + + +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 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;iDrawTexture(tex, piclist[i].xpos, piclist[i].ypos, DTA_320x200, true, + DTA_Alpha, (fixed_t)(translucent*basetrans*FRACUNIT), TAG_DONE); + } +} + + +//====================================================================== +// +//====================================================================== +static TArray & GetPicList() +{ + //TThinkerIterator 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 &piclist=GetPicList(); + unsigned int i; + + for(i=0;i &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 &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 &piclist=GetPicList(); + + if(handle >= piclist.Size()) return -1; + if(!piclist[handle].texturenum.isValid()) return -1; + + piclist[handle].draw = newval; + return 0; +} + diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp new file mode 100644 index 000000000..a67f75853 --- /dev/null +++ b/src/fragglescript/t_func.cpp @@ -0,0 +1,4843 @@ +// 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 +// +//-------------------------------------------------------------------------- +// +// Functions +// +// functions are stored as variables(see variable.c), the +// value being a pointer to a 'handler' function for the +// function. Arguments are stored in an argc/argv-style list +// +// this module contains all the handler functions for the +// basic FraggleScript Functions. +// +// 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 "templates.h" +#include "p_local.h" +#include "t_script.h" +#include "s_sound.h" +#include "p_lnspec.h" +#include "m_random.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "d_player.h" +#include "a_doomglobal.h" +#include "w_wad.h" +#include "gi.h" +#include "zstring.h" +#include "i_system.h" +#include "doomstat.h" +#include "g_level.h" +#include "v_palette.h" +#include "v_font.h" + +static FRandom pr_script("FScript"); + + +#define AngleToFixed(x) ((((double) x) / ((double) ANG45/45)) * FRACUNIT) +#define FixedToAngle(x) ((((double) x) / FRACUNIT) * ANG45/45) +#define FIXED_TO_FLOAT(f) ((f)/(float)FRACUNIT) + +// functions. FParser::SF_ means Script Function not, well.. heh, me + +/////////// actually running a function ///////////// + +//========================================================================== +// +// The Doom actors in their original order +// +//========================================================================== + +static const char * const ActorNames_init[]= +{ + "DoomPlayer", + "ZombieMan", + "ShotgunGuy", + "Archvile", + "ArchvileFire", + "Revenant", + "RevenantTracer", + "RevenantTracerSmoke", + "Fatso", + "FatShot", + "ChaingunGuy", + "DoomImp", + "Demon", + "Spectre", + "Cacodemon", + "BaronOfHell", + "BaronBall", + "HellKnight", + "LostSoul", + "SpiderMastermind", + "Arachnotron", + "Cyberdemon", + "PainElemental", + "WolfensteinSS", + "CommanderKeen", + "BossBrain", + "BossEye", + "BossTarget", + "SpawnShot", + "SpawnFire", + "ExplosiveBarrel", + "DoomImpBall", + "CacodemonBall", + "Rocket", + "PlasmaBall", + "BFGBall", + "ArachnotronPlasma", + "BulletPuff", + "Blood", + "TeleportFog", + "ItemFog", + "TeleportDest", + "BFGExtra", + "GreenArmor", + "BlueArmor", + "HealthBonus", + "ArmorBonus", + "BlueCard", + "RedCard", + "YellowCard", + "YellowSkull", + "RedSkull", + "BlueSkull", + "Stimpack", + "Medikit", + "Soulsphere", + "InvulnerabilitySphere", + "Berserk", + "BlurSphere", + "RadSuit", + "Allmap", + "Infrared", + "Megasphere", + "Clip", + "ClipBox", + "RocketAmmo", + "RocketBox", + "Cell", + "CellBox", + "Shell", + "ShellBox", + "Backpack", + "BFG9000", + "Chaingun", + "Chainsaw", + "RocketLauncher", + "PlasmaRifle", + "Shotgun", + "SuperShotgun", + "TechLamp", + "TechLamp2", + "Column", + "TallGreenColumn", + "ShortGreenColumn", + "TallRedColumn", + "ShortRedColumn", + "SkullColumn", + "HeartColumn", + "EvilEye", + "FloatingSkull", + "TorchTree", + "BlueTorch", + "GreenTorch", + "RedTorch", + "ShortBlueTorch", + "ShortGreenTorch", + "ShortRedTorch", + "Slalagtite", + "TechPillar", + "CandleStick", + "Candelabra", + "BloodyTwitch", + "Meat2", + "Meat3", + "Meat4", + "Meat5", + "NonsolidMeat2", + "NonsolidMeat4", + "NonsolidMeat3", + "NonsolidMeat5", + "NonsolidTwitch", + "DeadCacodemon", + "DeadMarine", + "DeadZombieMan", + "DeadDemon", + "DeadLostSoul", + "DeadDoomImp", + "DeadShotgunGuy", + "GibbedMarine", + "GibbedMarineExtra", + "HeadsOnAStick", + "Gibs", + "HeadOnAStick", + "HeadCandles", + "DeadStick", + "LiveStick", + "BigTree", + "BurningBarrel", + "HangNoGuts", + "HangBNoBrain", + "HangTLookingDown", + "HangTSkull", + "HangTLookingUp", + "HangTNoBrain", + "ColonGibs", + "SmallBloodPool", + "BrainStem", + "PointPusher", + "PointPuller", +}; + +static const PClass * ActorTypes[countof(ActorNames_init)]; + +//========================================================================== +// +// Some functions that take care of the major differences between the class +// based actor system from ZDoom and Doom's index based one +// +//========================================================================== + +//========================================================================== +// +// Gets an actor class +// Input can be either a class name, an actor variable or a Doom index +// Doom index is only supported for the original things up to MBF +// +//========================================================================== +const PClass * T_GetMobjType(svalue_t arg) +{ + const PClass * PClass=NULL; + + if (arg.type==svt_string) + { + PClass=PClass::FindClass(arg.string); + + // invalid object to spawn + if(!PClass) script_error("unknown object type: %s\n", arg.string.GetChars()); + } + else if (arg.type==svt_mobj) + { + AActor * mo = actorvalue(arg); + if (mo) PClass = mo->GetClass(); + } + else + { + int objtype = intvalue(arg); + if (objtype>=0 && objtypeplayer) + { + // I prefer this not to make an error. + // This way a player function used for a non-player + // object will just do nothing + //script_error("mobj not a player!\n"); + return -1; + } + playernum = int(arg.value.mobj->player - players); + } + else + playernum = intvalue(arg); + + if(playernum < 0 || playernum > MAXPLAYERS) + { + return -1; + } + if(!playeringame[playernum]) // no error, just return -1 + { + return -1; + } + return playernum; +} + +//========================================================================== +// +// Finds a sector from a tag. This has been extended to allow looking for +// sectors directly by passing a negative value +// +//========================================================================== +int T_FindSectorFromTag(int tagnum,int startsector) +{ + if (tagnum<=0) + { + if (startsector<0) + { + if (tagnum==-32768) return 0; + if (-tagnum= 4) + { + script_error("ammo number out of range: %i", ammonum); + return NULL; + } + p=DefAmmo[ammonum]; + } + const PClass * am=PClass::FindClass(p); + if (!am->IsDescendantOf(RUNTIME_CLASS(AAmmo))) + { + script_error("unknown ammo type : %s", p); + return NULL; + } + return am; + +} + +//========================================================================== +// +// Finds a sound in the sound table and adds a new entry if it isn't defined +// It's too bad that this is necessary but FS doesn't know about this kind +// of sound management. +// +//========================================================================== +static FSoundID T_FindSound(const char * name) +{ + char buffer[40]; + FSoundID so=S_FindSound(name); + + if (so>0) return so; + + // Now it gets dirty! + + if (gameinfo.gametype & GAME_DoomStrifeChex) + { + mysnprintf(buffer, countof(buffer), "DS%.35s", name); + if (Wads.CheckNumForName(buffer, ns_sounds)<0) strcpy(buffer, name); + } + else + { + strcpy(buffer, name); + if (Wads.CheckNumForName(buffer, ns_sounds)<0) mysnprintf(buffer, countof(buffer), "DS%.35s", name); + } + + int id = S_AddSound(name, buffer); + S_HashSounds(); + return FSoundID(id); +} + + +//========================================================================== +// +// Creates a string out of a print argument list. This version does not +// have any length restrictions like the original FS versions had. +// +//========================================================================== +FString FParser::GetFormatString(int startarg) +{ + FString fmt=""; + for(int i=startarg; iMakeIndex(Rover); + + for(n=0; nsections[n]; + + // check all the sections in this hashchain + while(current) + { + // a loop? + + if(current->type == st_loop) + // check to see if it's a loop that we're inside + if(rover_index >= current->start_index && rover_index <= current->end_index) + { + // a higher nesting level than the best one so far? + if(!best || (current->start_index > best->start_index)) + best = current; // save it + } + current = current->next; + } + } + + return best; // return the best one found +} + +//========================================================================== +// +// "continue;" in FraggleScript is a function +// +//========================================================================== + +void FParser::SF_Continue(void) +{ + DFsSection *section; + + if(!(section = looping_section()) ) // no loop found + { + script_error("continue() not in loop\n"); + return; + } + + Rover = Script->SectionEnd(section); // jump to the closing brace +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Break(void) +{ + DFsSection *section; + + if(!(section = looping_section()) ) + { + script_error("break() not in loop\n"); + return; + } + + Rover = Script->SectionEnd(section) + 1; // jump out of the loop +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Goto(void) +{ + if (CheckArgs(1)) + { + // check argument is a labelptr + + if(t_argv[0].type != svt_label) + { + script_error("goto argument not a label\n"); + return; + } + + // go there then if everythings fine + Rover = Script->LabelValue(t_argv[0]); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Return(void) +{ + throw CFsTerminator(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Include(void) +{ + char tempstr[12]; + + if (CheckArgs(1)) + { + if(t_argv[0].type == svt_string) + { + strncpy(tempstr, t_argv[0].string, 8); + tempstr[8]=0; + } + else + mysnprintf(tempstr, countof(tempstr), "%i", (int)t_argv[0].value.i); + + Script->ParseInclude(tempstr); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Input(void) +{ + Printf(PRINT_BOLD,"input() function not available in doom\n"); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Beep(void) +{ + S_Sound(CHAN_AUTO, "misc/chat", 1.0f, ATTN_IDLE); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Clock(void) +{ + t_return.type = svt_int; + t_return.value.i = (gametic*100)/TICRATE; +} + +/**************** doom stuff ****************/ + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ExitLevel(void) +{ + G_ExitLevel(0, false); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Tip(void) +{ + if (t_argc>0 && Script->trigger && + Script->trigger->CheckLocalView(consoleplayer)) + { + C_MidPrint(SmallFont, GetFormatString(0).GetChars()); + } +} + +//========================================================================== +// +// FParser::SF_TimedTip +// +// Implements: void timedtip(int clocks, ...) +// +//========================================================================== + +EXTERN_CVAR(Float, con_midtime) + +void FParser::SF_TimedTip(void) +{ + if (CheckArgs(2)) + { + float saved = con_midtime; + con_midtime = intvalue(t_argv[0])/100.0f; + C_MidPrint(SmallFont, GetFormatString(1).GetChars()); + con_midtime=saved; + } +} + + +//========================================================================== +// +// tip to a particular player +// +//========================================================================== + +void FParser::SF_PlayerTip(void) +{ + if (CheckArgs(1)) + { + int plnum = T_GetPlayerNum(t_argv[0]); + if (plnum!=-1 && players[plnum].mo->CheckLocalView(consoleplayer)) + { + C_MidPrint(SmallFont, GetFormatString(1).GetChars()); + } + } +} + +//========================================================================== +// +// message player +// +//========================================================================== + +void FParser::SF_Message(void) +{ + if (t_argc>0 && Script->trigger && + Script->trigger->CheckLocalView(consoleplayer)) + { + Printf(PRINT_HIGH, "%s\n", GetFormatString(0).GetChars()); + } +} + +//========================================================================== +// +// message to a particular player +// +//========================================================================== + +void FParser::SF_PlayerMsg(void) +{ + if (CheckArgs(1)) + { + int plnum = T_GetPlayerNum(t_argv[0]); + if (plnum!=-1 && players[plnum].mo->CheckLocalView(consoleplayer)) + { + Printf(PRINT_HIGH, "%s\n", GetFormatString(1).GetChars()); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_PlayerInGame(void) +{ + if (CheckArgs(1)) + { + int plnum = T_GetPlayerNum(t_argv[0]); + + if (plnum!=-1) + { + t_return.type = svt_int; + t_return.value.i = playeringame[plnum]; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_PlayerName(void) +{ + int plnum; + + if(!t_argc) + { + player_t *pl=NULL; + if (Script->trigger) pl = Script->trigger->player; + if(pl) plnum = int(pl - players); + else plnum=-1; + } + else + plnum = T_GetPlayerNum(t_argv[0]); + + if(plnum !=-1) + { + t_return.type = svt_string; + t_return.string = players[plnum].userinfo.netname; + } + else + { + script_error("script not started by player\n"); + } +} + +//========================================================================== +// +// object being controlled by player +// +//========================================================================== + +void FParser::SF_PlayerObj(void) +{ + int plnum; + + if(!t_argc) + { + player_t *pl=NULL; + if (Script->trigger) pl = Script->trigger->player; + if(pl) plnum = int(pl - players); + else plnum=-1; + } + else + plnum = T_GetPlayerNum(t_argv[0]); + + if(plnum !=-1) + { + t_return.type = svt_mobj; + t_return.value.mobj = players[plnum].mo; + } + else + { + script_error("script not started by player\n"); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Player(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_int; + + if(mo && mo->player) // haleyjd: added mo->player + { + t_return.value.i = (int)(mo->player - players); + } + else + { + t_return.value.i = -1; + } +} + +//========================================================================== +// +// FParser::SF_Spawn +// +// Implements: mobj spawn(int type, int x, int y, [int angle], [int z], [bool zrel]) +// +//========================================================================== + +void FParser::SF_Spawn(void) +{ + int x, y, z; + const PClass *PClass; + angle_t angle = 0; + + if (CheckArgs(3)) + { + if (!(PClass=T_GetMobjType(t_argv[0]))) return; + + x = fixedvalue(t_argv[1]); + y = fixedvalue(t_argv[2]); + + if(t_argc >= 5) + { + z = fixedvalue(t_argv[4]); + // [Graf Zahl] added option of spawning with a relative z coordinate + if(t_argc > 5) + { + if (intvalue(t_argv[5])) z+=P_PointInSector(x, y)->floorplane.ZatPoint(x,y); + } + } + else + { + // Legacy compatibility is more important than correctness. + z = ONFLOORZ;// (GetDefaultByType(PClass)->flags & MF_SPAWNCEILING) ? ONCEILINGZ : ONFLOORZ; + } + + if(t_argc >= 4) + { + angle = intvalue(t_argv[3]) * (SQWORD)ANG45 / 45; + } + + t_return.type = svt_mobj; + t_return.value.mobj = Spawn(PClass, x, y, z, ALLOW_REPLACE); + + if (t_return.value.mobj) + { + t_return.value.mobj->angle = angle; + + if (!DFraggleThinker::ActiveThinker->nocheckposition) + { + if (!P_TestMobjLocation(t_return.value.mobj)) + { + if (t_return.value.mobj->flags&MF_COUNTKILL) level.total_monsters--; + if (t_return.value.mobj->flags&MF_COUNTITEM) level.total_items--; + t_return.value.mobj->Destroy(); + t_return.value.mobj = NULL; + } + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_RemoveObj(void) +{ + if (CheckArgs(1)) + { + AActor * mo = actorvalue(t_argv[0]); + if(mo) // nullptr check + { + mo->ClearCounters(); + mo->Destroy(); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_KillObj(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; // default to trigger object + } + + if(mo) + { + // ensure the thing can be killed + mo->flags|=MF_SHOOTABLE; + mo->flags2&=~(MF2_INVULNERABLE|MF2_DORMANT); + // [GrafZahl] This called P_KillMobj directly + // which is a very bad thing to do! + P_DamageMobj(mo, NULL, NULL, mo->health, NAME_Massacre); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjX(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_fixed; // haleyjd: SoM's fixed-point fix + t_return.value.f = mo ? mo->x : 0; // null ptr check +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjY(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_fixed; // haleyjd + t_return.value.f = mo ? mo->y : 0; // null ptr check +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjZ(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_fixed; // haleyjd + t_return.value.f = mo ? mo->z : 0; // null ptr check +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjAngle(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_fixed; // haleyjd: fixed-point -- SoM again :) + t_return.value.f = mo ? (fixed_t)AngleToFixed(mo->angle) : 0; // null ptr check +} + + +//========================================================================== +// +// +// +//========================================================================== + +// teleport: object, sector_tag +void FParser::SF_Teleport(void) +{ + int tag; + AActor *mo; + + if (CheckArgs(1)) + { + if(t_argc == 1) // 1 argument: sector tag + { + mo = Script->trigger; // default to trigger + tag = intvalue(t_argv[0]); + } + else // 2 or more + { // teleport a given object + mo = actorvalue(t_argv[0]); + tag = intvalue(t_argv[1]); + } + + if(mo) + EV_Teleport(0, tag, NULL, 0, mo, true, true, false); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_SilentTeleport(void) +{ + int tag; + AActor *mo; + + if (CheckArgs(1)) + { + if(t_argc == 1) // 1 argument: sector tag + { + mo = Script->trigger; // default to trigger + tag = intvalue(t_argv[0]); + } + else // 2 or more + { // teleport a given object + mo = actorvalue(t_argv[0]); + tag = intvalue(t_argv[1]); + } + + if(mo) + EV_Teleport(0, tag, NULL, 0, mo, false, false, true); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_DamageObj(void) +{ + AActor *mo; + int damageamount; + + if (CheckArgs(1)) + { + if(t_argc == 1) // 1 argument: damage trigger by amount + { + mo = Script->trigger; // default to trigger + damageamount = intvalue(t_argv[0]); + } + else // 2 or more + { // damage a given object + mo = actorvalue(t_argv[0]); + damageamount = intvalue(t_argv[1]); + } + + if(mo) + P_DamageMobj(mo, NULL, Script->trigger, damageamount, NAME_None); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +// the tag number of the sector the thing is in +void FParser::SF_ObjSector(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_int; + t_return.value.i = mo ? mo->Sector->tag : 0; // nullptr check +} + +//========================================================================== +// +// +// +//========================================================================== + +// the health number of an object +void FParser::SF_ObjHealth(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_int; + t_return.value.i = mo ? mo->health : 0; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjFlag(void) +{ + AActor *mo; + int flagnum; + + if (CheckArgs(1)) + { + if(t_argc == 1) // use trigger, 1st is flag + { + // use trigger: + mo = Script->trigger; + flagnum = intvalue(t_argv[0]); + } + else if(t_argc == 2) // specified object + { + mo = actorvalue(t_argv[0]); + flagnum = intvalue(t_argv[1]); + } + else // >= 3 : SET flags + { + mo = actorvalue(t_argv[0]); + flagnum = intvalue(t_argv[1]); + + if(mo && flagnum<26) // nullptr check + { + // remove old bit + mo->flags &= ~(1 << flagnum); + + // make the new flag + mo->flags |= (!!intvalue(t_argv[2])) << flagnum; + } + } + t_return.type = svt_int; + if (mo && flagnum<26) + { + t_return.value.i = !!(mo->flags & (1 << flagnum)); + } + else t_return.value.i = 0; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +// apply momentum to a thing +void FParser::SF_PushThing(void) +{ + if (CheckArgs(3)) + { + AActor * mo = actorvalue(t_argv[0]); + if(!mo) return; + + angle_t angle = (angle_t)FixedToAngle(fixedvalue(t_argv[1])); + fixed_t force = fixedvalue(t_argv[2]); + + P_ThrustMobj(mo, angle, force); + } +} + +//========================================================================== +// +// FParser::SF_ReactionTime -- useful for freezing things +// +//========================================================================== + + +void FParser::SF_ReactionTime(void) +{ + if (CheckArgs(1)) + { + AActor *mo = actorvalue(t_argv[0]); + + if(t_argc > 1) + { + if(mo) mo->reactiontime = (intvalue(t_argv[1]) * TICRATE) / 100; + } + + t_return.type = svt_int; + t_return.value.i = mo ? mo->reactiontime : 0; + } +} + +//========================================================================== +// +// FParser::SF_MobjTarget -- sets a thing's target field +// +//========================================================================== + +// Sets a mobj's Target! >:) +void FParser::SF_MobjTarget(void) +{ + AActor* mo; + AActor* target; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + if(t_argc > 1) + { + target = actorvalue(t_argv[1]); + if(mo && target && mo->SeeState) // haleyjd: added target check -- no NULL allowed + { + mo->target=target; + mo->SetState(mo->SeeState); + mo->flags|=MF_JUSTHIT; + } + } + + t_return.type = svt_mobj; + t_return.value.mobj = mo ? mo->target : NULL; + } +} + +//========================================================================== +// +// FParser::SF_MobjMomx, MobjMomy, MobjMomz -- momentum functions +// +//========================================================================== + +void FParser::SF_MobjMomx(void) +{ + AActor* mo; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + if(t_argc > 1) + { + if(mo) + mo->velx = fixedvalue(t_argv[1]); + } + + t_return.type = svt_fixed; + t_return.value.f = mo ? mo->velx : 0; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_MobjMomy(void) +{ + AActor* mo; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + if(t_argc > 1) + { + if(mo) + mo->vely = fixedvalue(t_argv[1]); + } + + t_return.type = svt_fixed; + t_return.value.f = mo ? mo->vely : 0; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_MobjMomz(void) +{ + AActor* mo; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + if(t_argc > 1) + { + if(mo) + mo->velz = fixedvalue(t_argv[1]); + } + + t_return.type = svt_fixed; + t_return.value.f = mo ? mo->velz : 0; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +/****************** Trig *********************/ + +void FParser::SF_PointToAngle(void) +{ + if (CheckArgs(4)) + { + fixed_t x1 = fixedvalue(t_argv[0]); + fixed_t y1 = fixedvalue(t_argv[1]); + fixed_t x2 = fixedvalue(t_argv[2]); + fixed_t y2 = fixedvalue(t_argv[3]); + + angle_t angle = R_PointToAngle2(x1, y1, x2, y2); + + t_return.type = svt_fixed; + t_return.value.f = (fixed_t)AngleToFixed(angle); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_PointToDist(void) +{ + if (CheckArgs(4)) + { + // Doing this in floating point is actually faster with modern computers! + float x = floatvalue(t_argv[2]) - floatvalue(t_argv[0]); + float y = floatvalue(t_argv[3]) - floatvalue(t_argv[1]); + + t_return.type = svt_fixed; + t_return.value.f = (fixed_t)(sqrtf(x*x+y*y)*65536.f); + } +} + + +//========================================================================== +// +// setcamera(obj, [angle], [height], [pitch]) +// +// [GrafZahl] This is a complete rewrite. +// Although both Eternity and Legacy implement this function +// they are mutually incompatible with each other and with ZDoom... +// +//========================================================================== + +void FParser::SF_SetCamera(void) +{ + angle_t angle; + player_t * player; + AActor * newcamera; + + if (CheckArgs(1)) + { + player=Script->trigger->player; + if (!player) player=&players[0]; + + newcamera = actorvalue(t_argv[0]); + if(!newcamera) + { + script_error("invalid location object for camera\n"); + return; // nullptr check + } + + angle = t_argc < 2 ? newcamera->angle : (fixed_t)FixedToAngle(fixedvalue(t_argv[1])); + + newcamera->special1=newcamera->angle; + newcamera->special2=newcamera->z; + newcamera->z = t_argc < 3 ? (newcamera->z + (41 << FRACBITS)) : (intvalue(t_argv[2]) << FRACBITS); + newcamera->angle = angle; + if(t_argc < 4) newcamera->pitch = 0; + else + { + fixed_t pitch = fixedvalue(t_argv[3]); + if(pitch < -50*FRACUNIT) pitch = -50*FRACUNIT; + if(pitch > 50*FRACUNIT) pitch = 50*FRACUNIT; + newcamera->pitch=(angle_t)((pitch/65536.0f)*(ANGLE_45/45.0f)*(20.0f/32.0f)); + } + player->camera=newcamera; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ClearCamera(void) +{ + player_t * player; + player=Script->trigger->player; + if (!player) player=&players[0]; + + AActor * cam=player->camera; + if (cam) + { + player->camera=player->mo; + cam->angle=cam->special1; + cam->z=cam->special2; + } + +} + + + +/*********** sounds ******************/ + +//========================================================================== +// +// +// +//========================================================================== + +// start sound from thing +void FParser::SF_StartSound(void) +{ + AActor *mo; + + if (CheckArgs(2)) + { + mo = actorvalue(t_argv[0]); + + if (mo) + { + S_Sound(mo, CHAN_BODY, T_FindSound(stringvalue(t_argv[1])), 1, ATTN_NORM); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +// start sound from sector +void FParser::SF_StartSectorSound(void) +{ + sector_t *sector; + int tagnum; + + if (CheckArgs(2)) + { + tagnum = intvalue(t_argv[0]); + + int i=-1; + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sector = §ors[i]; + S_Sound(sector, CHAN_BODY, T_FindSound(stringvalue(t_argv[1])), 1.0f, ATTN_NORM); + } + } +} + +/************* Sector functions ***************/ + +//DMover::EResult P_MoveFloor (sector_t * m_Sector, fixed_t speed, fixed_t dest, int crush, int direction, int flags=0); +//DMover::EResult P_MoveCeiling (sector_t * m_Sector, fixed_t speed, fixed_t dest, int crush, int direction, int flags=0); + +class DFloorChanger : public DFloor +{ +public: + DFloorChanger(sector_t * sec) + : DFloor(sec) {} + + bool Move(fixed_t speed, fixed_t dest, int crush, int direction) + { + bool res = DMover::crushed != MoveFloor(speed, dest, crush, direction, false); + Destroy(); + m_Sector->floordata=NULL; + StopInterpolation(); + m_Sector=NULL; + return res; + } +}; + + +//========================================================================== +// +// +// +//========================================================================== + +// floor height of sector +void FParser::SF_FloorHeight(void) +{ + int tagnum; + int secnum; + fixed_t dest; + int returnval = 1; // haleyjd: SoM's fixes + + if (CheckArgs(1)) + { + tagnum = intvalue(t_argv[0]); + + if(t_argc > 1) // > 1: set floor height + { + int i; + int crush = (t_argc >= 3) ? intvalue(t_argv[2]) : false; + + i = -1; + dest = fixedvalue(t_argv[1]); + + // set all sectors with tag + + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + if (sectors[i].floordata) continue; // don't move floors that are active! + + DFloorChanger * f = new DFloorChanger(§ors[i]); + if (!f->Move( + abs(dest - sectors[i].CenterFloor()), + sectors[i].floorplane.PointToDist (CenterSpot(§ors[i]), dest), + crush? 10:-1, + (dest > sectors[i].CenterFloor()) ? 1 : -1)) + { + returnval = 0; + } + } + } + else + { + secnum = T_FindSectorFromTag(tagnum, -1); + if(secnum < 0) + { + script_error("sector not found with tagnum %i\n", tagnum); + return; + } + returnval = sectors[secnum].CenterFloor() >> FRACBITS; + } + + // return floor height + + t_return.type = svt_int; + t_return.value.i = returnval; + } +} + + +//============================================================================= +// +// +//============================================================================= +class DMoveFloor : public DFloor +{ +public: + DMoveFloor(sector_t * sec,int moveheight,int _m_Direction,int crush,int movespeed) + : DFloor(sec) + { + m_Type = floorRaiseByValue; + m_Crush = crush; + m_Speed=movespeed; + m_Direction = _m_Direction; + m_FloorDestDist = moveheight; + StartFloorSound(); + } +}; + + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_MoveFloor(void) +{ + int secnum = -1; + sector_t *sec; + int tagnum, platspeed = 1, destheight, crush; + + if (CheckArgs(2)) + { + tagnum = intvalue(t_argv[0]); + destheight = intvalue(t_argv[1]) * FRACUNIT; + platspeed = t_argc > 2 ? fixedvalue(t_argv[2]) : FRACUNIT; + crush = (t_argc > 3 ? intvalue(t_argv[3]) : -1); + + // move all sectors with tag + + while ((secnum = T_FindSectorFromTag(tagnum, secnum)) >= 0) + { + sec = §ors[secnum]; + // Don't start a second thinker on the same floor + if (sec->floordata) continue; + + new DMoveFloor(sec,sec->floorplane.PointToDist(CenterSpot(sec),destheight), + destheight < sec->CenterFloor() ? -1:1,crush,platspeed); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +class DCeilingChanger : public DCeiling +{ +public: + DCeilingChanger(sector_t * sec) + : DCeiling(sec) {} + + bool Move(fixed_t speed, fixed_t dest, int crush, int direction) + { + bool res = DMover::crushed != MoveCeiling(speed, dest, crush, direction, false); + Destroy(); + m_Sector->ceilingdata=NULL; + StopInterpolation (); + m_Sector=NULL; + return res; + } +}; + +//========================================================================== +// +// +// +//========================================================================== + +// ceiling height of sector +void FParser::SF_CeilingHeight(void) +{ + fixed_t dest; + int secnum; + int tagnum; + int returnval = 1; + + if (CheckArgs(1)) + { + tagnum = intvalue(t_argv[0]); + + if(t_argc > 1) // > 1: set ceilheight + { + int i; + int crush = (t_argc >= 3) ? intvalue(t_argv[2]) : false; + + i = -1; + dest = fixedvalue(t_argv[1]); + + // set all sectors with tag + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + if (sectors[i].ceilingdata) continue; // don't move ceilings that are active! + + DCeilingChanger * c = new DCeilingChanger(§ors[i]); + if (!c->Move( + abs(dest - sectors[i].CenterCeiling()), + sectors[i].ceilingplane.PointToDist (CenterSpot(§ors[i]), dest), + crush? 10:-1, + (dest > sectors[i].CenterCeiling()) ? 1 : -1)) + { + returnval = 0; + } + } + } + else + { + secnum = T_FindSectorFromTag(tagnum, -1); + if(secnum < 0) + { + script_error("sector not found with tagnum %i\n", tagnum); + return; + } + returnval = sectors[secnum].CenterCeiling() >> FRACBITS; + } + + // return ceiling height + t_return.type = svt_int; + t_return.value.i = returnval; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +class DMoveCeiling : public DCeiling +{ +public: + + DMoveCeiling(sector_t * sec,int tag,fixed_t destheight,fixed_t speed,int silent,int crush) + : DCeiling(sec) + { + m_Crush = crush; + m_Speed2 = m_Speed = m_Speed1 = speed; + m_Silent = silent; + m_Type = DCeiling::ceilLowerByValue; // doesn't really matter as long as it's no special value + m_Tag=tag; + vertex_t * spot=CenterSpot(sec); + m_TopHeight=m_BottomHeight=sec->ceilingplane.PointToDist(spot,destheight); + m_Direction=destheight>sec->GetPlaneTexZ(sector_t::ceiling)? 1:-1; + + // Do not interpolate instant movement ceilings. + fixed_t movedist = abs(sec->ceilingplane.d - m_BottomHeight); + if (m_Speed >= movedist) + { + StopInterpolation (); + m_Silent=2; + } + PlayCeilingSound(); + } +}; + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_MoveCeiling(void) +{ + int secnum = -1; + sector_t *sec; + int tagnum, platspeed = 1, destheight; + int crush; + int silent; + + if (CheckArgs(2)) + { + tagnum = intvalue(t_argv[0]); + destheight = intvalue(t_argv[1]) * FRACUNIT; + platspeed = /*FLOORSPEED **/ (t_argc > 2 ? fixedvalue(t_argv[2]) : FRACUNIT); + crush=t_argc>3 ? intvalue(t_argv[3]):-1; + silent=t_argc>4 ? intvalue(t_argv[4]):1; + + // move all sectors with tag + while ((secnum = T_FindSectorFromTag(tagnum, secnum)) >= 0) + { + sec = §ors[secnum]; + + // Don't start a second thinker on the same floor + if (sec->ceilingdata) continue; + new DMoveCeiling(sec, tagnum, destheight, platspeed, silent, crush); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_LightLevel(void) +{ + sector_t *sector; + int secnum; + int tagnum; + + if (CheckArgs(1)) + { + tagnum = intvalue(t_argv[0]); + + // argv is sector tag + secnum = T_FindSectorFromTag(tagnum, -1); + + if(secnum < 0) + { + return; + } + + sector = §ors[secnum]; + + if(t_argc > 1) // > 1: set light level + { + int i = -1; + + // set all sectors with tag + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sectors[i].SetLightLevel(intvalue(t_argv[1])); + } + } + + // return lightlevel + t_return.type = svt_int; + t_return.value.i = sector->lightlevel; + } +} + + + +//========================================================================== +// +// Simple light fade - locks lightingdata. For FParser::SF_FadeLight +// +//========================================================================== +class DLightLevel : public DLighting +{ + DECLARE_CLASS (DLightLevel, DLighting) + + unsigned char destlevel; + unsigned char speed; + + DLightLevel() {} + +public: + + DLightLevel(sector_t * s,int destlevel,int speed); + void Serialize (FArchive &arc); + void Tick (); + void Destroy() { Super::Destroy(); m_Sector->lightingdata=NULL; } +}; + + + +IMPLEMENT_CLASS (DLightLevel) + +void DLightLevel::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + arc << destlevel << speed; + if (arc.IsLoading()) m_Sector->lightingdata=this; +} + + +//========================================================================== +// sf 13/10/99: +// +// T_LightFade() +// +// Just fade the light level in a sector to a new level +// +//========================================================================== + +void DLightLevel::Tick() +{ + Super::Tick(); + if(m_Sector->lightlevel < destlevel) + { + // increase the lightlevel + if(m_Sector->lightlevel + speed >= destlevel) + { + // stop changing light level + m_Sector->SetLightLevel(destlevel); // set to dest lightlevel + Destroy(); + } + else + { + m_Sector->ChangeLightLevel(speed); + } + } + else + { + // decrease lightlevel + if(m_Sector->lightlevel - speed <= destlevel) + { + // stop changing light level + m_Sector->SetLightLevel(destlevel); // set to dest lightlevel + Destroy(); + } + else + { + m_Sector->ChangeLightLevel(-speed); + } + } +} + +//========================================================================== +// +//========================================================================== +DLightLevel::DLightLevel(sector_t * s,int _destlevel,int _speed) : DLighting(s) +{ + destlevel=_destlevel; + speed=_speed; + s->lightingdata=this; +} + +//========================================================================== +// sf 13/10/99: +// +// P_FadeLight() +// +// Fade all the lights in sectors with a particular tag to a new value +// +//========================================================================== +void FParser::SF_FadeLight(void) +{ + int sectag, destlevel, speed = 1; + int i; + + if (CheckArgs(2)) + { + sectag = intvalue(t_argv[0]); + destlevel = intvalue(t_argv[1]); + speed = t_argc>2 ? intvalue(t_argv[2]) : 1; + + for (i = -1; (i = P_FindSectorFromTag(sectag,i)) >= 0;) + { + if (!sectors[i].lightingdata) new DLightLevel(§ors[i],destlevel,speed); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== +void FParser::SF_FloorTexture(void) +{ + int tagnum, secnum; + sector_t *sector; + + if (CheckArgs(1)) + { + tagnum = intvalue(t_argv[0]); + + // argv is sector tag + secnum = T_FindSectorFromTag(tagnum, -1); + + if(secnum < 0) + { script_error("sector not found with tagnum %i\n", tagnum); return;} + + sector = §ors[secnum]; + + if(t_argc > 1) + { + int i = -1; + FTextureID picnum = TexMan.GetTexture(t_argv[1].string, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); + + // set all sectors with tag + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sectors[i].SetTexture(sector_t::floor, picnum); + } + } + + t_return.type = svt_string; + FTexture * tex = TexMan[sector->GetTexture(sector_t::floor)]; + t_return.string = tex? tex->Name : ""; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_SectorColormap(void) +{ + // This doesn't work properly and it never will. + // Whatever was done here originally, it is incompatible + // with Boom and ZDoom and doesn't work properly in Legacy either. + + // Making it no-op is probably the best thing one can do in this case. + + /* + int tagnum, secnum; + sector_t *sector; + int c=2; + int i = -1; + + if(t_argc<2) + { script_error("insufficient arguments to function\n"); return; } + + tagnum = intvalue(t_argv[0]); + + // argv is sector tag + secnum = T_FindSectorFromTag(tagnum, -1); + + if(secnum < 0) + { script_error("sector not found with tagnum %i\n", tagnum); return;} + + sector = §ors[secnum]; + + if (t_argv[1].type==svt_string) + { + DWORD cm = R_ColormapNumForName(t_argv[1].value.s); + + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sectors[i].midmap=cm; + sectors[i].heightsec=§ors[i]; + } + } + */ +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_CeilingTexture(void) +{ + int tagnum, secnum; + sector_t *sector; + + if (CheckArgs(1)) + { + tagnum = intvalue(t_argv[0]); + + // argv is sector tag + secnum = T_FindSectorFromTag(tagnum, -1); + + if(secnum < 0) + { script_error("sector not found with tagnum %i\n", tagnum); return;} + + sector = §ors[secnum]; + + if(t_argc > 1) + { + int i = -1; + FTextureID picnum = TexMan.GetTexture(t_argv[1].string, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); + + // set all sectors with tag + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sectors[i].SetTexture(sector_t::ceiling, picnum); + } + } + + t_return.type = svt_string; + FTexture * tex = TexMan[sector->GetTexture(sector_t::ceiling)]; + t_return.string = tex? tex->Name : ""; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ChangeHubLevel(void) +{ + I_Error("FS hub system permanently disabled\n"); +} + +// for start map: start new game on a particular skill +void FParser::SF_StartSkill(void) +{ + I_Error("startskill is not supported by this implementation!\n"); +} + +//========================================================================== +// +// Doors +// +// opendoor(sectag, [delay], [speed]) +// +//========================================================================== + +void FParser::SF_OpenDoor(void) +{ + int speed, wait_time; + int sectag; + + if (CheckArgs(1)) + { + // got sector tag + sectag = intvalue(t_argv[0]); + if (sectag==0) return; // tag 0 not allowed + + // door wait time + if(t_argc > 1) wait_time = (intvalue(t_argv[1]) * TICRATE) / 100; + else wait_time = 0; // 0= stay open + + // door speed + if(t_argc > 2) speed = intvalue(t_argv[2]); + else speed = 1; // 1= normal speed + + EV_DoDoor(wait_time? DDoor::doorRaise:DDoor::doorOpen,NULL,NULL,sectag,2*FRACUNIT*clamp(speed,1,127),wait_time,0,0); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_CloseDoor(void) +{ + int speed; + int sectag; + + if (CheckArgs(1)) + { + // got sector tag + sectag = intvalue(t_argv[0]); + if (sectag==0) return; // tag 0 not allowed + + // door speed + if(t_argc > 1) speed = intvalue(t_argv[1]); + else speed = 1; // 1= normal speed + + EV_DoDoor(DDoor::doorClose,NULL,NULL,sectag,2*FRACUNIT*clamp(speed,1,127),0,0,0); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +// run console cmd +void FParser::SF_RunCommand(void) +{ + FS_EmulateCmd(GetFormatString(0).LockBuffer()); +} + +//========================================================================== +// +// +// +//========================================================================== + +// any linedef type +extern void P_TranslateLineDef (line_t *ld, maplinedef_t *mld); + +void FParser::SF_LineTrigger() +{ + if (CheckArgs(1)) + { + line_t line; + maplinedef_t mld; + mld.special=intvalue(t_argv[0]); + mld.tag=t_argc > 1 ? intvalue(t_argv[1]) : 0; + P_TranslateLineDef(&line, &mld); + LineSpecials[line.special](NULL, Script->trigger, false, + line.args[0],line.args[1],line.args[2],line.args[3],line.args[4]); + } +} + +//========================================================================== +// +// +// +//========================================================================== +bool FS_ChangeMusic(const char * string) +{ + char buffer[40]; + + if (Wads.CheckNumForName(string, ns_music)<0 || !S_ChangeMusic(string,true)) + { + // Retry with O_ prepended to the music name, then with D_ + mysnprintf(buffer, countof(buffer), "O_%s", string); + if (Wads.CheckNumForName(buffer, ns_music)<0 || !S_ChangeMusic(buffer,true)) + { + mysnprintf(buffer, countof(buffer), "D_%s", string); + if (Wads.CheckNumForName(buffer, ns_music)<0) + { + S_ChangeMusic(NULL, 0); + return false; + } + else S_ChangeMusic(buffer,true); + } + } + return true; +} + +void FParser::SF_ChangeMusic(void) +{ + if (CheckArgs(1)) + { + FS_ChangeMusic(stringvalue(t_argv[0])); + } +} + + +//========================================================================== +// +// FParser::SF_SetLineBlocking() +// +// Sets a line blocking or unblocking +// +//========================================================================== +void FParser::SF_SetLineBlocking(void) +{ + static unsigned short blocks[]={0,ML_BLOCKING,ML_BLOCKEVERYTHING}; + + if (CheckArgs(2)) + { + int blocking=intvalue(t_argv[1]); + if (blocking>=0 && blocking<=2) + { + blocking=blocks[blocking]; + int tag=intvalue(t_argv[0]); + for (int i = -1; (i = P_FindLineFromID(tag, i)) >= 0;) + { + lines[i].flags = (lines[i].flags & ~(ML_BLOCKING|ML_BLOCKEVERYTHING)) | blocking; + } + } + } +} + +//========================================================================== +// +// similar, but monster blocking +// +//========================================================================== + +void FParser::SF_SetLineMonsterBlocking(void) +{ + if (CheckArgs(2)) + { + int blocking = intvalue(t_argv[1]) ? ML_BLOCKMONSTERS : 0; + int tag=intvalue(t_argv[0]); + + for (int i = -1; (i = P_FindLineFromID(tag, i)) >= 0;) + { + lines[i].flags = (lines[i].flags & ~ML_BLOCKMONSTERS) | blocking; + } + } +} + + + +//========================================================================== +// +//FParser::SF_SetLineTexture +// +// #2 in a not-so-long line of ACS-inspired functions +// This one is *much* needed, IMO +// +// Eternity: setlinetexture(tag, side, position, texture) +// Legacy: setlinetexture(tag, texture, side, sections) +// +//========================================================================== + +void FParser::SF_SetLineTexture(void) +{ + int tag; + int side; + int position; + const char *texture; + FTextureID texturenum; + int i; + + if (CheckArgs(4)) + { + tag = intvalue(t_argv[0]); + + // the eternity version + if (t_argv[3].type==svt_string) + { + side = intvalue(t_argv[1]); + if(side < 0 || side > 1) + { + script_error("invalid side number for texture change\n"); + return; + } + + position = intvalue(t_argv[2]); + if(position < 1 || position > 3) + { + script_error("invalid position for texture change\n"); + return; + } + position=3-position; + + texture = stringvalue(t_argv[3]); + texturenum = TexMan.GetTexture(texture, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); + + for (i = -1; (i = P_FindLineFromID(tag, i)) >= 0;) + { + // bad sidedef, Hexen just SEGV'd here! + if(lines[i].sidedef[side] != NULL) + { + if (position >=0 && position <=2) + { + lines[i].sidedef[side]->SetTexture(position, texturenum); + } + } + } + } + else // and an improved legacy version + { + FTextureID picnum = TexMan.GetTexture(t_argv[1].string, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); + side = !!intvalue(t_argv[2]); + int sections = intvalue(t_argv[3]); + + // set all sectors with tag + for (i = -1; (i = P_FindLineFromID(tag, i)) >= 0;) + { + side_t *sided = lines[i].sidedef[side]; + if(sided != NULL) + { + if(sections & 1) sided->SetTexture(side_t::top, picnum); + if(sections & 2) sided->SetTexture(side_t::mid, picnum); + if(sections & 4) sided->SetTexture(side_t::bottom, picnum); + } + } + } + } +} + + +//========================================================================== +// +// SoM: Max, Min, Abs math functions. +// +//========================================================================== + +void FParser::SF_Max(void) +{ + fixed_t n1, n2; + + if (CheckArgs(2)) + { + n1 = fixedvalue(t_argv[0]); + n2 = fixedvalue(t_argv[1]); + + t_return.type = svt_fixed; + t_return.value.f = (n1 > n2) ? n1 : n2; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Min(void) +{ + fixed_t n1, n2; + + if (CheckArgs(1)) + { + n1 = fixedvalue(t_argv[0]); + n2 = fixedvalue(t_argv[1]); + + t_return.type = svt_fixed; + t_return.value.f = (n1 < n2) ? n1 : n2; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Abs(void) +{ + fixed_t n1; + + if (CheckArgs(1)) + { + n1 = fixedvalue(t_argv[0]); + + t_return.type = svt_fixed; + t_return.value.f = (n1 < 0) ? -n1 : n1; + } +} + +//========================================================================== +// +// FParser::SF_Gameskill, FParser::SF_Gamemode +// +// Access functions are more elegant for these than variables, +// especially for the game mode, which doesn't exist as a numeric +// variable already. +// +//========================================================================== + +void FParser::SF_Gameskill(void) +{ + t_return.type = svt_int; + t_return.value.i = G_SkillProperty(SKILLP_ACSReturn) + 1; // +1 for the user skill value +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Gamemode(void) +{ + t_return.type = svt_int; + if(!multiplayer) + { + t_return.value.i = 0; // single-player + } + else if(!deathmatch) + { + t_return.value.i = 1; // cooperative + } + else + t_return.value.i = 2; // deathmatch +} + +//========================================================================== +// +// FParser::SF_IsPlayerObj() +// +// A function suggested by SoM to help the script coder prevent +// exceptions related to calling player functions on non-player +// objects. +// +//========================================================================== + +void FParser::SF_IsPlayerObj(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_int; + t_return.value.i = (mo && mo->player) ? 1 : 0; +} + +//============================================================================ +// +// Inventory stuff - mostly new to GZDoom +// +// all the original functions are still supported but they have not +// been expanded from their original and are limited as a result +// +// Since FraggleScript is rather hard coded to the original inventory +// handling of Doom this is quite messy. +// +//============================================================================ + + +//============================================================================ +// +// DoGiveInv +// +// Gives an item to a single actor. +// +//============================================================================ + +static void FS_GiveInventory (AActor *actor, const char * type, int amount) +{ + if (amount <= 0) + { + return; + } + if (strcmp (type, "Armor") == 0) + { + type = "BasicArmorPickup"; + } + const PClass * info = PClass::FindClass (type); + if (info == NULL || !info->IsDescendantOf (RUNTIME_CLASS(AInventory))) + { + Printf ("Unknown inventory item: %s\n", type); + return; + } + + AWeapon *savedPendingWeap = actor->player != NULL? actor->player->PendingWeapon : NULL; + bool hadweap = actor->player != NULL ? actor->player->ReadyWeapon != NULL : true; + + AInventory *item = static_cast(Spawn (info, 0,0,0, NO_REPLACE)); + + // This shouldn't count for the item statistics! + item->ClearCounters(); + if (info->IsDescendantOf (RUNTIME_CLASS(ABasicArmorPickup)) || + info->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus))) + { + static_cast(item)->SaveAmount *= amount; + } + else + { + item->Amount = amount; + } + if (!item->CallTryPickup (actor)) + { + item->Destroy (); + } + // If the item was a weapon, don't bring it up automatically + // unless the player was not already using a weapon. + if (savedPendingWeap != NULL && hadweap) + { + actor->player->PendingWeapon = savedPendingWeap; + } +} + +//============================================================================ +// +// DoTakeInv +// +// Takes an item from a single actor. +// +//============================================================================ + +static void FS_TakeInventory (AActor *actor, const char * type, int amount) +{ + if (strcmp (type, "Armor") == 0) + { + type = "BasicArmor"; + } + if (amount <= 0) + { + return; + } + const PClass * info = PClass::FindClass (type); + if (info == NULL) + { + return; + } + + AInventory *item = actor->FindInventory (info); + if (item != NULL) + { + item->Amount -= amount; + if (item->Amount <= 0) + { + // If it's not ammo, destroy it. Ammo needs to stick around, even + // when it's zero for the benefit of the weapons that use it and + // to maintain the maximum ammo amounts a backpack might have given. + if (item->GetClass()->ParentClass != RUNTIME_CLASS(AAmmo)) + { + item->Destroy (); + } + else + { + item->Amount = 0; + } + } + } +} + +//============================================================================ +// +// CheckInventory +// +// Returns how much of a particular item an actor has. +// +//============================================================================ + +static int FS_CheckInventory (AActor *activator, const char *type) +{ + if (activator == NULL) + return 0; + + if (strcmp (type, "Armor") == 0) + { + type = "BasicArmor"; + } + else if (strcmp (type, "Health") == 0) + { + return activator->health; + } + + const PClass *info = PClass::FindClass (type); + AInventory *item = activator->FindInventory (info); + return item ? item->Amount : 0; +} + + +//========================================================================== +// +// This function is just kept for backwards compatibility +// and intentionally limited to thr standard keys! +// Use Give/Take/CheckInventory instead! +// +//========================================================================== + +void FParser::SF_PlayerKeys(void) +{ + static const char * const DoomKeys[]={"BlueCard", "YellowCard", "RedCard", "BlueSkull", "YellowSkull", "RedSkull"}; + int playernum, keynum, givetake; + const char * keyname; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + if (playernum==-1) return; + + keynum = intvalue(t_argv[1]); + if(keynum < 0 || keynum >= 6) + { + script_error("key number out of range: %i\n", keynum); + return; + } + keyname=DoomKeys[keynum]; + + if(t_argc == 2) + { + t_return.type = svt_int; + t_return.value.i = FS_CheckInventory(players[playernum].mo, keyname); + return; + } + else + { + givetake = intvalue(t_argv[2]); + if(givetake) FS_GiveInventory(players[playernum].mo, keyname, 1); + else FS_TakeInventory(players[playernum].mo, keyname, 1); + t_return.type = svt_int; + t_return.value.i = 0; + } + } +} + +//========================================================================== +// +// This function is just kept for backwards compatibility and intentionally limited! +// Use Give/Take/CheckInventory instead! +// +//========================================================================== + +void FParser::SF_PlayerAmmo(void) +{ + int playernum, amount; + const PClass * ammotype; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + if (playernum==-1) return; + + ammotype=T_GetAmmo(t_argv[1]); + if (!ammotype) return; + + if(t_argc >= 3) + { + AInventory * iammo = players[playernum].mo->FindInventory(ammotype); + amount = intvalue(t_argv[2]); + if(amount < 0) amount = 0; + if (iammo) iammo->Amount = amount; + else players[playernum].mo->GiveAmmo(ammotype, amount); + } + + t_return.type = svt_int; + AInventory * iammo = players[playernum].mo->FindInventory(ammotype); + if (iammo) t_return.value.i = iammo->Amount; + else t_return.value.i = 0; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_MaxPlayerAmmo() +{ + int playernum, amount; + const PClass * ammotype; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + if (playernum==-1) return; + + ammotype=T_GetAmmo(t_argv[1]); + if (!ammotype) return; + + if(t_argc == 2) + { + } + else if(t_argc >= 3) + { + AAmmo * iammo = (AAmmo*)players[playernum].mo->FindInventory(ammotype); + amount = intvalue(t_argv[2]); + if(amount < 0) amount = 0; + if (!iammo) + { + iammo = static_cast(Spawn (ammotype, 0, 0, 0, NO_REPLACE)); + iammo->Amount = 0; + iammo->AttachToOwner (players[playernum].mo); + } + iammo->MaxAmount = amount; + + + for (AInventory *item = players[playernum].mo->Inventory; item != NULL; item = item->Inventory) + { + if (item->IsKindOf(RUNTIME_CLASS(ABackpackItem))) + { + if (t_argc>=4) amount = intvalue(t_argv[3]); + else amount*=2; + break; + } + } + iammo->BackpackMaxAmount=amount; + } + + t_return.type = svt_int; + AInventory * iammo = players[playernum].mo->FindInventory(ammotype); + if (iammo) t_return.value.i = iammo->MaxAmount; + else t_return.value.i = ((AAmmo*)GetDefaultByType(ammotype))->MaxAmount; + } +} + +//========================================================================== +// +// This function is just kept for backwards compatibility and +// intentionally limited to the standard weapons! +// Use Give/Take/CheckInventory instead! +// +//========================================================================== + +void FParser::SF_PlayerWeapon() +{ + static const char * const WeaponNames[]={ + "Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher", + "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" }; + + + int playernum; + int weaponnum; + int newweapon; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + weaponnum = intvalue(t_argv[1]); + if (playernum==-1) return; + if (weaponnum<0 || weaponnum>9) + { + script_error("weaponnum out of range! %s\n", weaponnum); + return; + } + const PClass * ti = PClass::FindClass(WeaponNames[weaponnum]); + if (!ti) + { + script_error("incompatibility in playerweapon\n", weaponnum); + return; + } + + if (t_argc == 2) + { + AActor * wp = players[playernum].mo->FindInventory(ti); + t_return.type = svt_int; + t_return.value.i = wp!=NULL;; + return; + } + else + { + AActor * wp = players[playernum].mo->FindInventory(ti); + + newweapon = !!intvalue(t_argv[2]); + if (!newweapon) + { + if (wp) + { + wp->Destroy(); + // If the weapon is active pick a replacement. Legacy didn't do this! + if (players[playernum].PendingWeapon==wp) players[playernum].PendingWeapon=WP_NOCHANGE; + if (players[playernum].ReadyWeapon==wp) + { + players[playernum].ReadyWeapon=NULL; + players[playernum].mo->PickNewWeapon(NULL); + } + } + } + else + { + if (!wp) + { + AWeapon * pw=players[playernum].PendingWeapon; + players[playernum].mo->GiveInventoryType(ti); + players[playernum].PendingWeapon=pw; + } + } + + t_return.type = svt_int; + t_return.value.i = !!newweapon; + return; + } + } +} + +//========================================================================== +// +// This function is just kept for backwards compatibility and +// intentionally limited to the standard weapons! +// +//========================================================================== + +void FParser::SF_PlayerSelectedWeapon() +{ + int playernum; + int weaponnum; + + + static const char * const WeaponNames[]={ + "Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher", + "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" }; + + + if (CheckArgs(1)) + { + playernum=T_GetPlayerNum(t_argv[0]); + + if(t_argc == 2) + { + weaponnum = intvalue(t_argv[1]); + + if (weaponnum<0 || weaponnum>=9) + { + script_error("weaponnum out of range! %s\n", weaponnum); + return; + } + const PClass * ti = PClass::FindClass(WeaponNames[weaponnum]); + if (!ti) + { + script_error("incompatibility in playerweapon\n", weaponnum); + return; + } + + players[playernum].PendingWeapon = (AWeapon*)players[playernum].mo->FindInventory(ti); + + } + t_return.type = svt_int; + for(int i=0;i<9;i++) + { + if (players[playernum].ReadyWeapon->GetClass ()->TypeName == FName(WeaponNames[i])) + { + t_return.value.i=i; + break; + } + } + } +} + +//========================================================================== +// +// new for GZDoom: named inventory handling +// +//========================================================================== + +void FParser::SF_GiveInventory(void) +{ + int playernum, count; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + if (playernum==-1) return; + + if(t_argc == 2) count=1; + else count=intvalue(t_argv[2]); + FS_GiveInventory(players[playernum].mo, stringvalue(t_argv[1]), count); + t_return.type = svt_int; + t_return.value.i = 0; + } +} + +//========================================================================== +// +// new for GZDoom: named inventory handling +// +//========================================================================== + +void FParser::SF_TakeInventory(void) +{ + int playernum, count; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + if (playernum==-1) return; + + if(t_argc == 2) count=32767; + else count=intvalue(t_argv[2]); + FS_TakeInventory(players[playernum].mo, stringvalue(t_argv[1]), count); + t_return.type = svt_int; + t_return.value.i = 0; + } +} + +//========================================================================== +// +// new for GZDoom: named inventory handling +// +//========================================================================== + +void FParser::SF_CheckInventory(void) +{ + int playernum; + + if (CheckArgs(2)) + { + playernum=T_GetPlayerNum(t_argv[0]); + if (playernum==-1) + { + t_return.value.i = 0; + return; + } + t_return.type = svt_int; + t_return.value.i = FS_CheckInventory(players[playernum].mo, stringvalue(t_argv[1])); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_SetWeapon() +{ + if (CheckArgs(2)) + { + int playernum=T_GetPlayerNum(t_argv[0]); + if (playernum!=-1) + { + AInventory *item = players[playernum].mo->FindInventory (PClass::FindClass (stringvalue(t_argv[1]))); + + if (item == NULL || !item->IsKindOf (RUNTIME_CLASS(AWeapon))) + { + } + else if (players[playernum].ReadyWeapon == item) + { + // The weapon is already selected, so setweapon succeeds by default, + // but make sure the player isn't switching away from it. + players[playernum].PendingWeapon = WP_NOCHANGE; + t_return.value.i = 1; + } + else + { + AWeapon *weap = static_cast (item); + + if (weap->CheckAmmo (AWeapon::EitherFire, false)) + { + // There's enough ammo, so switch to it. + t_return.value.i = 1; + players[playernum].PendingWeapon = weap; + } + } + } + t_return.value.i = 0; + } +} + +//========================================================================== +// +// movecamera(camera, targetobj, targetheight, movespeed, targetangle, anglespeed) +// +//========================================================================== + +void FParser::SF_MoveCamera(void) +{ + fixed_t x, y, z; + fixed_t xdist, ydist, zdist, xydist, movespeed; + fixed_t xstep, ystep, zstep, targetheight; + angle_t anglespeed, anglestep, angledist, targetangle, + mobjangle, bigangle, smallangle; + + // I have to use floats for the math where angles are divided + // by fixed values. + double fangledist, fanglestep, fmovestep; + int angledir; + AActor* target; + int moved; + int quad1, quad2; + AActor * cam; + + angledir = moved = 0; + + if (CheckArgs(6)) + { + cam = actorvalue(t_argv[0]); + + target = actorvalue(t_argv[1]); + if(!cam || !target) + { + script_error("invalid target for camera\n"); return; + } + + targetheight = fixedvalue(t_argv[2]); + movespeed = fixedvalue(t_argv[3]); + targetangle = (angle_t)FixedToAngle(fixedvalue(t_argv[4])); + anglespeed = (angle_t)FixedToAngle(fixedvalue(t_argv[5])); + + // figure out how big one step will be + xdist = target->x - cam->x; + ydist = target->y - cam->y; + zdist = targetheight - cam->z; + + // Angle checking... + // 90 + // Q1|Q0 + //180--+--0 + // Q2|Q3 + // 270 + quad1 = targetangle / ANG90; + quad2 = cam->angle / ANG90; + bigangle = targetangle > cam->angle ? targetangle : cam->angle; + smallangle = targetangle < cam->angle ? targetangle : cam->angle; + if((quad1 > quad2 && quad1 - 1 == quad2) || (quad2 > quad1 && quad2 - 1 == quad1) || + quad1 == quad2) + { + angledist = bigangle - smallangle; + angledir = targetangle > cam->angle ? 1 : -1; + } + else + { + angle_t diff180 = (bigangle + ANG180) - (smallangle + ANG180); + + if(quad2 == 3 && quad1 == 0) + { + angledist = diff180; + angledir = 1; + } + else if(quad1 == 3 && quad2 == 0) + { + angledist = diff180; + angledir = -1; + } + else + { + angledist = bigangle - smallangle; + if(angledist > ANG180) + { + angledist = diff180; + angledir = targetangle > cam->angle ? -1 : 1; + } + else + angledir = targetangle > cam->angle ? 1 : -1; + } + } + + // set step variables based on distance and speed + mobjangle = R_PointToAngle2(cam->x, cam->y, target->x, target->y); + xydist = R_PointToDist2(target->x - cam->x, target->y - cam->y); + + xstep = FixedMul(finecosine[mobjangle >> ANGLETOFINESHIFT], movespeed); + ystep = FixedMul(finesine[mobjangle >> ANGLETOFINESHIFT], movespeed); + + if(xydist && movespeed) + zstep = FixedDiv(zdist, FixedDiv(xydist, movespeed)); + else + zstep = zdist > 0 ? movespeed : -movespeed; + + if(xydist && movespeed && !anglespeed) + { + fangledist = ((double)angledist / (ANG45/45)); + fmovestep = ((double)FixedDiv(xydist, movespeed) / FRACUNIT); + if(fmovestep) + fanglestep = fangledist / fmovestep; + else + fanglestep = 360; + + anglestep =(angle_t) (fanglestep * (ANG45/45)); + } + else + anglestep = anglespeed; + + if(abs(xstep) >= (abs(xdist) - 1)) + x = target->x; + else + { + x = cam->x + xstep; + moved = 1; + } + + if(abs(ystep) >= (abs(ydist) - 1)) + y = target->y; + else + { + y = cam->y + ystep; + moved = 1; + } + + if(abs(zstep) >= (abs(zdist) - 1)) + z = targetheight; + else + { + z = cam->z + zstep; + moved = 1; + } + + if(anglestep >= angledist) + cam->angle = targetangle; + else + { + if(angledir == 1) + { + cam->angle += anglestep; + moved = 1; + } + else if(angledir == -1) + { + cam->angle -= anglestep; + moved = 1; + } + } + + cam->radius=8; + cam->height=8; + if ((x != cam->x || y != cam->y) && !P_TryMove(cam, x, y, true)) + { + Printf("Illegal camera move to (%f, %f)\n", x/65536.f, y/65536.f); + return; + } + cam->z = z; + + t_return.type = svt_int; + t_return.value.i = moved; + } +} + + + +//========================================================================== +// +// FParser::SF_ObjAwaken +// +// Implements: void objawaken([mobj mo]) +// +//========================================================================== + +void FParser::SF_ObjAwaken(void) +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + if(mo) + { + mo->Activate(Script->trigger); + } +} + +//========================================================================== +// +// FParser::SF_AmbientSound +// +// Implements: void ambientsound(string name) +// +//========================================================================== + +void FParser::SF_AmbientSound(void) +{ + if (CheckArgs(1)) + { + S_Sound(CHAN_AUTO, T_FindSound(stringvalue(t_argv[0])), 1, ATTN_NORM); + } +} + + +//========================================================================== +// +// FParser::SF_ExitSecret +// +// Implements: void exitsecret() +// +//========================================================================== + +void FParser::SF_ExitSecret(void) +{ + G_SecretExitLevel(0); +} + + +//========================================================================== +// +// Type forcing functions -- useful with arrays et al +// +//========================================================================== + +void FParser::SF_MobjValue(void) +{ + if (CheckArgs(1)) + { + t_return.type = svt_mobj; + t_return.value.mobj = actorvalue(t_argv[0]); + } +} + +void FParser::SF_StringValue(void) +{ + if (CheckArgs(1)) + { + t_return.type = svt_string; + if (t_argv[0].type == svt_string) + { + t_return.string = t_argv[0].string; + } + else + { + t_return.string = stringvalue(t_argv[0]); + } + } +} + +void FParser::SF_IntValue(void) +{ + if (CheckArgs(1)) + { + t_return.type = svt_int; + t_return.value.i = intvalue(t_argv[0]); + } +} + +void FParser::SF_FixedValue(void) +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = fixedvalue(t_argv[0]); + } +} + + +//========================================================================== +// +// Starting here are functions present in Legacy but not Eternity. +// +//========================================================================== + +void FParser::SF_SpawnExplosion() +{ + fixed_t x, y, z; + AActor* spawn; + const PClass * PClass; + + if (CheckArgs(3)) + { + if (!(PClass=T_GetMobjType(t_argv[0]))) return; + + x = fixedvalue(t_argv[1]); + y = fixedvalue(t_argv[2]); + if(t_argc > 3) + z = fixedvalue(t_argv[3]); + else + z = P_PointInSector(x, y)->floorplane.ZatPoint(x,y); + + spawn = Spawn (PClass, x, y, z, ALLOW_REPLACE); + t_return.type = svt_int; + t_return.value.i=0; + if (spawn) + { + spawn->ClearCounters(); + t_return.value.i = spawn->SetState(spawn->FindState(NAME_Death)); + if(spawn->DeathSound) S_Sound (spawn, CHAN_BODY, spawn->DeathSound, 1, ATTN_NORM); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_RadiusAttack() +{ + AActor *spot; + AActor *source; + int damage; + + if (CheckArgs(3)) + { + spot = actorvalue(t_argv[0]); + source = actorvalue(t_argv[1]); + damage = intvalue(t_argv[2]); + + if (spot && source) + { + P_RadiusAttack(spot, source, damage, damage, NAME_None, true); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_SetObjPosition() +{ + AActor* mobj; + + if (CheckArgs(2)) + { + mobj = actorvalue(t_argv[0]); + + if (!mobj) return; + + mobj->UnlinkFromWorld(); + + mobj->x = intvalue(t_argv[1]) << FRACBITS; + if(t_argc >= 3) mobj->y = intvalue(t_argv[2]) << FRACBITS; + if(t_argc == 4) mobj->z = intvalue(t_argv[3]) << FRACBITS; + + mobj->LinkToWorld(); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_TestLocation() +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + if (mo) + { + t_return.type = svt_int; + t_return.value.f = !!P_TestMobjLocation(mo); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_HealObj() //no pain sound +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + if(t_argc < 2) + { + mo->health = mo->GetDefault()->health; + if(mo->player) mo->player->health = mo->health; + } + + else if (t_argc == 2) + { + mo->health += intvalue(t_argv[1]); + if(mo->player) mo->player->health = mo->health; + } + + else + script_error("invalid number of arguments for objheal"); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjDead() +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + t_return.type = svt_int; + if(mo && (mo->health <= 0 || mo->flags&MF_CORPSE)) + t_return.value.i = 1; + else + t_return.value.i = 0; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_SpawnMissile() +{ + AActor *mobj; + AActor *target; + const PClass * PClass; + + if (CheckArgs(3)) + { + if (!(PClass=T_GetMobjType(t_argv[2]))) return; + + mobj = actorvalue(t_argv[0]); + target = actorvalue(t_argv[1]); + if (mobj && target) P_SpawnMissile(mobj, target, PClass); + } +} + +//========================================================================== +// +//checks to see if a Map Thing Number exists; used to avoid script errors +// +//========================================================================== + +void FParser::SF_MapThingNumExist() +{ + TArray > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + + int intval; + + if (CheckArgs(1)) + { + intval = intvalue(t_argv[0]); + + if (intval < 0 || intval >= int(SpawnedThings.Size()) || !SpawnedThings[intval]) + { + t_return.type = svt_int; + t_return.value.i = 0; + } + else + { + t_return.type = svt_int; + t_return.value.i = 1; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_MapThings() +{ + TArray > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + + t_return.type = svt_int; + t_return.value.i = SpawnedThings.Size(); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_ObjState() +{ + int state; + AActor *mo = NULL; + + if (CheckArgs(1)) + { + if(t_argc == 1) + { + mo = Script->trigger; + state = intvalue(t_argv[0]); + } + + else if(t_argc == 2) + { + mo = actorvalue(t_argv[0]); + state = intvalue(t_argv[1]); + } + + if (mo) + { + static ENamedName statenames[]= { + NAME_Spawn, NAME_See, NAME_Missile, NAME_Melee, + NAME_Pain, NAME_Death, NAME_Raise, NAME_XDeath, NAME_Crash }; + + if (state <1 || state > 9) + { + script_error("objstate: invalid state"); + return; + } + + t_return.type = svt_int; + t_return.value.i = mo->SetState(mo->FindState(statenames[state])); + } + } +} + + +//========================================================================== +// +// only here for Legacy maps. The implementation of this function +// is completely useless. +// +//========================================================================== + +void FParser::SF_LineFlag() +{ + line_t* line; + int linenum; + int flagnum; + + if (CheckArgs(2)) + { + linenum = intvalue(t_argv[0]); + if(linenum < 0 || linenum > numlines) + { + script_error("LineFlag: Invalid line number.\n"); + return; + } + + line = lines + linenum; + + flagnum = intvalue(t_argv[1]); + if(flagnum < 0 || (flagnum > 8 && flagnum!=15)) + { + script_error("LineFlag: Invalid flag number.\n"); + return; + } + + if(t_argc > 2) + { + line->flags &= ~(1 << flagnum); + if(intvalue(t_argv[2])) + line->flags |= (1 << flagnum); + } + + t_return.type = svt_int; + t_return.value.i = line->flags & (1 << flagnum); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_PlayerAddFrag() +{ + int playernum1; + int playernum2; + + if (CheckArgs(1)) + { + if (t_argc == 1) + { + playernum1 = T_GetPlayerNum(t_argv[0]); + + players[playernum1].fragcount++; + + t_return.type = svt_int; + t_return.value.f = players[playernum1].fragcount; + } + + else + { + playernum1 = T_GetPlayerNum(t_argv[0]); + playernum2 = T_GetPlayerNum(t_argv[1]); + + players[playernum1].frags[playernum2]++; + + t_return.type = svt_int; + t_return.value.f = players[playernum1].frags[playernum2]; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_SkinColor() +{ + // Ignoring it for now. +} + +void FParser::SF_PlayDemo() +{ + // Ignoring it for now. +} + +void FParser::SF_CheckCVar() +{ + // can't be done so return 0. +} +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_Resurrect() +{ + + AActor *mo; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + + FState * state = mo->FindState(NAME_Raise); + if (!state) //Don't resurrect things that can't be resurrected + return; + + mo->SetState(state); + mo->height = mo->GetDefault()->height; + mo->radius = mo->GetDefault()->radius; + mo->flags = mo->GetDefault()->flags; + mo->flags2 = mo->GetDefault()->flags2; + mo->flags3 = mo->GetDefault()->flags3; + mo->flags4 = mo->GetDefault()->flags4; + mo->flags5 = mo->GetDefault()->flags5; + mo->health = mo->GetDefault()->health; + mo->target = NULL; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_LineAttack() +{ + AActor *mo; + int damage, angle, slope; + + if (CheckArgs(3)) + { + mo = actorvalue(t_argv[0]); + damage = intvalue(t_argv[2]); + + angle = (intvalue(t_argv[1]) * (ANG45 / 45)); + slope = P_AimLineAttack(mo, angle, MISSILERANGE); + + P_LineAttack(mo, angle, MISSILERANGE, slope, damage, NAME_None, NAME_BulletPuff); + } +} + +//========================================================================== +// +// This is a lousy hack. It only works for the standard actors +// and it is quite inefficient. +// +//========================================================================== + +void FParser::SF_ObjType() +{ + // use trigger object if not specified + AActor *mo; + if(t_argc) + { + mo = actorvalue(t_argv[0]); + } + else + { + mo = Script->trigger; + } + + for(unsigned int i=0;iGetClass() == ActorTypes[i]) + { + t_return.type = svt_int; + t_return.value.i = i; + return; + } + t_return.type = svt_int; + t_return.value.i = -1; +} + +//========================================================================== +// +// some new math functions +// +//========================================================================== + +inline fixed_t double2fixed(double t) +{ + return (fixed_t)(t*65536.0); +} + + + +void FParser::SF_Sin() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(sin(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_ASin() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(asin(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_Cos() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(cos(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_ACos() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(acos(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_Tan() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(tan(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_ATan() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(atan(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_Exp() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(exp(floatvalue(t_argv[0]))); + } +} + +void FParser::SF_Log() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(log(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_Sqrt() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(sqrt(floatvalue(t_argv[0]))); + } +} + + +void FParser::SF_Floor() +{ + if (CheckArgs(1)) + { + t_return.type = svt_fixed; + t_return.value.f = fixedvalue(t_argv[0]) & 0xffFF0000; + } +} + + +void FParser::SF_Pow() +{ + if (CheckArgs(2)) + { + t_return.type = svt_fixed; + t_return.value.f = double2fixed(pow(floatvalue(t_argv[0]), floatvalue(t_argv[1]))); + } +} + +//========================================================================== +// +// HUD pics (not operational yet!) +// +//========================================================================== + + +int HU_GetFSPic(FTextureID lumpnum, int xpos, int ypos); +int HU_DeleteFSPic(unsigned int handle); +int HU_ModifyFSPic(unsigned int handle, FTextureID lumpnum, int xpos, int ypos); +int HU_FSDisplay(unsigned int handle, bool newval); + +void FParser::SF_NewHUPic() +{ + if (CheckArgs(3)) + { + t_return.type = svt_int; + t_return.value.i = HU_GetFSPic( + TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny), + intvalue(t_argv[1]), intvalue(t_argv[2])); + } +} + +void FParser::SF_DeleteHUPic() +{ + if (CheckArgs(1)) + { + if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1) + script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0])); + } +} + +void FParser::SF_ModifyHUPic() +{ + if (t_argc != 4) + { + script_error("modifyhupic: invalid number of arguments\n"); + return; + } + + if (HU_ModifyFSPic(intvalue(t_argv[0]), + TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny), + intvalue(t_argv[2]), intvalue(t_argv[3])) == -1) + { + script_error("modifyhypic: invalid sfpic handle %i\n", intvalue(t_argv[0])); + } + return; +} + +void FParser::SF_SetHUPicDisplay() +{ + if (t_argc != 2) + { + script_error("sethupicdisplay: invalud number of arguments\n"); + return; + } + + if (HU_FSDisplay(intvalue(t_argv[0]), intvalue(t_argv[1]) > 0 ? 1 : 0) == -1) + script_error("sethupicdisplay: invalid pic handle %i\n", intvalue(t_argv[0])); +} + + +//========================================================================== +// +// Yet to be made operational. +// +//========================================================================== + +void FParser::SF_SetCorona(void) +{ + if(t_argc != 3) + { + script_error("incorrect arguments to function\n"); + return; + } + + int num = t_argv[0].value.i; // which corona we want to modify + int what = t_argv[1].value.i; // what we want to modify (type, color, offset,...) + int ival = t_argv[2].value.i; // the value of what we modify + double fval = ((double) t_argv[2].value.f / FRACUNIT); + + /* + switch (what) + { + case 0: + lspr[num].type = ival; + break; + case 1: + lspr[num].light_xoffset = fval; + break; + case 2: + lspr[num].light_yoffset = fval; + break; + case 3: + if (t_argv[2].type == svt_string) + lspr[num].corona_color = String2Hex(t_argv[2].value.s); + else + memcpy(&lspr[num].corona_color, &ival, sizeof(int)); + break; + case 4: + lspr[num].corona_radius = fval; + break; + case 5: + if (t_argv[2].type == svt_string) + lspr[num].dynamic_color = String2Hex(t_argv[2].value.s); + else + memcpy(&lspr[num].dynamic_color, &ival, sizeof(int)); + break; + case 6: + lspr[num].dynamic_radius = fval; + lspr[num].dynamic_sqrradius = sqrt(lspr[num].dynamic_radius); + break; + default: + CONS_Printf("Error in setcorona\n"); + break; + } + */ + + // no use for this! + t_return.type = svt_int; + t_return.value.i = 0; +} + +//========================================================================== +// +// new for GZDoom: Call a Hexen line special (deprecated, superseded by direct use) +// +//========================================================================== + +void FParser::SF_Ls() +{ + int args[5]={0,0,0,0,0}; + int spc; + + if (CheckArgs(1)) + { + spc=intvalue(t_argv[0]); + for(int i=0;i<5;i++) + { + if (t_argc>=i+2) args[i]=intvalue(t_argv[i+1]); + } + if (spc>=0 && spc<256) + LineSpecials[spc](NULL,Script->trigger,false, args[0],args[1],args[2],args[3],args[4]); + } +} + + +//========================================================================== +// +// new for GZDoom: Gets the levelnum +// +//========================================================================== + +void FParser::SF_LevelNum() +{ + t_return.type = svt_int; + t_return.value.f = level.levelnum; +} + + +//========================================================================== +// +// new for GZDoom +// +//========================================================================== + +void FParser::SF_MobjRadius(void) +{ + AActor* mo; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + if(t_argc > 1) + { + if(mo) + mo->radius = fixedvalue(t_argv[1]); + } + + t_return.type = svt_fixed; + t_return.value.f = mo ? mo->radius : 0; + } +} + + +//========================================================================== +// +// new for GZDoom +// +//========================================================================== + +void FParser::SF_MobjHeight(void) +{ + AActor* mo; + + if (CheckArgs(1)) + { + mo = actorvalue(t_argv[0]); + if(t_argc > 1) + { + if(mo) + mo->height = fixedvalue(t_argv[1]); + } + + t_return.type = svt_fixed; + t_return.value.f = mo ? mo->height : 0; + } +} + + +//========================================================================== +// +// new for GZDoom +// +//========================================================================== + +void FParser::SF_ThingCount(void) +{ + const PClass *pClass; + AActor * mo; + int count=0; + bool replacemented = false; + + + if (CheckArgs(1)) + { + pClass=T_GetMobjType(t_argv[0]); + if (!pClass) return; + // If we want to count map items we must consider actor replacement + pClass = pClass->ActorInfo->GetReplacement()->Class; + +again: + TThinkerIterator it; + + if (t_argc<2 || intvalue(t_argv[1])==0 || pClass->IsDescendantOf(RUNTIME_CLASS(AInventory))) + { + while ((mo=it.Next())) + { + if (mo->IsA(pClass)) + { + if (!mo->IsKindOf (RUNTIME_CLASS(AInventory)) || + static_cast(mo)->Owner == NULL) + { + count++; + } + } + } + } + else + { + while ((mo=it.Next())) + { + if (mo->IsA(pClass) && mo->health>0) count++; + } + } + if (!replacemented) + { + // Again, with decorate replacements + replacemented = true; + PClass *newkind = pClass->ActorInfo->GetReplacement()->Class; + if (newkind != pClass) + { + pClass = newkind; + goto again; + } + } + t_return.type = svt_int; + t_return.value.i = count; + } +} + +//========================================================================== +// +// new for GZDoom: Sets a sector color +// +//========================================================================== + +void FParser::SF_SetColor(void) +{ + int tagnum, secnum; + int c=2; + int i = -1; + PalEntry color=0; + + if (CheckArgs(2)) + { + tagnum = intvalue(t_argv[0]); + + secnum = T_FindSectorFromTag(tagnum, -1); + + if(secnum < 0) + { + return; + } + + if(t_argc >1 && t_argc<4) + { + color=intvalue(t_argv[1]); + } + else if (t_argc>=4) + { + color.r=intvalue(t_argv[1]); + color.g=intvalue(t_argv[2]); + color.b=intvalue(t_argv[3]); + color.a=0; + } + else return; + + // set all sectors with tag + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sectors[i].ColorMap = GetSpecialLights (color, sectors[i].ColorMap->Fade, 0); + } + } +} + + +//========================================================================== +// +// Spawns a projectile at a map spot +// +//========================================================================== + +void FParser::SF_SpawnShot2(void) +{ + AActor *source = NULL; + const PClass * PClass; + int z=0; + + // t_argv[0] = type to spawn + // t_argv[1] = source mobj, optional, -1 to default + // shoots at source's angle + + if (CheckArgs(2)) + { + if(t_argv[1].type == svt_int && t_argv[1].value.i < 0) + source = Script->trigger; + else + source = actorvalue(t_argv[1]); + + if (t_argc>2) z=fixedvalue(t_argv[2]); + + if(!source) return; + + if (!(PClass=T_GetMobjType(t_argv[0]))) return; + + t_return.type = svt_mobj; + + AActor *mo = Spawn (PClass, source->x, source->y, source->z+z, ALLOW_REPLACE); + if (mo) + { + S_Sound (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM); + mo->target = source; + P_ThrustMobj(mo, mo->angle = source->angle, mo->Speed); + if (!P_CheckMissileSpawn(mo)) mo = NULL; + } + t_return.value.mobj = mo; + } +} + + + +//========================================================================== +// +// new for GZDoom +// +//========================================================================== + +void FParser::SF_KillInSector() +{ + if (CheckArgs(1)) + { + TThinkerIterator it; + AActor * mo; + int tag=intvalue(t_argv[0]); + + while ((mo=it.Next())) + { + if (mo->flags3&MF3_ISMONSTER && mo->Sector->tag==tag) P_DamageMobj(mo, NULL, NULL, 1000000, NAME_Massacre); + } + } +} + +//========================================================================== +// +// new for GZDoom: Sets a sector's type +// (Sure, this is not particularly useful. But having it made it possible +// to fix a few annoying bugs in some old maps ;) ) +// +//========================================================================== + +void FParser::SF_SectorType(void) +{ + int tagnum, secnum; + sector_t *sector; + + if (CheckArgs(1)) + { + tagnum = intvalue(t_argv[0]); + + // argv is sector tag + secnum = T_FindSectorFromTag(tagnum, -1); + + if(secnum < 0) + { script_error("sector not found with tagnum %i\n", tagnum); return;} + + sector = §ors[secnum]; + + if(t_argc > 1) + { + int i = -1; + int spec = intvalue(t_argv[1]); + + // set all sectors with tag + while ((i = T_FindSectorFromTag(tagnum, i)) >= 0) + { + sectors[i].special = spec; + } + } + + t_return.type = svt_int; + t_return.value.i = sector->special; + } +} + +//========================================================================== +// +// new for GZDoom: Sets a new line trigger type (Doom format!) +// (Sure, this is not particularly useful. But having it made it possible +// to fix a few annoying bugs in some old maps ;) ) +// +//========================================================================== + +void FParser::SF_SetLineTrigger() +{ + int i,id,spec,tag(0); + + if (CheckArgs(2)) + { + id=intvalue(t_argv[0]); + spec=intvalue(t_argv[1]); + if (t_argc>2) tag=intvalue(t_argv[2]); + for (i = -1; (i = P_FindLineFromID (id, i)) >= 0; ) + { + if (t_argc==2) tag=lines[i].id; + maplinedef_t mld; + mld.special=spec; + mld.tag=tag; + mld.flags=0; + int f = lines[i].flags; + P_TranslateLineDef(&lines[i], &mld); + lines[i].id=tag; + lines[i].flags = (lines[i].flags & (ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_SPAC_MASK|ML_FIRSTSIDEONLY)) | + (f & ~(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_SPAC_MASK|ML_FIRSTSIDEONLY)); + + } + } +} + + +//========================================================================== +// +// new for GZDoom: Changes a sector's tag +// (I only need this because MAP02 in RTC-3057 has some issues with the GL +// renderer that I can't fix without the scripts. But loading a FS on top on +// ACS still works so I can hack around it with this.) +// +//========================================================================== + +void FParser::SF_ChangeTag() +{ + if (CheckArgs(2)) + { + for (int secnum = -1; (secnum = P_FindSectorFromTag (t_argv[0].value.i, secnum)) >= 0; ) + { + sectors[secnum].tag=t_argv[1].value.i; + } + + // Recreate the hash tables + int i; + + for (i=numsectors; --i>=0; ) sectors[i].firsttag = -1; + for (i=numsectors; --i>=0; ) + { + int j = (unsigned) sectors[i].tag % (unsigned) numsectors; + sectors[i].nexttag = sectors[j].firsttag; + sectors[j].firsttag = i; + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FParser::SF_WallGlow() +{ + // Development garbage! +} + + +//========================================================================== +// +// new for GZDoom: Call a Hexen line special +// +//========================================================================== + +void FParser::RunLineSpecial(const FLineSpecial *spec) +{ + + if (CheckArgs(spec->min_args)) + { + int args[5]; + for(int i=0;i<5;i++) + { + if (t_argc>i) args[i]=intvalue(t_argv[i]); + else args[i] = 0; + } + t_return.value.i = LineSpecials[spec->number](NULL,Script->trigger,false, args[0],args[1],args[2],args[3],args[4]); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +DRunningScript *FParser::SaveCurrentScript() +{ + DFraggleThinker *th = DFraggleThinker::ActiveThinker; + if (th) + { + DRunningScript *runscr = new DRunningScript(Script->trigger, Script, Script->MakeIndex(Rover)); + + // hook into chain at start + th->AddRunningScript(runscr); + return runscr; + } + return NULL; +} + +//========================================================================== +// +// script function +// +//========================================================================== + +void FParser::SF_Wait() +{ + DRunningScript *runscr; + + if(t_argc != 1) + { + script_error("incorrect arguments to function\n"); + return; + } + + runscr = SaveCurrentScript(); + + runscr->wait_type = wt_delay; + + runscr->wait_data = (intvalue(t_argv[0]) * TICRATE) / 100; + throw CFsTerminator(); +} + +//========================================================================== +// +// wait for sector with particular tag to stop moving +// +//========================================================================== + +void FParser::SF_TagWait() +{ + DRunningScript *runscr; + + if(t_argc != 1) + { + script_error("incorrect arguments to function\n"); + return; + } + + runscr = SaveCurrentScript(); + + runscr->wait_type = wt_tagwait; + runscr->wait_data = intvalue(t_argv[0]); + throw CFsTerminator(); +} + +//========================================================================== +// +// wait for a script to finish +// +//========================================================================== + +void FParser::SF_ScriptWait() +{ + DRunningScript *runscr; + + if(t_argc != 1) + { + script_error("insufficient arguments to function\n"); + return; + } + + runscr = SaveCurrentScript(); + + runscr->wait_type = wt_scriptwait; + runscr->wait_data = intvalue(t_argv[0]); + throw CFsTerminator(); +} + +//========================================================================== +// +// haleyjd 05/23/01: wait for a script to start (zdoom-inspired) +// +//========================================================================== + +void FParser::SF_ScriptWaitPre() +{ + DRunningScript *runscr; + + if(t_argc != 1) + { + script_error("insufficient arguments to function\n"); + return; + } + + runscr = SaveCurrentScript(); + runscr->wait_type = wt_scriptwaitpre; + runscr->wait_data = intvalue(t_argv[0]); + throw CFsTerminator(); +} + +//========================================================================== +// +// start a new script +// +//========================================================================== + +void FParser::SF_StartScript() +{ + if(t_argc != 1) + { + script_error("incorrect arguments to function\n"); + return; + } + + int snum = intvalue(t_argv[0]); + + if(snum < 0 || snum >= MAXSCRIPTS) + { + script_error("script number %d out of range\n",snum); + return; + } + + DFraggleThinker *th = DFraggleThinker::ActiveThinker; + if (th) + { + + DFsScript *script = th->LevelScript->children[snum]; + + if(!script) + { + script_error("script %i not defined\n", snum); + } + + DRunningScript *runscr = new DRunningScript(Script->trigger, script, 0); + // hook into chain at start + th->AddRunningScript(runscr); + } +} + +//========================================================================== +// +// checks if a script is running +// +//========================================================================== + +void FParser::SF_ScriptRunning() +{ + DRunningScript *current; + int snum = 0; + + if(t_argc < 1) + { + script_error("not enough arguments to function\n"); + return; + } + + snum = intvalue(t_argv[0]); + + for(current = DFraggleThinker::ActiveThinker->RunningScripts->next; current; current=current->next) + { + if(current->script->scriptnum == snum) + { + // script found so return + t_return.type = svt_int; + t_return.value.i = 1; + return; + } + } + + // script not found + t_return.type = svt_int; + t_return.value.i = 0; +} + + +//========================================================================== +// +// Init Functions +// +//========================================================================== + +static int zoom=1; // Dummy - no longer needed! + +void init_functions(void) +{ + for(unsigned i=0;iNewVariable("consoleplayer", svt_pInt)->value.pI = &consoleplayer; + gscr->NewVariable("displayplayer", svt_pInt)->value.pI = &consoleplayer; + gscr->NewVariable("zoom", svt_pInt)->value.pI = &zoom; + gscr->NewVariable("fov", svt_pInt)->value.pI = &zoom; + gscr->NewVariable("trigger", svt_pMobj)->value.pMobj = &trigger_obj; + + // Create constants for all existing line specials + for(int i=0; i<256; i++) + { + const FLineSpecial *ls = LineSpecialsInfo[i]; + + if (ls != NULL && ls->max_args >= 0) // specials with max args set to -1 can only be used in a map and are of no use hee. + { + gscr->NewVariable(ls->name, svt_linespec)->value.ls = ls; + } + } + + // important C-emulating stuff + gscr->NewFunction("break", &FParser::SF_Break); + gscr->NewFunction("continue", &FParser::SF_Continue); + gscr->NewFunction("return", &FParser::SF_Return); + gscr->NewFunction("goto", &FParser::SF_Goto); + gscr->NewFunction("include", &FParser::SF_Include); + + // standard FraggleScript functions + gscr->NewFunction("print", &FParser::SF_Print); + gscr->NewFunction("rnd", &FParser::SF_Rnd); // Legacy uses a normal rand() call for this which is extremely dangerous. + gscr->NewFunction("prnd", &FParser::SF_Rnd); // I am mapping rnd and prnd to the same named RNG which should eliminate any problem + gscr->NewFunction("input", &FParser::SF_Input); + gscr->NewFunction("beep", &FParser::SF_Beep); + gscr->NewFunction("clock", &FParser::SF_Clock); + gscr->NewFunction("wait", &FParser::SF_Wait); + gscr->NewFunction("tagwait", &FParser::SF_TagWait); + gscr->NewFunction("scriptwait", &FParser::SF_ScriptWait); + gscr->NewFunction("startscript", &FParser::SF_StartScript); + gscr->NewFunction("scriptrunning", &FParser::SF_ScriptRunning); + + // doom stuff + gscr->NewFunction("startskill", &FParser::SF_StartSkill); + gscr->NewFunction("exitlevel", &FParser::SF_ExitLevel); + gscr->NewFunction("tip", &FParser::SF_Tip); + gscr->NewFunction("timedtip", &FParser::SF_TimedTip); + gscr->NewFunction("message", &FParser::SF_Message); + gscr->NewFunction("gameskill", &FParser::SF_Gameskill); + gscr->NewFunction("gamemode", &FParser::SF_Gamemode); + + // player stuff + gscr->NewFunction("playermsg", &FParser::SF_PlayerMsg); + gscr->NewFunction("playertip", &FParser::SF_PlayerTip); + gscr->NewFunction("playeringame", &FParser::SF_PlayerInGame); + gscr->NewFunction("playername", &FParser::SF_PlayerName); + gscr->NewFunction("playeraddfrag", &FParser::SF_PlayerAddFrag); + gscr->NewFunction("playerobj", &FParser::SF_PlayerObj); + gscr->NewFunction("isplayerobj", &FParser::SF_IsPlayerObj); + gscr->NewFunction("isobjplayer", &FParser::SF_IsPlayerObj); + gscr->NewFunction("skincolor", &FParser::SF_SkinColor); + gscr->NewFunction("playerkeys", &FParser::SF_PlayerKeys); + gscr->NewFunction("playerammo", &FParser::SF_PlayerAmmo); + gscr->NewFunction("maxplayerammo", &FParser::SF_MaxPlayerAmmo); + gscr->NewFunction("playerweapon", &FParser::SF_PlayerWeapon); + gscr->NewFunction("playerselwep", &FParser::SF_PlayerSelectedWeapon); + + // mobj stuff + gscr->NewFunction("spawn", &FParser::SF_Spawn); + gscr->NewFunction("spawnexplosion", &FParser::SF_SpawnExplosion); + gscr->NewFunction("radiusattack", &FParser::SF_RadiusAttack); + gscr->NewFunction("kill", &FParser::SF_KillObj); + gscr->NewFunction("removeobj", &FParser::SF_RemoveObj); + gscr->NewFunction("objx", &FParser::SF_ObjX); + gscr->NewFunction("objy", &FParser::SF_ObjY); + gscr->NewFunction("objz", &FParser::SF_ObjZ); + gscr->NewFunction("testlocation", &FParser::SF_TestLocation); + gscr->NewFunction("teleport", &FParser::SF_Teleport); + gscr->NewFunction("silentteleport", &FParser::SF_SilentTeleport); + gscr->NewFunction("damageobj", &FParser::SF_DamageObj); + gscr->NewFunction("healobj", &FParser::SF_HealObj); + gscr->NewFunction("player", &FParser::SF_Player); + gscr->NewFunction("objsector", &FParser::SF_ObjSector); + gscr->NewFunction("objflag", &FParser::SF_ObjFlag); + gscr->NewFunction("pushobj", &FParser::SF_PushThing); + gscr->NewFunction("pushthing", &FParser::SF_PushThing); + gscr->NewFunction("objangle", &FParser::SF_ObjAngle); + gscr->NewFunction("objhealth", &FParser::SF_ObjHealth); + gscr->NewFunction("objdead", &FParser::SF_ObjDead); + gscr->NewFunction("reactiontime", &FParser::SF_ReactionTime); + gscr->NewFunction("objreactiontime", &FParser::SF_ReactionTime); + gscr->NewFunction("objtarget", &FParser::SF_MobjTarget); + gscr->NewFunction("objmomx", &FParser::SF_MobjMomx); + gscr->NewFunction("objmomy", &FParser::SF_MobjMomy); + gscr->NewFunction("objmomz", &FParser::SF_MobjMomz); + + gscr->NewFunction("spawnmissile", &FParser::SF_SpawnMissile); + gscr->NewFunction("mapthings", &FParser::SF_MapThings); + gscr->NewFunction("objtype", &FParser::SF_ObjType); + gscr->NewFunction("mapthingnumexist", &FParser::SF_MapThingNumExist); + gscr->NewFunction("objstate", &FParser::SF_ObjState); + gscr->NewFunction("resurrect", &FParser::SF_Resurrect); + gscr->NewFunction("lineattack", &FParser::SF_LineAttack); + gscr->NewFunction("setobjposition", &FParser::SF_SetObjPosition); + + // sector stuff + gscr->NewFunction("floorheight", &FParser::SF_FloorHeight); + gscr->NewFunction("floortext", &FParser::SF_FloorTexture); + gscr->NewFunction("floortexture", &FParser::SF_FloorTexture); // haleyjd: alias + gscr->NewFunction("movefloor", &FParser::SF_MoveFloor); + gscr->NewFunction("ceilheight", &FParser::SF_CeilingHeight); + gscr->NewFunction("ceilingheight", &FParser::SF_CeilingHeight); // haleyjd: alias + gscr->NewFunction("moveceil", &FParser::SF_MoveCeiling); + gscr->NewFunction("moveceiling", &FParser::SF_MoveCeiling); // haleyjd: aliases + gscr->NewFunction("ceilingtexture", &FParser::SF_CeilingTexture); + gscr->NewFunction("ceiltext", &FParser::SF_CeilingTexture); // haleyjd: wrong + gscr->NewFunction("lightlevel", &FParser::SF_LightLevel); // handler - was + gscr->NewFunction("fadelight", &FParser::SF_FadeLight); // &FParser::SF_FloorTexture! + gscr->NewFunction("colormap", &FParser::SF_SectorColormap); + + // cameras! + gscr->NewFunction("setcamera", &FParser::SF_SetCamera); + gscr->NewFunction("clearcamera", &FParser::SF_ClearCamera); + gscr->NewFunction("movecamera", &FParser::SF_MoveCamera); + + // trig functions + gscr->NewFunction("pointtoangle", &FParser::SF_PointToAngle); + gscr->NewFunction("pointtodist", &FParser::SF_PointToDist); + + // sound functions + gscr->NewFunction("startsound", &FParser::SF_StartSound); + gscr->NewFunction("startsectorsound", &FParser::SF_StartSectorSound); + gscr->NewFunction("ambientsound", &FParser::SF_AmbientSound); + gscr->NewFunction("startambiantsound", &FParser::SF_AmbientSound); // Legacy's incorrectly spelled name! + gscr->NewFunction("changemusic", &FParser::SF_ChangeMusic); + + // hubs! + gscr->NewFunction("changehublevel", &FParser::SF_ChangeHubLevel); + + // doors + gscr->NewFunction("opendoor", &FParser::SF_OpenDoor); + gscr->NewFunction("closedoor", &FParser::SF_CloseDoor); + + // HU Graphics + gscr->NewFunction("newhupic", &FParser::SF_NewHUPic); + gscr->NewFunction("createpic", &FParser::SF_NewHUPic); + gscr->NewFunction("deletehupic", &FParser::SF_DeleteHUPic); + gscr->NewFunction("modifyhupic", &FParser::SF_ModifyHUPic); + gscr->NewFunction("modifypic", &FParser::SF_ModifyHUPic); + gscr->NewFunction("sethupicdisplay", &FParser::SF_SetHUPicDisplay); + gscr->NewFunction("setpicvisible", &FParser::SF_SetHUPicDisplay); + + // + gscr->NewFunction("playdemo", &FParser::SF_PlayDemo); + gscr->NewFunction("runcommand", &FParser::SF_RunCommand); + gscr->NewFunction("checkcvar", &FParser::SF_CheckCVar); + gscr->NewFunction("setlinetexture", &FParser::SF_SetLineTexture); + gscr->NewFunction("linetrigger", &FParser::SF_LineTrigger); + gscr->NewFunction("lineflag", &FParser::SF_LineFlag); + + //Hurdler: new math functions + gscr->NewFunction("max", &FParser::SF_Max); + gscr->NewFunction("min", &FParser::SF_Min); + gscr->NewFunction("abs", &FParser::SF_Abs); + + gscr->NewFunction("sin", &FParser::SF_Sin); + gscr->NewFunction("asin", &FParser::SF_ASin); + gscr->NewFunction("cos", &FParser::SF_Cos); + gscr->NewFunction("acos", &FParser::SF_ACos); + gscr->NewFunction("tan", &FParser::SF_Tan); + gscr->NewFunction("atan", &FParser::SF_ATan); + gscr->NewFunction("exp", &FParser::SF_Exp); + gscr->NewFunction("log", &FParser::SF_Log); + gscr->NewFunction("sqrt", &FParser::SF_Sqrt); + gscr->NewFunction("floor", &FParser::SF_Floor); + gscr->NewFunction("pow", &FParser::SF_Pow); + + // Eternity extensions + gscr->NewFunction("setlineblocking", &FParser::SF_SetLineBlocking); + gscr->NewFunction("setlinetrigger", &FParser::SF_SetLineTrigger); + gscr->NewFunction("setlinemnblock", &FParser::SF_SetLineMonsterBlocking); + gscr->NewFunction("scriptwaitpre", &FParser::SF_ScriptWaitPre); + gscr->NewFunction("exitsecret", &FParser::SF_ExitSecret); + gscr->NewFunction("objawaken", &FParser::SF_ObjAwaken); + + // forced coercion functions + gscr->NewFunction("mobjvalue", &FParser::SF_MobjValue); + gscr->NewFunction("stringvalue", &FParser::SF_StringValue); + gscr->NewFunction("intvalue", &FParser::SF_IntValue); + gscr->NewFunction("fixedvalue", &FParser::SF_FixedValue); + + // new for GZDoom + gscr->NewFunction("spawnshot2", &FParser::SF_SpawnShot2); + gscr->NewFunction("setcolor", &FParser::SF_SetColor); + gscr->NewFunction("sectortype", &FParser::SF_SectorType); + gscr->NewFunction("wallglow", &FParser::SF_WallGlow); + gscr->NewFunction("objradius", &FParser::SF_MobjRadius); + gscr->NewFunction("objheight", &FParser::SF_MobjHeight); + gscr->NewFunction("thingcount", &FParser::SF_ThingCount); + gscr->NewFunction("killinsector", &FParser::SF_KillInSector); + gscr->NewFunction("changetag", &FParser::SF_ChangeTag); + gscr->NewFunction("levelnum", &FParser::SF_LevelNum); + + // new inventory + gscr->NewFunction("giveinventory", &FParser::SF_GiveInventory); + gscr->NewFunction("takeinventory", &FParser::SF_TakeInventory); + gscr->NewFunction("checkinventory", &FParser::SF_CheckInventory); + gscr->NewFunction("setweapon", &FParser::SF_SetWeapon); + + gscr->NewFunction("ls", &FParser::SF_Ls); // execute Hexen type line special + + // Dummies - shut up warnings + gscr->NewFunction("setcorona", &FParser::SF_SetCorona); +} + diff --git a/src/fragglescript/t_load.cpp b/src/fragglescript/t_load.cpp new file mode 100644 index 000000000..c374de710 --- /dev/null +++ b/src/fragglescript/t_load.cpp @@ -0,0 +1,368 @@ +/* +** 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_data.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("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("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 > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + SpawnedThings.Push(GC::ReadBarrier(ac)); + } +} diff --git a/src/fragglescript/t_oper.cpp b/src/fragglescript/t_oper.cpp new file mode 100644 index 000000000..c30124712 --- /dev/null +++ b/src/fragglescript/t_oper.cpp @@ -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)); +} + diff --git a/src/fragglescript/t_parse.cpp b/src/fragglescript/t_parse.cpp new file mode 100644 index 000000000..25f1bbf84 --- /dev/null +++ b/src/fragglescript/t_parse.cpp @@ -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 +#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*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; tempscriptnum, 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 diff --git a/src/fragglescript/t_prepro.cpp b/src/fragglescript/t_prepro.cpp new file mode 100644 index 000000000..c793c222f --- /dev/null +++ b/src/fragglescript/t_prepro.cpp @@ -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;inext; + 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; nnext; + } + } + 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; +} + diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp new file mode 100644 index 000000000..c02958758 --- /dev/null +++ b/src/fragglescript/t_script.cpp @@ -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 "r_local.h" +#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" + +//========================================================================== +// +// 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;jDestroy(); + children[j]=NULL; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +DFsScript::DFsScript() +{ + int i; + + for(i=0; i 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; ivariables[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; inext; // 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::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 = §ors[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; iscript->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(old)) + { + SpawnedThings[i] = static_cast(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; ivariables[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