qzdoom/src/fragglescript/t_variable.cpp
Christoph Oelckers 66d28a24b8 - disabled the scripted virtual function module after finding out that it only works if each single class that may serve as a parent for scripting is explicitly declared.
Needless to say, this is simply too volatile and would require constant active maintenance, not to mention a huge amount of work up front to get going.
It also hid a nasty problem with the Destroy method. Due to the way the garbage collector works, Destroy cannot be exposed to scripts as-is. It may be called from scripts but it may not be overridden from scripts because the garbage collector can call this function after all data needed for calling a scripted override has already been destroyed because if that data is also being collected there is no guarantee that proper order of destruction is observed. So for now Destroy is just a normal native method to scripted classes
2016-11-25 00:25:26 +01:00

445 lines
11 KiB
C++

// Emacs style mode select -*- C++ -*-
//----------------------------------------------------------------------------
//
// Copyright(C) 2000 Simon Howard
// Copyright(C) 2002-2008 Christoph Oelckers
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//--------------------------------------------------------------------------
//
// Variables.
//
// Variable code: create new variables, look up variables, get value,
// set value
//
// variables are stored inside the individual scripts, to allow for
// 'local' and 'global' variables. This way, individual scripts cannot
// access variables in other scripts. However, 'global' variables can
// be made which can be accessed by all scripts. These are stored inside
// a dedicated DFsScript which exists only to hold all of these global
// variables.
//
// functions are also stored as variables, these are kept in the global
// script so they can be accessed by all scripts. function variables
// cannot be set or changed inside the scripts themselves.
//
//---------------------------------------------------------------------------
//
// FraggleScript is from SMMU which is under the GPL. Technically,
// therefore, combining the FraggleScript code with the non-free
// ZDoom code is a violation of the GPL.
//
// As this may be a problem for you, I hereby grant an exception to my
// copyright on the SMMU source (including FraggleScript). You may use
// any code from SMMU in (G)ZDoom, provided that:
//
// * For any binary release of the port, the source code is also made
// available.
// * The copyright notice is kept on any file containing my code.
//
//
#include "t_script.h"
#include "a_pickups.h"
#include "serializer.h"
//==========================================================================
//
//
//
//==========================================================================
int intvalue(const svalue_t &v)
{
return (v.type == svt_string ? atoi(v.string) :
v.type == svt_fixed ? (int)(v.value.f / 65536.) :
v.type == svt_mobj ? -1 : v.value.i );
}
//==========================================================================
//
//
//
//==========================================================================
fsfix fixedvalue(const svalue_t &v)
{
return (v.type == svt_fixed ? v.value.f :
v.type == svt_string ? (fsfix)(atof(v.string) * 65536.) :
v.type == svt_mobj ? -65536 : v.value.i * 65536 );
}
//==========================================================================
//
//
//
//==========================================================================
double floatvalue(const svalue_t &v)
{
return
v.type == svt_string ? atof(v.string) :
v.type == svt_fixed ? v.value.f / 65536. :
v.type == svt_mobj ? -1. : (double)v.value.i;
}
//==========================================================================
//
// sf: string value of an svalue_t
//
//==========================================================================
const char *stringvalue(const svalue_t & v)
{
static char buffer[256];
switch(v.type)
{
case svt_string:
return v.string;
case svt_mobj:
// return the class name
return (const char *)v.value.mobj->GetClass()->TypeName;
case svt_fixed:
{
double val = v.value.f / 65536.;
mysnprintf(buffer, countof(buffer), "%g", val);
return buffer;
}
case svt_int:
default:
mysnprintf(buffer, countof(buffer), "%i", v.value.i);
return buffer;
}
}
//==========================================================================
//
//
//==========================================================================
AActor* actorvalue(const svalue_t &svalue)
{
int intval;
if(svalue.type == svt_mobj)
{
// Inventory items in the player's inventory have to be considered non-present.
if (svalue.value.mobj != NULL &&
svalue.value.mobj->IsKindOf(RUNTIME_CLASS(AInventory)) &&
static_cast<AInventory*>(svalue.value.mobj)->Owner != NULL)
{
return NULL;
}
return svalue.value.mobj;
}
else
{
TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
// this requires some creativity. We use the intvalue
// as the thing number of a thing in the level.
intval = intvalue(svalue);
if(intval < 0 || intval >= (int)SpawnedThings.Size())
{
return NULL;
}
// Inventory items in the player's inventory have to be considered non-present.
if (SpawnedThings[intval] != NULL &&
SpawnedThings[intval]->IsKindOf(RUNTIME_CLASS(AInventory)) &&
barrier_cast<AInventory*>(SpawnedThings[intval])->Owner != NULL)
{
return NULL;
}
return SpawnedThings[intval];
}
}
//==========================================================================
//
//
//==========================================================================
IMPLEMENT_CLASS(DFsVariable, false, true)
IMPLEMENT_POINTERS_START(DFsVariable)
IMPLEMENT_POINTER(next)
IMPLEMENT_POINTER(actor)
IMPLEMENT_POINTERS_END
//==========================================================================
//
//
//==========================================================================
DFsVariable::DFsVariable(const char * _name)
{
Name=_name;
type=svt_int;
actor = NULL;
value.i=0;
next=NULL;
}
//==========================================================================
//
// returns an svalue_t holding the current
// value of a particular variable.
//
//==========================================================================
void DFsVariable::GetValue(svalue_t &returnvar)
{
switch (type)
{
case svt_pInt:
returnvar.type = svt_int;
returnvar.value.i = *value.pI;
break;
case svt_pMobj:
returnvar.type = svt_mobj;
returnvar.value.mobj = *value.pMobj;
break;
case svt_mobj:
returnvar.type = type;
returnvar.value.mobj = actor;
break;
case svt_linespec:
returnvar.type = svt_int;
returnvar.value.i = value.ls->number;
break;
case svt_string:
returnvar.type = type;
returnvar.string = string;
break;
default:
// copy the value (also handles fixed)
returnvar.type = type;
returnvar.value.i = value.i;
break;
}
}
//==========================================================================
//
// set a variable to a value from an svalue_t
//
//==========================================================================
void DFsVariable::SetValue(const svalue_t &newvalue)
{
if(type == svt_const)
{
// const adapts to the value it is set to
type = newvalue.type;
}
switch (type)
{
case svt_int:
value.i = intvalue(newvalue);
break;
case svt_string:
if (newvalue.type == svt_string)
{
string = newvalue.string;
}
else
{
string = stringvalue(newvalue);
}
break;
case svt_fixed:
value.fixed = fixedvalue(newvalue);
break;
case svt_mobj:
actor = actorvalue(newvalue);
break;
case svt_pInt:
*value.pI = intvalue(newvalue);
break;
case svt_pMobj:
*value.pMobj = actorvalue(newvalue);
break;
case svt_function:
script_error("attempt to set function to a value\n");
break;
default:
script_error("invalid variable type\n");
break;
}
}
//==========================================================================
//
// Archive one script variable
//
//==========================================================================
void DFsVariable::Serialize(FSerializer & ar)
{
Super::Serialize(ar);
ar("name", Name)
("type", type)
("string", string)
("actor", actor)
("value", value.i)
("next", next);
}
//==========================================================================
//
// From here: variable related functions inside DFsScript
//
//==========================================================================
//==========================================================================
//
// create a new variable in a particular script.
// returns a pointer to the new variable.
//
//==========================================================================
DFsVariable *DFsScript::NewVariable(const char *name, int vtype)
{
DFsVariable *newvar = new DFsVariable(name);
newvar->type = vtype;
int n = variable_hash(name);
newvar->next = variables[n];
variables[n] = newvar;
GC::WriteBarrier(this, newvar);
return newvar;
}
void DFsScript::NewFunction(const char *name, void (FParser::*handler)() )
{
NewVariable (name, svt_function)->value.handler = handler;
}
//==========================================================================
//
// search a particular script for a variable, which
// is returned if it exists
//
//==========================================================================
DFsVariable *DFsScript::VariableForName(const char *name)
{
int n = variable_hash(name);
DFsVariable *current = variables[n];
while(current)
{
if(!strcmp(name, current->Name)) // found it?
return current;
current = current->next; // check next in chain
}
return NULL;
}
//==========================================================================
//
// find_variable checks through the current script, level script
// and global script to try to find the variable of the name wanted
//
//==========================================================================
DFsVariable *DFsScript::FindVariable(const char *name)
{
DFsVariable *var;
DFsScript *current = this;
while(current)
{
// check this script
if ((var = current->VariableForName(name)))
return var;
current = current->parent; // try the parent of this one
}
return NULL; // no variable
}
//==========================================================================
//
// free all the variables in a given script
//
//==========================================================================
void DFsScript::ClearVariables(bool complete)
{
int i;
DFsVariable *current, *next;
for(i=0; i<VARIABLESLOTS; i++)
{
current = variables[i];
// go thru this chain
while(current)
{
// labels are added before variables, during
// preprocessing, so will be at the end of the chain
// we can be sure there are no more variables to free
if(current->type == svt_label && !complete) break;
next = current->next; // save for after freeing
current->Destroy();
current = next; // go to next in chain
}
// start of labels or NULL
variables[i] = current;
}
}
//==========================================================================
//
//
//
//==========================================================================
char *DFsScript::LabelValue(const svalue_t &v)
{
if (v.type == svt_label) return data + v.value.i;
else return NULL;
}