forked from Mirrors/openclonk
396 lines
10 KiB
C++
396 lines
10 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, Matthes Bender
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
|
|
/* Handles script file components (calls, inheritance, function maps) */
|
|
|
|
#include "C4Include.h"
|
|
#include "script/C4ScriptHost.h"
|
|
|
|
#include "object/C4Def.h"
|
|
#include "script/C4AulCompiler.h"
|
|
#include "script/C4AulParse.h"
|
|
#include "script/C4AulScriptFunc.h"
|
|
#include "script/C4AulScriptFunc.h"
|
|
#include "script/C4Effect.h"
|
|
|
|
/*--- C4ScriptHost ---*/
|
|
|
|
C4ScriptHost::C4ScriptHost()
|
|
{
|
|
Script = nullptr;
|
|
stringTable = nullptr;
|
|
SourceScripts.push_back(this);
|
|
// prepare include list
|
|
IncludesResolved = false;
|
|
Resolving=false;
|
|
Includes.clear();
|
|
Appends.clear();
|
|
}
|
|
C4ScriptHost::~C4ScriptHost()
|
|
{
|
|
Unreg();
|
|
Clear();
|
|
}
|
|
|
|
void C4ScriptHost::Clear()
|
|
{
|
|
UnlinkOwnedFunctions();
|
|
C4ComponentHost::Clear();
|
|
ast.reset();
|
|
Script.Clear();
|
|
LocalValues.Clear();
|
|
DeleteOwnedPropLists();
|
|
SourceScripts.clear();
|
|
SourceScripts.push_back(this);
|
|
if (stringTable)
|
|
{
|
|
stringTable->DelRef();
|
|
stringTable = nullptr;
|
|
}
|
|
// remove includes
|
|
Includes.clear();
|
|
Appends.clear();
|
|
// reset flags
|
|
State = ASS_NONE;
|
|
enabledWarnings.clear();
|
|
}
|
|
|
|
void C4ScriptHost::UnlinkOwnedFunctions()
|
|
{
|
|
// Remove owned functions from their parents. This solves a problem
|
|
// where overloading a definition would unload the C4ScriptHost, but
|
|
// keep around global functions, which then contained dangling pointers.
|
|
for (const auto &box : ownedFunctions)
|
|
{
|
|
assert(box.GetType() == C4V_Function);
|
|
C4AulScriptFunc *func = box._getFunction()->SFunc();
|
|
C4PropList *parent = func->Parent;
|
|
if (parent == GetPropList())
|
|
continue;
|
|
assert(parent == &::ScriptEngine);
|
|
C4Value v;
|
|
parent->GetPropertyByS(func->Name, &v);
|
|
if (v.getFunction() == func)
|
|
{
|
|
// If the function we're deleting is the top-level function in
|
|
// the inheritance chain, promote the next one in its stead;
|
|
// if there is no overloaded function, remove the property.
|
|
parent->Thaw();
|
|
if (func->OwnerOverloaded)
|
|
parent->SetPropertyByS(func->Name, C4VFunction(func->OwnerOverloaded));
|
|
else
|
|
parent->ResetProperty(func->Name);
|
|
}
|
|
else
|
|
{
|
|
C4AulScriptFunc *func_chain = v.getFunction()->SFunc();
|
|
assert(func_chain != func);
|
|
while (func_chain)
|
|
{
|
|
// Unlink the removed function from the inheritance chain
|
|
if (func_chain->OwnerOverloaded == func)
|
|
{
|
|
func_chain->OwnerOverloaded = func->OwnerOverloaded;
|
|
break;
|
|
}
|
|
assert(func_chain->OwnerOverloaded && "Removed function not found in inheritance chain");
|
|
func_chain = func_chain->OwnerOverloaded->SFunc();
|
|
}
|
|
}
|
|
}
|
|
ownedFunctions.clear();
|
|
}
|
|
|
|
void C4ScriptHost::DeleteOwnedPropLists()
|
|
{
|
|
// delete all static proplists associated to this script host.
|
|
// Note that just clearing the vector is not enough in case of
|
|
// cyclic references.
|
|
for (C4Value& value: ownedPropLists)
|
|
{
|
|
C4PropList* plist = value.getPropList();
|
|
if (plist)
|
|
{
|
|
if (plist->Delete()) delete plist;
|
|
else plist->Clear();
|
|
}
|
|
}
|
|
ownedPropLists.clear();
|
|
}
|
|
|
|
void C4ScriptHost::Unreg()
|
|
{
|
|
// remove from list
|
|
if (Prev) Prev->Next = Next; else if (Engine) Engine->Child0 = Next;
|
|
if (Next) Next->Prev = Prev; else if (Engine) Engine->ChildL = Prev;
|
|
Prev = Next = nullptr;
|
|
Engine = nullptr;
|
|
}
|
|
|
|
void C4ScriptHost::Reg2List(C4AulScriptEngine *pEngine)
|
|
{
|
|
// already regged? (def reloaded)
|
|
if (Engine) return;
|
|
// reg to list
|
|
if ((Engine = pEngine))
|
|
{
|
|
if ((Prev = Engine->ChildL))
|
|
Prev->Next = this;
|
|
else
|
|
Engine->Child0 = this;
|
|
Engine->ChildL = this;
|
|
}
|
|
else
|
|
Prev = nullptr;
|
|
Next = nullptr;
|
|
}
|
|
|
|
bool C4ScriptHost::Load(C4Group &hGroup, const char *szFilename,
|
|
const char *szLanguage, class C4LangStringTable *pLocalTable)
|
|
{
|
|
// Base load
|
|
bool fSuccess = C4ComponentHost::Load(hGroup,szFilename,szLanguage);
|
|
// String Table
|
|
if (stringTable != pLocalTable)
|
|
{
|
|
if (stringTable) stringTable->DelRef();
|
|
stringTable = pLocalTable;
|
|
if (stringTable) stringTable->AddRef();
|
|
}
|
|
// set name
|
|
ScriptName.Ref(GetFilePath());
|
|
// preparse script
|
|
MakeScript();
|
|
// Success
|
|
return fSuccess;
|
|
}
|
|
|
|
bool C4ScriptHost::LoadData(const char *szFilename, const char *szData, class C4LangStringTable *pLocalTable)
|
|
{
|
|
// String Table
|
|
if (stringTable != pLocalTable)
|
|
{
|
|
if (stringTable) stringTable->DelRef();
|
|
stringTable = pLocalTable;
|
|
if (stringTable) stringTable->AddRef();
|
|
}
|
|
ScriptName.Copy(szFilename);
|
|
|
|
StdStrBuf tempScript;
|
|
tempScript.Copy(szData);
|
|
|
|
Script.Clear();
|
|
if(stringTable)
|
|
stringTable->ReplaceStrings(tempScript, Script);
|
|
else
|
|
Script.Take(tempScript);
|
|
|
|
Preparse();
|
|
return true;
|
|
}
|
|
|
|
void C4ScriptHost::MakeScript()
|
|
{
|
|
// clear prev
|
|
Script.Clear();
|
|
|
|
// create script
|
|
if (stringTable)
|
|
{
|
|
stringTable->ReplaceStrings(GetDataBuf(), Script);
|
|
}
|
|
else
|
|
{
|
|
Script.Ref(GetDataBuf());
|
|
}
|
|
|
|
// preparse script
|
|
Preparse();
|
|
}
|
|
|
|
bool C4ScriptHost::ReloadScript(const char *szPath, const char *szLanguage)
|
|
{
|
|
// this?
|
|
if (SEqualNoCase(szPath, GetFilePath()) || (stringTable && SEqualNoCase(szPath, stringTable->GetFilePath())))
|
|
{
|
|
// try reload
|
|
char szParentPath[_MAX_PATH + 1]; C4Group ParentGrp;
|
|
if (GetParentPath(szPath, szParentPath))
|
|
if (ParentGrp.Open(szParentPath))
|
|
if (Load(ParentGrp, nullptr, szLanguage, stringTable))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string C4ScriptHost::Translate(const std::string &text) const
|
|
{
|
|
if (stringTable)
|
|
return stringTable->Translate(text);
|
|
throw C4LangStringTable::NoSuchTranslation(text);
|
|
}
|
|
|
|
/*--- C4ExtraScriptHost ---*/
|
|
|
|
C4ExtraScriptHost::C4ExtraScriptHost(C4String *parent_key_name):
|
|
ParserPropList(C4PropList::NewStatic(nullptr, nullptr, parent_key_name))
|
|
{
|
|
}
|
|
|
|
C4ExtraScriptHost::~C4ExtraScriptHost()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void C4ExtraScriptHost::Clear()
|
|
{
|
|
C4ScriptHost::Clear();
|
|
ParserPropList._getPropList()->Clear();
|
|
}
|
|
|
|
C4PropListStatic * C4ExtraScriptHost::GetPropList()
|
|
{
|
|
return ParserPropList._getPropList()->IsStatic();
|
|
}
|
|
|
|
|
|
/*--- C4ScenarioObjectsScriptHost ---*/
|
|
|
|
C4ScenarioObjectsScriptHost::C4ScenarioObjectsScriptHost() : C4ExtraScriptHost(::Strings.RegString("ScenarioObjects"))
|
|
{
|
|
// Note that "ScenarioObjects" is a fake key name under which you cannot access this prop list from script.
|
|
// It's just given to have a proper name when script errors are reported.
|
|
}
|
|
|
|
/*--- C4DefScriptHost ---*/
|
|
|
|
bool C4DefScriptHost::Parse()
|
|
{
|
|
bool r = C4ScriptHost::Parse();
|
|
assert(Def);
|
|
|
|
// Check category
|
|
if (!Def->GetPlane() && Def->Category & C4D_SortLimit)
|
|
{
|
|
int Plane; bool gotplane = true;
|
|
switch (Def->Category & C4D_SortLimit)
|
|
{
|
|
case C4D_StaticBack: Plane = 100; break;
|
|
case C4D_Structure: Plane = C4Plane_Structure; break;
|
|
case C4D_Vehicle: Plane = 300; break;
|
|
case C4D_Living: Plane = 400; break;
|
|
case C4D_Object: Plane = 500; break;
|
|
case C4D_StaticBack | C4D_Background: Plane = -500; break;
|
|
case C4D_Structure | C4D_Background: Plane = -400; break;
|
|
case C4D_Vehicle | C4D_Background: Plane = -300; break;
|
|
case C4D_Living | C4D_Background: Plane = -200; break;
|
|
case C4D_Object | C4D_Background: Plane = -100; break;
|
|
case C4D_StaticBack | C4D_Foreground: Plane = 1100; break;
|
|
case C4D_Structure | C4D_Foreground: Plane = 1200; break;
|
|
case C4D_Vehicle | C4D_Foreground: Plane = 1300; break;
|
|
case C4D_Living | C4D_Foreground: Plane = 1400; break;
|
|
case C4D_Object | C4D_Foreground: Plane = 1500; break;
|
|
default:
|
|
Warn("Def %s (%s) has invalid category", Def->GetName(), Def->GetDataString().getData());
|
|
gotplane = false;
|
|
break;
|
|
}
|
|
if (gotplane) Def->SetProperty(P_Plane, C4VInt(Plane));
|
|
}
|
|
if (!Def->GetPlane())
|
|
{
|
|
Warn("Def %s (%s) has invalid Plane", Def->GetName(), Def->GetDataString().getData());
|
|
Def->SetProperty(P_Plane, C4VInt(1));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
C4PropListStatic * C4DefScriptHost::GetPropList() { return Def; }
|
|
|
|
class C4PropListScen: public C4PropListStatic
|
|
{
|
|
public:
|
|
C4PropListScen(const C4PropListStatic * parent, C4String * key): C4PropListStatic(nullptr, parent, key)
|
|
{
|
|
C4PropList * proto = C4PropList::NewStatic(ScriptEngine.GetPropList(), this, &::Strings.P[P_Prototype]);
|
|
C4PropListStatic::SetPropertyByS(&::Strings.P[P_Prototype], C4VPropList(proto));
|
|
}
|
|
void SetPropertyByS(C4String * k, const C4Value & to) override
|
|
{
|
|
if (k == &Strings.P[P_Prototype])
|
|
{
|
|
DebugLog("ERROR: Attempt to set Scenario.Prototype.");
|
|
return;
|
|
}
|
|
C4PropListStatic::SetPropertyByS(k, to);
|
|
}
|
|
};
|
|
|
|
/*--- C4GameScriptHost ---*/
|
|
|
|
C4GameScriptHost::C4GameScriptHost(): ScenPrototype(0), ScenPropList(0) { }
|
|
C4GameScriptHost::~C4GameScriptHost() = default;
|
|
|
|
bool C4GameScriptHost::Load(C4Group & g, const char * f, const char * l, C4LangStringTable * t)
|
|
{
|
|
assert(ScriptEngine.GetPropList());
|
|
C4PropListStatic * pScen = new C4PropListScen(nullptr, &::Strings.P[P_Scenario]);
|
|
ScenPropList.SetPropList(pScen);
|
|
::ScriptEngine.RegisterGlobalConstant("Scenario", ScenPropList);
|
|
ScenPrototype.SetPropList(pScen->GetPrototype());
|
|
Reg2List(&ScriptEngine);
|
|
return C4ScriptHost::Load(g, f, l, t);
|
|
}
|
|
|
|
bool C4GameScriptHost::LoadData(const char * f, const char * d, C4LangStringTable * t)
|
|
{
|
|
assert(ScriptEngine.GetPropList());
|
|
C4PropListStatic * pScen = new C4PropListScen(nullptr, &::Strings.P[P_Scenario]);
|
|
ScenPropList.SetPropList(pScen);
|
|
::ScriptEngine.RegisterGlobalConstant("Scenario", ScenPropList);
|
|
ScenPrototype.SetPropList(pScen->GetPrototype());
|
|
Reg2List(&ScriptEngine);
|
|
return C4ScriptHost::LoadData(f, d, t);
|
|
}
|
|
|
|
void C4GameScriptHost::Clear()
|
|
{
|
|
C4ScriptHost::Clear();
|
|
ScenPropList.Set0();
|
|
ScenPrototype.Set0();
|
|
delete pScenarioEffects; pScenarioEffects=nullptr;
|
|
}
|
|
|
|
C4PropListStatic * C4GameScriptHost::GetPropList()
|
|
{
|
|
C4PropList * p = ScenPrototype._getPropList();
|
|
return p ? p->IsStatic() : nullptr;
|
|
}
|
|
|
|
void C4GameScriptHost::Denumerate(C4ValueNumbers * numbers)
|
|
{
|
|
ScenPropList.Denumerate(numbers);
|
|
if (pScenarioEffects) pScenarioEffects->Denumerate(numbers);
|
|
}
|
|
|
|
C4Value C4GameScriptHost::Call(const char *szFunction, C4AulParSet *Pars, bool fPassError)
|
|
{
|
|
return ScenPropList._getPropList()->Call(szFunction, Pars, fPassError);
|
|
}
|
|
|
|
C4GameScriptHost GameScript;
|