From fb76308812be036964030f0ff07ef296ad8dd0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 3 Feb 2016 03:33:17 +0100 Subject: [PATCH 01/25] Make C4ValueConv::Type a constexpr variable instead of an inline function --- src/script/C4AulDefFunc.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/script/C4AulDefFunc.h b/src/script/C4AulDefFunc.h index 35b44bc0e..bf2494f40 100644 --- a/src/script/C4AulDefFunc.h +++ b/src/script/C4AulDefFunc.h @@ -49,7 +49,7 @@ class Nillable bool _nil; T _val; public: - inline Nillable(const T &value) : _nil(!value && !C4Value::IsNullableType(C4ValueConv::Type())), _val(value) {} + inline Nillable(const T &value) : _nil(!value && !C4Value::IsNullableType(C4ValueConv::Type)), _val(value) {} inline Nillable() : _nil(true), _val(T()) {} inline Nillable(std::nullptr_t) : _nil(true), _val(T()) {} template inline Nillable(const Nillable & n2) : _nil(n2._nil), _val(n2._val) {} @@ -59,7 +59,7 @@ public: inline Nillable &operator =(const T &val) { _val = val; - _nil = !val && !C4Value::IsNullableType(C4ValueConv::Type()); + _nil = !val && !C4Value::IsNullableType(C4ValueConv::Type); return *this; } inline Nillable &operator =(const Nillable &val) @@ -140,71 +140,71 @@ template struct C4ValueConv > { inline static Nillable _FromC4V(C4Value &v) { if (v.GetType() == C4V_Nil) return C4Void(); else return C4ValueConv::_FromC4V(v); } - inline static C4V_Type Type() { return C4ValueConv::Type(); } + static constexpr C4V_Type Type = C4ValueConv::Type; }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Nil; } + static constexpr C4V_Type Type = C4V_Nil; }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Int; } + static constexpr C4V_Type Type = C4V_Int; inline static int _FromC4V(C4Value &v) { return v._getInt(); } }; template <> struct C4ValueConv: public C4ValueConv { }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Bool; } + static constexpr C4V_Type Type = C4V_Bool; inline static bool _FromC4V(C4Value &v) { return v._getBool(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_PropList; } + static constexpr C4V_Type Type = C4V_PropList; inline static C4ID _FromC4V(C4Value &v) { C4Def * def = v.getDef(); return def ? def->id : C4ID::None; } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Object; } + static constexpr C4V_Type Type = C4V_Object; inline static C4Object *_FromC4V(C4Value &v) { return v._getObj(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_String; } + static constexpr C4V_Type Type = C4V_String; inline static C4String *_FromC4V(C4Value &v) { return v._getStr(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Array; } + static constexpr C4V_Type Type = C4V_Array; inline static C4ValueArray *_FromC4V(C4Value &v) { return v._getArray(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Function; } + static constexpr C4V_Type Type = C4V_Function; inline static C4AulFunc *_FromC4V(C4Value &v) { return v._getFunction(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_PropList; } + static constexpr C4V_Type Type = C4V_PropList; inline static C4PropList *_FromC4V(C4Value &v) { return v._getPropList(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Effect; } + static constexpr C4V_Type Type = C4V_Effect; inline static C4Effect *_FromC4V(C4Value &v) { C4PropList * p = v._getPropList(); return p ? p->GetEffect() : 0; } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Def; } + static constexpr C4V_Type Type = C4V_Def; inline static C4Def *_FromC4V(C4Value &v) { return v._getDef(); } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Any; } + static constexpr C4V_Type Type = C4V_Any; inline static const C4Value &_FromC4V(C4Value &v) { return v; } }; template <> struct C4ValueConv { - inline static C4V_Type Type() { return C4V_Any; } + static constexpr C4V_Type Type = C4V_Any; inline static C4Value _FromC4V(C4Value &v) { return v; } }; @@ -218,7 +218,7 @@ public: C4AulEngineFunc(C4PropListStatic * Parent, const char *pName, Func pFunc, bool Public): C4AulFunc(Parent, pName), - pFunc(pFunc), ParType {C4ValueConv::Type()...}, Public(Public) + pFunc(pFunc), ParType {C4ValueConv::Type...}, Public(Public) { Parent->SetPropertyByS(Name, C4VFunction(this)); for(int i = GetParCount(); i < C4AUL_MAX_Par; ++i) @@ -237,7 +237,7 @@ public: virtual C4V_Type GetRetType() const { - return C4ValueConv::Type(); + return C4ValueConv::Type; } virtual bool GetPublic() const From 55572720bc24f8e144016c2c67141039d24886d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 3 Feb 2016 04:11:36 +0100 Subject: [PATCH 02/25] Make the C4ScriptHost a ComponentHost again --- src/script/C4ScriptHost.cpp | 12 ++++++------ src/script/C4ScriptHost.h | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/script/C4ScriptHost.cpp b/src/script/C4ScriptHost.cpp index 5212e1114..bd79a6615 100644 --- a/src/script/C4ScriptHost.cpp +++ b/src/script/C4ScriptHost.cpp @@ -48,7 +48,7 @@ C4ScriptHost::~C4ScriptHost() void C4ScriptHost::Clear() { - ComponentHost.Clear(); + C4ComponentHost::Clear(); Script.Clear(); LocalNamed.Reset(); LocalValues.Clear(); @@ -97,7 +97,7 @@ bool C4ScriptHost::Load(C4Group &hGroup, const char *szFilename, const char *szLanguage, class C4LangStringTable *pLocalTable) { // Base load - bool fSuccess = ComponentHost.Load(hGroup,szFilename,szLanguage); + bool fSuccess = C4ComponentHost::Load(hGroup,szFilename,szLanguage); // String Table if (stringTable != pLocalTable) { @@ -106,7 +106,7 @@ bool C4ScriptHost::Load(C4Group &hGroup, const char *szFilename, if (stringTable) stringTable->AddRef(); } // set name - ScriptName.Ref(ComponentHost.GetFilePath()); + ScriptName.Ref(GetFilePath()); // preparse script MakeScript(); // Success @@ -145,11 +145,11 @@ void C4ScriptHost::MakeScript() // create script if (stringTable) { - stringTable->ReplaceStrings(ComponentHost.GetDataBuf(), Script); + stringTable->ReplaceStrings(GetDataBuf(), Script); } else { - Script.Ref(ComponentHost.GetDataBuf()); + Script.Ref(GetDataBuf()); } // preparse script @@ -159,7 +159,7 @@ void C4ScriptHost::MakeScript() bool C4ScriptHost::ReloadScript(const char *szPath, const char *szLanguage) { // this? - if (SEqualNoCase(szPath, ComponentHost.GetFilePath()) || (stringTable && SEqualNoCase(szPath, stringTable->GetFilePath()))) + if (SEqualNoCase(szPath, GetFilePath()) || (stringTable && SEqualNoCase(szPath, stringTable->GetFilePath()))) { // try reload char szParentPath[_MAX_PATH + 1]; C4Group ParentGrp; diff --git a/src/script/C4ScriptHost.h b/src/script/C4ScriptHost.h index 2054c2056..ddaa6914f 100644 --- a/src/script/C4ScriptHost.h +++ b/src/script/C4ScriptHost.h @@ -33,7 +33,7 @@ enum C4AulScriptState }; // generic script host for objects -class C4ScriptHost +class C4ScriptHost: public C4ComponentHost { public: virtual ~C4ScriptHost(); @@ -56,7 +56,6 @@ protected: void Unreg(); // remove from list void MakeScript(); virtual bool ReloadScript(const char *szPath, const char *szLanguage); - C4ComponentHost ComponentHost; bool Preparse(); // preparse script; return if successfull virtual bool Parse(); // parse preparsed script; return if successfull From 5fbac346d389b87cbb26007964230b6943f13e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Thu, 4 Feb 2016 00:45:20 +0100 Subject: [PATCH 03/25] Incompatible type warnings work for parameters with type declarations --- src/script/C4AulParse.cpp | 9 +++++++++ tests/aul/AulTest.cpp | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 244a8402e..b860d6388 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -1057,6 +1057,15 @@ C4V_Type C4AulParse::GetLastRetType(C4V_Type to) case AB_Not: case AB_LessThan: case AB_LessThanEqual: case AB_GreaterThan: case AB_GreaterThanEqual: case AB_Equal: case AB_NotEqual: from = C4V_Bool; break; + case AB_DUP: + { + int pos = Fn->GetLastCode()->Par.i + iStack - 2 + Fn->VarNamed.iSize + Fn->GetParCount(); + if (pos < Fn->GetParCount()) + from = Fn->GetParType()[pos]; + else + from = C4V_Any; + break; + } default: from = C4V_Any; break; } diff --git a/tests/aul/AulTest.cpp b/tests/aul/AulTest.cpp index 358ce5f5e..b7d0be6c8 100644 --- a/tests/aul/AulTest.cpp +++ b/tests/aul/AulTest.cpp @@ -22,6 +22,7 @@ #include "script/C4ScriptHost.h" #include "lib/C4Random.h" #include "object/C4DefList.h" +#include "TestLog.h" C4Value AulTest::RunCode(const char *code, bool wrap) { @@ -129,3 +130,19 @@ TEST_F(AulTest, Vars) EXPECT_EQ(C4VInt(42), RunCode("var i = 21; i = i + i; return i;")); EXPECT_EQ(C4VInt(42), RunCode("var i = -42; i = Abs(i); return i;")); } + +TEST_F(AulTest, Warnings) +{ + LogMock log; + EXPECT_CALL(log, DebugLog(testing::StartsWith("WARNING:"))).Times(3); + EXPECT_EQ(C4Value(), RunCode("func Main(string s, object o, array a) { Sin(s); }", false)); + EXPECT_EQ(C4Value(), RunCode("func Main(string s, object o, array a) { Sin(o); }", false)); + EXPECT_EQ(C4Value(), RunCode("func Main(string s, object o, array a) { Sin(a); }", false)); +} + +TEST_F(AulTest, NoWarnings) +{ + LogMock log; + EXPECT_CALL(log, DebugLog(testing::StartsWith("WARNING:"))).Times(0); + EXPECT_EQ(C4Value(), RunCode("func Main(string s, object o, array a) { var x; Sin(x); }", false)); +} From e22ca51f722dbdae61fe0514de3727e36b90f4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Thu, 4 Feb 2016 01:23:07 +0100 Subject: [PATCH 04/25] Don't leak references to Strings on script errors (#583) --- src/script/C4AulParse.cpp | 24 ++++++------- src/script/C4AulScriptFunc.cpp | 28 +-------------- src/script/C4AulScriptFunc.h | 62 ++++++++++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 41 deletions(-) diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index b860d6388..3a5999da3 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -1074,7 +1074,7 @@ C4V_Type C4AulParse::GetLastRetType(C4V_Type to) C4AulBCC C4AulParse::MakeSetter(bool fLeaveValue) { - if(Type != PARSER) { C4AulBCC Dummy; Dummy.bccType = AB_ERR; return Dummy; } + if(Type != PARSER) { return C4AulBCC(AB_ERR, 0); } C4AulBCC Value = *(Fn->GetLastCode()), Setter = Value; // Check type switch (Value.bccType) @@ -1089,11 +1089,9 @@ C4AulBCC C4AulParse::MakeSetter(bool fLeaveValue) case AB_STACK_SET: Setter.bccType = AB_STACK_SET; break; case AB_LOCALN: Setter.bccType = AB_LOCALN_SET; - Setter.Par.s->IncRef(); // so string isn't dropped by RemoveLastBCC, see also C4AulScript::AddBCC break; case AB_PROP: Setter.bccType = AB_PROP_SET; - Setter.Par.s->IncRef(); // so string isn't dropped by RemoveLastBCC, see also C4AulScript::AddBCC break; case AB_GLOBALN: Setter.bccType = AB_GLOBALN_SET; break; default: @@ -2390,15 +2388,17 @@ void C4AulParse::Parse_Expression(int iParentPrio) Fn->GetLastCode()->Par.i = - Fn->GetLastCode()->Par.i; break; } - // changer? make a setter BCC, leave value for operator - C4AulBCC Changer; - if(op->Changer) - Changer = MakeSetter(true); - // write byte code - AddBCC(op->Code, 0); - // writter setter - if(op->Changer) - AddBCC(Changer.bccType, Changer.Par.X); + { + // changer? make a setter BCC, leave value for operator + C4AulBCC Changer; + if(op->Changer) + Changer = MakeSetter(true); + // write byte code + AddBCC(op->Code, 0); + // writter setter + if(op->Changer) + AddBCC(Changer.bccType, Changer.Par.X); + } break; case ATT_BOPEN: Shift(); diff --git a/src/script/C4AulScriptFunc.cpp b/src/script/C4AulScriptFunc.cpp index 184ba8109..a4ce8da5e 100644 --- a/src/script/C4AulScriptFunc.cpp +++ b/src/script/C4AulScriptFunc.cpp @@ -62,38 +62,12 @@ void C4AulScriptFunc::SetOverloaded(C4AulFunc * f) void C4AulScriptFunc::AddBCC(C4AulBCCType eType, intptr_t X, const char * SPos) { // store chunk - C4AulBCC bcc; - bcc.bccType = eType; - bcc.Par.X = X; - Code.push_back(bcc); + Code.emplace_back(eType, X); PosForCode.push_back(SPos); - - switch (eType) - { - case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_PROP: - /* case AB_LOCALN_SET/AB_PROP_SET: -- expected to already have a reference upon creation, see MakeSetter */ - bcc.Par.s->IncRef(); - break; - case AB_CARRAY: - bcc.Par.a->IncRef(); - break; - default: break; - } } void C4AulScriptFunc::RemoveLastBCC() { - C4AulBCC *pBCC = &Code.back(); - switch (pBCC->bccType) - { - case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET: - pBCC->Par.s->DecRef(); - break; - case AB_CARRAY: - pBCC->Par.a->DecRef(); - break; - default: break; - } Code.pop_back(); PosForCode.pop_back(); } diff --git a/src/script/C4AulScriptFunc.h b/src/script/C4AulScriptFunc.h index 975db4f48..58264d0e7 100644 --- a/src/script/C4AulScriptFunc.h +++ b/src/script/C4AulScriptFunc.h @@ -93,18 +93,76 @@ enum C4AulBCCType }; // byte code chunk -struct C4AulBCC +class C4AulBCC { +public: C4AulBCCType bccType; // chunk type union { + intptr_t X; int32_t i; C4String * s; C4PropList * p; C4ValueArray * a; C4AulFunc * f; - intptr_t X; } Par; // extra info + C4AulBCC(): bccType(AB_ERR) { } + C4AulBCC(C4AulBCCType bccType, intptr_t X): bccType(bccType), Par{X} + { + IncRef(); + } + C4AulBCC(const C4AulBCC & from): C4AulBCC(from.bccType, from.Par.X) { } + C4AulBCC & operator = (const C4AulBCC & from) + { + DecRef(); + bccType = from.bccType; + Par = from.Par; + IncRef(); + return *this; + } + C4AulBCC(C4AulBCC && from): bccType(from.bccType), Par(from.Par) + { + from.bccType = AB_ERR; + } + C4AulBCC & operator = (C4AulBCC && from) + { + DecRef(); + bccType = from.bccType; + Par = from.Par; + from.bccType = AB_ERR; + return *this; + } + ~C4AulBCC() + { + DecRef(); + } +private: + void IncRef() + { + switch (bccType) + { + case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET: + Par.s->IncRef(); + break; + case AB_CARRAY: + Par.a->IncRef(); + break; + default: break; + } + } + void DecRef() + { + switch (bccType) + { + case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET: + Par.s->DecRef(); + break; + case AB_CARRAY: + Par.a->DecRef(); + break; + default: break; + } + } }; // script function class From 632c5cbd0fa33c3e624204cb6973bb9321d17d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Tue, 22 Dec 2015 21:50:35 +0100 Subject: [PATCH 05/25] Refactor effect callbacks into helper functions --- src/game/C4GameScript.cpp | 3 +- src/script/C4Effect.cpp | 91 ++++++++++++++++++++++++--------------- src/script/C4Effect.h | 6 +++ 3 files changed, 63 insertions(+), 37 deletions(-) diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index 512ef13ba..ce5325f9d 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -2234,8 +2234,7 @@ static C4Value FnEffectCall(C4PropList * _this, C4Value * Pars) // evaluate parameters C4Object *pTarget = Pars[0].getObj(); C4Effect * pEffect = Pars[1].getPropList() ? Pars[1].getPropList()->GetEffect() : 0; - C4String *psCallFn = Pars[2].getStr(); - const char *szCallFn = FnStringPar(psCallFn); + const char *szCallFn = FnStringPar(Pars[2].getStr()); // safety if (pTarget && !pTarget->Status) return C4Value(); if (!szCallFn || !*szCallFn) return C4Value(); diff --git a/src/script/C4Effect.cpp b/src/script/C4Effect.cpp index 751635aca..59b42a4c4 100644 --- a/src/script/C4Effect.cpp +++ b/src/script/C4Effect.cpp @@ -119,10 +119,9 @@ C4Effect * C4Effect::New(C4Object * pForObj, C4String * szName, int32_t iPrio, i // bad things may happen if (pForObj && !pForObj->Status) return 0; // this will be invalid! pEffect->iPriority = iPrio; // validate effect now - if (pEffect->pFnStart) - if (pEffect->pFnStart->Exec(pCmdTarget, &C4AulParSet(C4VObj(pForObj), C4VPropList(pEffect), C4VInt(0), rVal1, rVal2, rVal3, rVal4)).getInt() == C4Fx_Start_Deny) - // the effect denied to start: assume it hasn't, and mark it dead - pEffect->SetDead(); + if (pEffect->CallStart(pForObj, 0, rVal1, rVal2, rVal3, rVal4) == C4Fx_Start_Deny) + // the effect denied to start: assume it hasn't, and mark it dead + pEffect->SetDead(); if (fRemoveUpper && pEffect->pNext && pEffect->pFnStart) pEffect->TempReaddUpperEffects(pForObj, pLastRemovedEffect); if (pForObj && !pForObj->Status) return 0; // this will be invalid! @@ -229,9 +228,9 @@ C4Effect* C4Effect::Check(C4Object *pForObj, const char *szCheckEffect, int32_t C4Effect *pLastRemovedEffect=NULL; for (C4Effect *pCheck = this; pCheck; pCheck = pCheck->pNext) { - if (!pCheck->IsDead() && pCheck->pFnEffect && pCheck->iPriority >= iPrio) + if (!pCheck->IsDead() && pCheck->iPriority >= iPrio) { - int32_t iResult = pCheck->pFnEffect->Exec(pCheck->CommandTarget, &C4AulParSet(C4VString(szCheckEffect), C4VObj(pForObj), C4VPropList(pCheck), rVal1, rVal2, rVal3, rVal4)).getInt(); + int32_t iResult = pCheck->CallEffect(szCheckEffect, pForObj, rVal1, rVal2, rVal3, rVal4); if (iResult == C4Fx_Effect_Deny) // effect denied return (C4Effect*)C4Fx_Effect_Deny; @@ -293,21 +292,15 @@ void C4Effect::Execute(C4Object *pObj) // check timer execution if (pEffect->iInterval && !(pEffect->iTime % pEffect->iInterval)) { - if (pEffect->pFnTimer) + if (pEffect->CallTimer(pObj, pEffect->iTime) == C4Fx_Execute_Kill) { - if (pEffect->pFnTimer->Exec(pEffect->CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(pEffect), C4VInt(pEffect->iTime))).getInt() == C4Fx_Execute_Kill) - { - // safety: this class got deleted! - if (pObj && !pObj->Status) return; - // timer function decided to finish it - pEffect->Kill(pObj); - } // safety: this class got deleted! if (pObj && !pObj->Status) return; - } - else - // no timer function: mark dead after time elapsed + // timer function decided to finish it pEffect->Kill(pObj); + } + // safety: this class got deleted! + if (pObj && !pObj->Status) return; } // next effect ppPrevEffect = &pEffect->pNext; @@ -327,13 +320,12 @@ void C4Effect::Kill(C4Object *pObj) else // otherwise: temp reactivate before real removal // this happens only if a lower priority effect removes an upper priority effect in its add- or removal-call - if (pFnStart && iPriority!=1) pFnStart->Exec(CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(this), C4VInt(C4FxCall_TempAddForRemoval))); + if (iPriority!=1) CallStart(pObj, C4FxCall_TempAddForRemoval, C4Value(), C4Value(), C4Value(), C4Value()); // remove this effect int32_t iPrevPrio = iPriority; SetDead(); - if (pFnStop) - if (pFnStop->Exec(CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(this), C4VInt(C4FxCall_Normal))).getInt() == C4Fx_Stop_Deny) - // effect denied to be removed: recover - iPriority = iPrevPrio; + if (CallStop(pObj, C4FxCall_Normal, false) == C4Fx_Stop_Deny) + // effect denied to be removed: recover + iPriority = iPrevPrio; // reactivate other effects TempReaddUpperEffects(pObj, pLastRemovedEffect); // Update OnFire cache @@ -351,15 +343,14 @@ void C4Effect::ClearAll(C4Object *pObj, int32_t iClearFlag) if ((pObj && !pObj->Status) || IsDead()) return; int32_t iPrevPrio = iPriority; SetDead(); - if (pFnStop) - if (pFnStop->Exec(CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(this), C4VInt(iClearFlag))).getInt() == C4Fx_Stop_Deny) - { - // this stop-callback might have deleted the object and then denied its own removal - // must not modify self in this case... - if (pObj && !pObj->Status) return; - // effect denied to be removed: recover it - iPriority = iPrevPrio; - } + if (CallStop(pObj, iClearFlag, false) == C4Fx_Stop_Deny) + { + // this stop-callback might have deleted the object and then denied its own removal + // must not modify self in this case... + if (pObj && !pObj->Status) return; + // effect denied to be removed: recover it + iPriority = iPrevPrio; + } // Update OnFire cache if (pObj && WildcardMatch(C4Fx_AnyFire, GetName()) && IsDead()) if (!Get(C4Fx_AnyFire)) @@ -372,8 +363,8 @@ void C4Effect::DoDamage(C4Object *pObj, int32_t &riDamage, int32_t iDamageType, C4Effect *pEff = this; do { - if (!pEff->IsDead() && pEff->pFnDamage) - riDamage = pEff->pFnDamage->Exec(pEff->CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(pEff), C4VInt(riDamage), C4VInt(iDamageType), C4VInt(iCausePlr))).getInt(); + if (!pEff->IsDead()) + pEff->CallDamage(pObj, riDamage, iDamageType, iCausePlr); if (pObj && !pObj->Status) return; } while ((pEff = pEff->pNext) && riDamage); @@ -389,6 +380,36 @@ C4Value C4Effect::DoCall(C4Object *pObj, const char *szFn, const C4Value &rVal1, return p->Call(fn, &C4AulParSet(pObj, this, rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); } +int C4Effect::CallStart(C4Object * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) +{ + if (pFnStart) + return pFnStart->Exec(CommandTarget, &C4AulParSet(obj, this, temporary, var1, var2, var3, var4)).getInt(); + return C4Fx_OK; +} +int C4Effect::CallStop(C4Object * obj, int reason, bool temporary) +{ + if (pFnStop) + return pFnStop->Exec(CommandTarget, &C4AulParSet(obj, this, reason, temporary)).getInt(); + return C4Fx_OK; +} +int C4Effect::CallTimer(C4Object * obj, int time) +{ + if (pFnTimer) + return pFnTimer->Exec(CommandTarget, &C4AulParSet(obj, this, time)).getInt(); + return C4Fx_Execute_Kill; +} +void C4Effect::CallDamage(C4Object * obj, int32_t & damage, int damagetype, int plr) +{ + if (pFnDamage) + damage = pFnDamage->Exec(CommandTarget, &C4AulParSet(obj, this, damage, damagetype, plr)).getInt(); +} +int C4Effect::CallEffect(const char * effect, C4Object * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) +{ + if (pFnEffect) + return pFnEffect->Exec(CommandTarget, &C4AulParSet(effect, obj, this, var1, var2, var3, var4)).getInt(); + return C4Fx_OK; +} + void C4Effect::OnObjectChangedDef(C4Object *pObj) { // safety @@ -423,7 +444,7 @@ void C4Effect::TempRemoveUpperEffects(C4Object *pObj, bool fTempRemoveThis, C4Ef if (!Get(C4Fx_AnyFire)) pObj->SetOnFire(false); // temp callbacks only for higher priority effects - if (pFnStop && iPriority!=1) pFnStop->Exec(CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(this), C4VInt(C4FxCall_Temp), C4VBool(true))); + if (iPriority!=1) CallStop(pObj, C4FxCall_Temp, true); if (!*ppLastRemovedEffect) *ppLastRemovedEffect = this; } } @@ -439,7 +460,7 @@ void C4Effect::TempReaddUpperEffects(C4Object *pObj, C4Effect *pLastReaddEffect) if (pEff->IsInactiveAndNotDead()) { pEff->FlipActive(); - if (pEff->pFnStart && pEff->iPriority!=1) pEff->pFnStart->Exec(pEff->CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(pEff), C4VInt(C4FxCall_Temp))); + if (pEff->iPriority!=1) pEff->CallStart(pObj, C4FxCall_Temp, C4Value(), C4Value(), C4Value(), C4Value()); if (pObj && WildcardMatch(C4Fx_AnyFire, pEff->GetName())) pObj->SetOnFire(true); } diff --git a/src/script/C4Effect.h b/src/script/C4Effect.h index 730e02960..5a4df201d 100644 --- a/src/script/C4Effect.h +++ b/src/script/C4Effect.h @@ -87,6 +87,12 @@ protected: void AssignCallbackFunctions(); // resolve callback function names + int CallStart(C4Object * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); + int CallStop(C4Object * obj, int reason, bool temporary); + int CallTimer(C4Object * obj, int time); + void CallDamage(C4Object * obj, int32_t & damage, int damagetype, int plr); + int CallEffect(const char * effect, C4Object * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); + C4Effect(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4Object * pCmdTarget, C4ID idCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); C4Effect(const C4Effect &); // unimplemented, do not use C4Effect(); // for the StdCompiler From bfb9b6b1fdf75456a72816aafe699b2067fd3eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Fri, 18 Dec 2015 23:11:58 +0100 Subject: [PATCH 06/25] Drop the roundtrip through C4ID for the effect C4Def command target Unfortunately, this complicates the C4Effect::CompileFunc to stay compatible. --- src/game/C4GameScript.cpp | 7 +++- src/script/C4Effect.cpp | 86 ++++++++++++++++++++------------------- src/script/C4Effect.h | 13 +++--- src/script/C4Value.h | 1 + 4 files changed, 56 insertions(+), 51 deletions(-) diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index ce5325f9d..09af0a2cb 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -2145,14 +2145,17 @@ static long FnLoadScenarioSection(C4PropList * _this, C4String *pstrSection, lon } static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4Object * pTarget, - int iPrio, int iTimerInterval, C4Object * pCmdTarget, C4ID idCmdTarget, + int iPrio, int iTimerInterval, C4Object * pCmdTarget, C4Def * idCmdTarget, const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) { // safety if (pTarget && !pTarget->Status) return C4Value(); if (!szEffect || !*szEffect->GetCStr() || !iPrio) return C4Value(); // create effect - C4Effect * pEffect = C4Effect::New(pTarget, szEffect, iPrio, iTimerInterval, pCmdTarget, idCmdTarget, Val1, Val2, Val3, Val4); + C4PropList * p = pCmdTarget; + if (!p) p = idCmdTarget; + if (!p) p = ::ScriptEngine.GetPropList(); + C4Effect * pEffect = C4Effect::New(pTarget, szEffect, iPrio, iTimerInterval, p, Val1, Val2, Val3, Val4); // return effect - may be 0 if the effect has been denied by another effect if (!pEffect) return C4Value(); return C4VPropList(pEffect); diff --git a/src/script/C4Effect.cpp b/src/script/C4Effect.cpp index 59b42a4c4..8941af691 100644 --- a/src/script/C4Effect.cpp +++ b/src/script/C4Effect.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -39,27 +38,16 @@ void C4Effect::AssignCallbackFunctions() C4PropList * C4Effect::GetCallbackScript() { - C4Def *pDef; - if (CommandTarget) - { - // overwrite ID for sync safety in runtime join - idCommandTarget = CommandTarget->id; - return CommandTarget; - } - else if (idCommandTarget && (pDef=::Definitions.ID2Def(idCommandTarget))) - return pDef; - else - return ::ScriptEngine.GetPropList(); + return CommandTarget._getPropList(); } -C4Effect::C4Effect(C4Object *pForObj, C4String *szName, int32_t iPrio, int32_t iTimerInterval, C4Object *pCmdTarget, C4ID idCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) +C4Effect::C4Effect(C4Object *pForObj, C4String *szName, int32_t iPrio, int32_t iTimerInterval, C4PropList *pCmdTarget) { // assign values iPriority = 0; // effect is not yet valid; some callbacks to other effects are done before iInterval = iTimerInterval; iTime = 0; - CommandTarget = pCmdTarget; - idCommandTarget = idCmdTarget; + CommandTarget.SetPropList(pCmdTarget); AcquireNumber(); Register(pForObj, iPrio); // Set name and callback functions @@ -87,9 +75,9 @@ void C4Effect::Register(C4Object *pForObj, int32_t iPrio) } } -C4Effect * C4Effect::New(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4Object * pCmdTarget, C4ID idCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) +C4Effect * C4Effect::New(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) { - C4Effect * pEffect = new C4Effect(pForObj, szName, iPrio, iTimerInterval, pCmdTarget, idCmdTarget, rVal1, rVal2, rVal3, rVal4); + C4Effect * pEffect = new C4Effect(pForObj, szName, iPrio, iTimerInterval, pCmdTarget); // ask all effects with higher priority first - except for prio 1 effects, which are considered out of the priority call chain (as per doc) bool fRemoveUpper = (iPrio != 1); // note that apart from denying the creation of this effect, higher priority effects may also remove themselves @@ -135,7 +123,7 @@ C4Effect::C4Effect() { // defaults iPriority=iTime=iInterval=0; - CommandTarget=NULL; + CommandTarget.Set0(); pNext = NULL; } @@ -158,7 +146,7 @@ void C4Effect::Denumerate(C4ValueNumbers * numbers) do { // command target - pEff->CommandTarget.DenumeratePointers(); + pEff->CommandTarget.Denumerate(numbers); // assign any callback functions pEff->AssignCallbackFunctions(); pEff->C4PropList::Denumerate(numbers); @@ -166,16 +154,16 @@ void C4Effect::Denumerate(C4ValueNumbers * numbers) while ((pEff=pEff->pNext)); } -void C4Effect::ClearPointers(C4Object *pObj) +void C4Effect::ClearPointers(C4PropList *pObj) { // clear pointers in all effects C4Effect *pEff = this; do // command target lost: effect dead w/o callback - if (pEff->CommandTarget == pObj) + if (pEff->CommandTarget.getPropList() == pObj) { pEff->SetDead(); - pEff->CommandTarget=NULL; + pEff->CommandTarget.Set0(); } while ((pEff=pEff->pNext)); } @@ -383,34 +371,34 @@ C4Value C4Effect::DoCall(C4Object *pObj, const char *szFn, const C4Value &rVal1, int C4Effect::CallStart(C4Object * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { if (pFnStart) - return pFnStart->Exec(CommandTarget, &C4AulParSet(obj, this, temporary, var1, var2, var3, var4)).getInt(); + return pFnStart->Exec(GetCallbackScript(), &C4AulParSet(obj, this, temporary, var1, var2, var3, var4)).getInt(); return C4Fx_OK; } int C4Effect::CallStop(C4Object * obj, int reason, bool temporary) { if (pFnStop) - return pFnStop->Exec(CommandTarget, &C4AulParSet(obj, this, reason, temporary)).getInt(); + return pFnStop->Exec(GetCallbackScript(), &C4AulParSet(obj, this, reason, temporary)).getInt(); return C4Fx_OK; } int C4Effect::CallTimer(C4Object * obj, int time) { if (pFnTimer) - return pFnTimer->Exec(CommandTarget, &C4AulParSet(obj, this, time)).getInt(); + return pFnTimer->Exec(GetCallbackScript(), &C4AulParSet(obj, this, time)).getInt(); return C4Fx_Execute_Kill; } void C4Effect::CallDamage(C4Object * obj, int32_t & damage, int damagetype, int plr) { if (pFnDamage) - damage = pFnDamage->Exec(CommandTarget, &C4AulParSet(obj, this, damage, damagetype, plr)).getInt(); + damage = pFnDamage->Exec(GetCallbackScript(), &C4AulParSet(obj, this, damage, damagetype, plr)).getInt(); } int C4Effect::CallEffect(const char * effect, C4Object * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { if (pFnEffect) - return pFnEffect->Exec(CommandTarget, &C4AulParSet(effect, obj, this, var1, var2, var3, var4)).getInt(); + return pFnEffect->Exec(GetCallbackScript(), &C4AulParSet(effect, obj, this, var1, var2, var3, var4)).getInt(); return C4Fx_OK; } -void C4Effect::OnObjectChangedDef(C4Object *pObj) +void C4Effect::OnObjectChangedDef(C4PropList *pObj) { // safety if (!pObj) return; @@ -418,7 +406,7 @@ void C4Effect::OnObjectChangedDef(C4Object *pObj) C4Effect *pCheck = this; while (pCheck) { - if (pCheck->CommandTarget == pObj) + if (pCheck->GetCallbackScript() == pObj) pCheck->ReAssignCallbackFunctions(); pCheck = pCheck->pNext; } @@ -479,9 +467,34 @@ void C4Effect::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers) pComp->Value(iTime); pComp->Separator(); pComp->Value(iInterval); pComp->Separator(); // read object number - pComp->Value(CommandTarget); pComp->Separator(); + // FIXME: replace with this when savegame compat breaks for other reasons + // pComp->Value(mkParAdapt(CommandTarget, numbers)); + int32_t nptr = 0; + if (!pComp->isCompiler() && CommandTarget.getPropList() && CommandTarget._getPropList()->GetPropListNumbered()) + nptr = CommandTarget._getPropList()->GetPropListNumbered()->Number; + pComp->Value(nptr); + if (pComp->isCompiler()) + CommandTarget.SetObjectEnum(nptr); + pComp->Separator(); // read ID - pComp->Value(idCommandTarget); pComp->Separator(); + if (pComp->isDecompiler()) + { + const C4PropListStatic * p = CommandTarget.getPropList()->IsStatic(); + if (p) + p->RefCompileFunc(pComp, numbers); + else + pComp->String(const_cast("None"), 5, StdCompiler::RCT_ID); + } + else + { + StdStrBuf s; + pComp->Value(mkParAdapt(s, StdCompiler::RCT_ID)); + // An Object trumps a definition as command target + if (!nptr) + if (!::ScriptEngine.GetGlobalConstant(s.getData(), &CommandTarget)) + CommandTarget.Set0(); + } + pComp->Separator(); // proplist C4PropListNumbered::CompileFunc(pComp, numbers); pComp->Separator(StdCompiler::SEP_END); // ')' @@ -555,16 +568,7 @@ bool C4Effect::GetPropertyByS(C4String *k, C4Value *pResult) const case P_Name: return C4PropListNumbered::GetPropertyByS(k, pResult); case P_Priority: *pResult = C4VInt(Abs(iPriority)); return true; case P_Interval: *pResult = C4VInt(iInterval); return true; - case P_CommandTarget: - if (CommandTarget) - *pResult = C4VObj(CommandTarget); - else if (idCommandTarget) - *pResult = C4VPropList(Definitions.ID2Def(idCommandTarget)); - else - *pResult = C4VNull; - //*pResult = CommandTarget ? C4VObj(CommandTarget) : - // (idCommandTarget ? C4VPropList(Definitions.ID2Def(idCommandTarget)) : C4VNull); - return true; + case P_CommandTarget: *pResult = CommandTarget; return true; case P_Time: *pResult = C4VInt(iTime); return true; } } diff --git a/src/script/C4Effect.h b/src/script/C4Effect.h index 5a4df201d..73dfb31fc 100644 --- a/src/script/C4Effect.h +++ b/src/script/C4Effect.h @@ -24,7 +24,6 @@ #ifndef INC_C4Effects #define INC_C4Effects -#include #include // callback return values @@ -70,15 +69,13 @@ class C4Effect: public C4PropListNumbered { public: - C4ObjectPtr CommandTarget; // target object for script callbacks - if deleted, the effect is removed without callbacks - C4ID idCommandTarget; // ID of command target definition - int32_t iPriority; // effect priority for sorting into effect list; -1 indicates a dead effect int32_t iTime, iInterval; // effect time; effect callback intervall C4Effect *pNext; // next effect in linked list protected: + C4Value CommandTarget; // target object for script callbacks - if deleted, the effect is removed without callbacks // presearched callback functions for faster calling C4AulFunc *pFnTimer; // timer function Fx%sTimer C4AulFunc *pFnStart, *pFnStop; // init/deinit-functions Fx%sStart, Fx%sStop @@ -93,17 +90,17 @@ protected: void CallDamage(C4Object * obj, int32_t & damage, int damagetype, int plr); int CallEffect(const char * effect, C4Object * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); - C4Effect(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4Object * pCmdTarget, C4ID idCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); + C4Effect(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget); C4Effect(const C4Effect &); // unimplemented, do not use C4Effect(); // for the StdCompiler friend void CompileNewFunc(C4Effect *&, StdCompiler *, C4ValueNumbers * const &); public: - static C4Effect * New(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4Object * pCmdTarget, C4ID idCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); + static C4Effect * New(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); ~C4Effect(); // dtor - deletes all following effects void Register(C4Object *pForObj, int32_t iPrio); // add into effect list of object or global effect list void Denumerate(C4ValueNumbers *); // numbers to object pointers - void ClearPointers(C4Object *pObj); // clear all pointers to object - may kill some effects w/o callback, because the callback target is lost + void ClearPointers(C4PropList *pObj); // clear all pointers to object - may kill some effects w/o callback, because the callback target is lost void SetDead() { iPriority=0; } // mark effect to be removed in next execution cycle bool IsDead() { return !iPriority; } // return whether effect is to be removed @@ -130,7 +127,7 @@ public: ReAssignCallbackFunctions(); if (pNext) pNext->ReAssignAllCallbackFunctions(); } - void OnObjectChangedDef(C4Object *pObj); + void OnObjectChangedDef(C4PropList *pObj); void CompileFunc(StdCompiler *pComp, C4ValueNumbers *); virtual C4Effect * GetEffect() { return this; } diff --git a/src/script/C4Value.h b/src/script/C4Value.h index f8dd24626..f89ca1a54 100644 --- a/src/script/C4Value.h +++ b/src/script/C4Value.h @@ -130,6 +130,7 @@ public: void SetArray(C4ValueArray * Array) { C4V_Data d; d.Array = Array; Set(d, C4V_Array); } void SetFunction(C4AulFunc * Fn) { C4V_Data d; d.Fn = Fn; Set(d, C4V_Function); } void SetPropList(C4PropList * PropList) { C4V_Data d; d.PropList = PropList; Set(d, C4V_PropList); } + void SetObjectEnum(int i) { C4V_Data d; d.Int = i; Set(d, C4V_C4ObjectEnum); } void Set0(); bool operator == (const C4Value& Value2) const; From 42a15e3be998d8a71c35ea555b1144f34cb4dc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sat, 19 Dec 2015 00:39:08 +0100 Subject: [PATCH 07/25] Convert C4Effect parameters that do not need to be C4Object* to C4PropList* --- src/script/C4Effect.cpp | 24 ++++++++++++------------ src/script/C4Effect.h | 24 ++++++++++++------------ src/script/C4PropList.h | 1 + 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/script/C4Effect.cpp b/src/script/C4Effect.cpp index 8941af691..c9aa46840 100644 --- a/src/script/C4Effect.cpp +++ b/src/script/C4Effect.cpp @@ -207,7 +207,7 @@ int32_t C4Effect::GetCount(const char *szMask, int32_t iMaxPriority) return iCnt; } -C4Effect* C4Effect::Check(C4Object *pForObj, const char *szCheckEffect, int32_t iPrio, int32_t iTimer, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) +C4Effect* C4Effect::Check(C4PropList *pForObj, const char *szCheckEffect, int32_t iPrio, int32_t iTimer, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) { // priority=1: always OK; no callbacks if (iPrio == 1) return 0; @@ -298,7 +298,7 @@ void C4Effect::Execute(C4Object *pObj) while (pEffect); } -void C4Effect::Kill(C4Object *pObj) +void C4Effect::Kill(C4PropList *pObj) { // active? C4Effect *pLastRemovedEffect=NULL; @@ -322,7 +322,7 @@ void C4Effect::Kill(C4Object *pObj) pObj->SetOnFire(false); } -void C4Effect::ClearAll(C4Object *pObj, int32_t iClearFlag) +void C4Effect::ClearAll(C4PropList *pObj, int32_t iClearFlag) { // simply remove access all effects recursively, and do removal calls // this does not regard lower-level effects being added in the removal calls, @@ -345,7 +345,7 @@ void C4Effect::ClearAll(C4Object *pObj, int32_t iClearFlag) pObj->SetOnFire(false); } -void C4Effect::DoDamage(C4Object *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr) +void C4Effect::DoDamage(C4PropList *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr) { // ask all effects for damage adjustments C4Effect *pEff = this; @@ -358,7 +358,7 @@ void C4Effect::DoDamage(C4Object *pObj, int32_t &riDamage, int32_t iDamageType, while ((pEff = pEff->pNext) && riDamage); } -C4Value C4Effect::DoCall(C4Object *pObj, const char *szFn, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4, const C4Value &rVal5, const C4Value &rVal6, const C4Value &rVal7) +C4Value C4Effect::DoCall(C4PropList *pObj, const char *szFn, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4, const C4Value &rVal5, const C4Value &rVal6, const C4Value &rVal7) { // def script or global only? C4PropList *p = GetCallbackScript(); @@ -368,30 +368,30 @@ C4Value C4Effect::DoCall(C4Object *pObj, const char *szFn, const C4Value &rVal1, return p->Call(fn, &C4AulParSet(pObj, this, rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); } -int C4Effect::CallStart(C4Object * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) +int C4Effect::CallStart(C4PropList * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { if (pFnStart) return pFnStart->Exec(GetCallbackScript(), &C4AulParSet(obj, this, temporary, var1, var2, var3, var4)).getInt(); return C4Fx_OK; } -int C4Effect::CallStop(C4Object * obj, int reason, bool temporary) +int C4Effect::CallStop(C4PropList * obj, int reason, bool temporary) { if (pFnStop) return pFnStop->Exec(GetCallbackScript(), &C4AulParSet(obj, this, reason, temporary)).getInt(); return C4Fx_OK; } -int C4Effect::CallTimer(C4Object * obj, int time) +int C4Effect::CallTimer(C4PropList * obj, int time) { if (pFnTimer) return pFnTimer->Exec(GetCallbackScript(), &C4AulParSet(obj, this, time)).getInt(); return C4Fx_Execute_Kill; } -void C4Effect::CallDamage(C4Object * obj, int32_t & damage, int damagetype, int plr) +void C4Effect::CallDamage(C4PropList * obj, int32_t & damage, int damagetype, int plr) { if (pFnDamage) damage = pFnDamage->Exec(GetCallbackScript(), &C4AulParSet(obj, this, damage, damagetype, plr)).getInt(); } -int C4Effect::CallEffect(const char * effect, C4Object * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) +int C4Effect::CallEffect(const char * effect, C4PropList * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { if (pFnEffect) return pFnEffect->Exec(GetCallbackScript(), &C4AulParSet(effect, obj, this, var1, var2, var3, var4)).getInt(); @@ -412,7 +412,7 @@ void C4Effect::OnObjectChangedDef(C4PropList *pObj) } } -void C4Effect::TempRemoveUpperEffects(C4Object *pObj, bool fTempRemoveThis, C4Effect **ppLastRemovedEffect) +void C4Effect::TempRemoveUpperEffects(C4PropList *pObj, bool fTempRemoveThis, C4Effect **ppLastRemovedEffect) { if (pObj && !pObj->Status) return; // this will be invalid! // priority=1: no callbacks @@ -437,7 +437,7 @@ void C4Effect::TempRemoveUpperEffects(C4Object *pObj, bool fTempRemoveThis, C4Ef } } -void C4Effect::TempReaddUpperEffects(C4Object *pObj, C4Effect *pLastReaddEffect) +void C4Effect::TempReaddUpperEffects(C4PropList *pObj, C4Effect *pLastReaddEffect) { // nothing to do? - this will also happen if TempRemoveUpperEffects did nothing due to priority==1 if (!pLastReaddEffect) return; diff --git a/src/script/C4Effect.h b/src/script/C4Effect.h index 73dfb31fc..2e674e589 100644 --- a/src/script/C4Effect.h +++ b/src/script/C4Effect.h @@ -84,11 +84,11 @@ protected: void AssignCallbackFunctions(); // resolve callback function names - int CallStart(C4Object * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); - int CallStop(C4Object * obj, int reason, bool temporary); - int CallTimer(C4Object * obj, int time); - void CallDamage(C4Object * obj, int32_t & damage, int damagetype, int plr); - int CallEffect(const char * effect, C4Object * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); + int CallStart(C4PropList * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); + int CallStop(C4PropList * obj, int reason, bool temporary); + int CallTimer(C4PropList * obj, int time); + void CallDamage(C4PropList * obj, int32_t & damage, int damagetype, int plr); + int CallEffect(const char * effect, C4PropList * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); C4Effect(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget); C4Effect(const C4Effect &); // unimplemented, do not use @@ -110,15 +110,15 @@ public: C4Effect *Get(const char *szName, int32_t iIndex=0, int32_t iMaxPriority=0); // get effect by name int32_t GetCount(const char *szMask, int32_t iMaxPriority=0); // count effects that match the mask - C4Effect *Check(C4Object *pForObj, const char *szCheckEffect, int32_t iPrio, int32_t iTimer, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); // do some effect callbacks + C4Effect *Check(C4PropList *pForObj, const char *szCheckEffect, int32_t iPrio, int32_t iTimer, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); // do some effect callbacks C4PropList * GetCallbackScript(); // get script context for effect callbacks void Execute(C4Object *pObj); // execute all effects - void Kill(C4Object *pObj); // mark this effect deleted and do approprioate calls - void ClearAll(C4Object *pObj, int32_t iClearFlag);// kill all effects doing removal calls w/o reagard of inactive effects - void DoDamage(C4Object *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr); // ask all effects for damage + void Kill(C4PropList *pObj); // mark this effect deleted and do approprioate calls + void ClearAll(C4PropList *pObj, int32_t iClearFlag);// kill all effects doing removal calls w/o reagard of inactive effects + void DoDamage(C4PropList *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr); // ask all effects for damage - C4Value DoCall(C4Object *pObj, const char *szFn, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4, const C4Value &rVal5, const C4Value &rVal6, const C4Value &rVal7); // custom call + C4Value DoCall(C4PropList *pObj, const char *szFn, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4, const C4Value &rVal5, const C4Value &rVal6, const C4Value &rVal7); // custom call void ReAssignCallbackFunctions() { AssignCallbackFunctions(); } @@ -137,8 +137,8 @@ public: virtual C4ValueArray * GetProperties() const; protected: - void TempRemoveUpperEffects(C4Object *pObj, bool fTempRemoveThis, C4Effect **ppLastRemovedEffect); // temp remove all effects with higher priority - void TempReaddUpperEffects(C4Object *pObj, C4Effect *pLastReaddEffect); // temp remove all effects with higher priority + void TempRemoveUpperEffects(C4PropList *pObj, bool fTempRemoveThis, C4Effect **ppLastRemovedEffect); // temp remove all effects with higher priority + void TempReaddUpperEffects(C4PropList *pObj, C4Effect *pLastReaddEffect); // temp remove all effects with higher priority }; // fire effect constants diff --git a/src/script/C4PropList.h b/src/script/C4PropList.h index 36eca737e..a76dd2dcb 100644 --- a/src/script/C4PropList.h +++ b/src/script/C4PropList.h @@ -68,6 +68,7 @@ public: void Clear() { constant = false; Properties.Clear(); prototype.Set0(); } const char *GetName() const; virtual void SetName (const char *NewName = 0); + virtual void SetOnFire(bool OnFire) { } // These functions return this or a prototype. virtual C4Def const * GetDef() const; From c89e73608b2df8652eb6772910b20ba764ff6579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sat, 19 Dec 2015 20:32:53 +0100 Subject: [PATCH 08/25] Unite getting the effects list from a C4Object pointer into a function This way, that function can be re-implemented in standalone c4script. --- src/game/C4GameScript.cpp | 13 +++++++++---- src/script/C4AulDefFunc.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index 09af0a2cb..1683730a9 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -43,6 +43,11 @@ #include #include +C4Effect ** FnGetEffectsFor(C4Object * pTarget) +{ + return pTarget ? &pTarget->pEffects : &Game.pGlobalEffects; +} + // undocumented! static bool FnIncinerateLandscape(C4PropList * _this, long iX, long iY, long caused_by_plr) { @@ -2165,7 +2170,7 @@ static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4Obje { const char *szEffect = FnStringPar(psEffectName); // get effects - C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects; + C4Effect *pEffect = *FnGetEffectsFor(pTarget); if (!pEffect) return NULL; // name/wildcard given: find effect by name and index if (szEffect && *szEffect) @@ -2182,7 +2187,7 @@ static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4Object // otherwise, the correct effect will be searched in the target's effects or in the global ones if (!pEffect) { - pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects; + pEffect = *FnGetEffectsFor(pTarget); // the object has no effects attached, nothing to look for if (!pEffect) return 0; // name/wildcard given: find effect by name @@ -2211,7 +2216,7 @@ static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4Obje if (pTarget && !pTarget->Status) return C4Value(); if (!szEffect || !*szEffect) return C4Value(); // get effects - C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects; + C4Effect *pEffect = *FnGetEffectsFor(pTarget); if (!pEffect) return C4Value(); // let them check C4Effect * r = pEffect->Check(pTarget, szEffect, iPrio, iTimerInterval, Val1, Val2, Val3, Val4); @@ -2225,7 +2230,7 @@ static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4Objec // evaluate parameters const char *szEffect = FnStringPar(psEffectName); // get effects - C4Effect *pEffect = pTarget ? pTarget->pEffects : Game.pGlobalEffects; + C4Effect *pEffect = *FnGetEffectsFor(pTarget); if (!pEffect) return false; // count effects if (!*szEffect) szEffect = 0; diff --git a/src/script/C4AulDefFunc.h b/src/script/C4AulDefFunc.h index bf2494f40..cce09ff2e 100644 --- a/src/script/C4AulDefFunc.h +++ b/src/script/C4AulDefFunc.h @@ -37,6 +37,7 @@ inline C4Object * Object(C4PropList * _this) return _this ? _this->GetObject() : NULL; } StdStrBuf FnStringFormat(C4PropList * _this, C4String *szFormatPar, C4Value * Pars, int ParCount); +C4Effect ** FnGetEffectsFor(C4Object * pTarget); // Nillable: Allow integer and boolean parameters to be nil // pointer parameters represent nil via plain NULL From e2c6c2a8413133eb7bd82ce4a4d44c454e9eecf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sat, 19 Dec 2015 01:20:54 +0100 Subject: [PATCH 09/25] Do not use C4Object in C4Effect Instead, the pointer to the effect list is passed to the functions that previously used the object to determine it. --- src/game/C4Game.cpp | 6 ++--- src/game/C4GameScript.cpp | 3 ++- src/object/C4Object.cpp | 2 +- src/script/C4Effect.cpp | 50 ++++++++++++++++++++------------------- src/script/C4Effect.h | 9 +++---- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/game/C4Game.cpp b/src/game/C4Game.cpp index 88fc1e8a8..2bfe5e3d2 100644 --- a/src/game/C4Game.cpp +++ b/src/game/C4Game.cpp @@ -721,8 +721,8 @@ bool C4Game::Execute() // Returns true if the game is over // Game EXEC_S( ExecObjects(); , ExecObjectsStat ) - if (pGlobalEffects) - EXEC_S_DR( pGlobalEffects->Execute(NULL); , GEStats , "GEEx\0"); + EXEC_S_DR( C4Effect::Execute(NULL, &Game.pGlobalEffects); + , GEStats , "GEEx\0"); EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx") EXEC_S_DR( MassMover.Execute(); , MassMoverStat , "MMvEx") EXEC_S_DR( Weather.Execute(); , WeatherStat , "WtrEx") @@ -1748,7 +1748,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumber while ((pOldGlobalEffects=pNextOldGlobalEffects)) { pNextOldGlobalEffects = pOldGlobalEffects->pNext; - pOldGlobalEffects->Register(NULL, Abs(pOldGlobalEffects->iPriority)); + pOldGlobalEffects->Register(&pGlobalEffects, Abs(pOldGlobalEffects->iPriority)); } } else diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index 1683730a9..eb72b176d 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -2160,7 +2160,8 @@ static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4Object * p C4PropList * p = pCmdTarget; if (!p) p = idCmdTarget; if (!p) p = ::ScriptEngine.GetPropList(); - C4Effect * pEffect = C4Effect::New(pTarget, szEffect, iPrio, iTimerInterval, p, Val1, Val2, Val3, Val4); + C4Effect * pEffect = C4Effect::New(pTarget, FnGetEffectsFor(pTarget), + szEffect, iPrio, iTimerInterval, p, Val1, Val2, Val3, Val4); // return effect - may be 0 if the effect has been denied by another effect if (!pEffect) return C4Value(); return C4VPropList(pEffect); diff --git a/src/object/C4Object.cpp b/src/object/C4Object.cpp index 552d7c296..d318c13cb 100644 --- a/src/object/C4Object.cpp +++ b/src/object/C4Object.cpp @@ -1053,7 +1053,7 @@ void C4Object::Execute() // effects if (pEffects) { - pEffects->Execute(this); + C4Effect::Execute(this, &pEffects); if (!Status) return; } // Life diff --git a/src/script/C4Effect.cpp b/src/script/C4Effect.cpp index c9aa46840..19c65980e 100644 --- a/src/script/C4Effect.cpp +++ b/src/script/C4Effect.cpp @@ -21,8 +21,8 @@ #include #include -#include -#include +#include +#include void C4Effect::AssignCallbackFunctions() { @@ -41,7 +41,7 @@ C4PropList * C4Effect::GetCallbackScript() return CommandTarget._getPropList(); } -C4Effect::C4Effect(C4Object *pForObj, C4String *szName, int32_t iPrio, int32_t iTimerInterval, C4PropList *pCmdTarget) +C4Effect::C4Effect(C4Effect **ppEffectList, C4String *szName, int32_t iPrio, int32_t iTimerInterval, C4PropList *pCmdTarget) { // assign values iPriority = 0; // effect is not yet valid; some callbacks to other effects are done before @@ -49,15 +49,14 @@ C4Effect::C4Effect(C4Object *pForObj, C4String *szName, int32_t iPrio, int32_t i iTime = 0; CommandTarget.SetPropList(pCmdTarget); AcquireNumber(); - Register(pForObj, iPrio); + Register(ppEffectList, iPrio); // Set name and callback functions SetProperty(P_Name, C4VString(szName)); } -void C4Effect::Register(C4Object *pForObj, int32_t iPrio) +void C4Effect::Register(C4Effect **ppEffectList, int32_t iPrio) { // get effect target - C4Effect **ppEffectList = pForObj ? &pForObj->pEffects : &Game.pGlobalEffects; C4Effect *pCheck, *pPrev = *ppEffectList; if (pPrev && Abs(pPrev->iPriority) < iPrio) { @@ -75,18 +74,23 @@ void C4Effect::Register(C4Object *pForObj, int32_t iPrio) } } -C4Effect * C4Effect::New(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) +C4Effect * C4Effect::New(C4PropList *pForObj, C4Effect **ppEffectList, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) +{ + C4Effect * pEffect = new C4Effect(ppEffectList, szName, iPrio, iTimerInterval, pCmdTarget); + return pEffect->Init(pForObj, iPrio, rVal1, rVal2, rVal3, rVal4); +} + +C4Effect * C4Effect::Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) { - C4Effect * pEffect = new C4Effect(pForObj, szName, iPrio, iTimerInterval, pCmdTarget); // ask all effects with higher priority first - except for prio 1 effects, which are considered out of the priority call chain (as per doc) bool fRemoveUpper = (iPrio != 1); // note that apart from denying the creation of this effect, higher priority effects may also remove themselves // or do other things with the effect list // (which does not quite make sense, because the effect might be denied by another effect) // so the priority is assigned after this call, marking this effect dead before it's definitely valid - if (fRemoveUpper && pEffect->pNext) + if (fRemoveUpper && pNext) { - C4Effect * pEffect2 = pEffect->pNext->Check(pForObj, szName->GetCStr(), iPrio, iTimerInterval, rVal1, rVal2, rVal3, rVal4); + C4Effect * pEffect2 = pNext->Check(pForObj, GetName(), iPrio, iInterval, rVal1, rVal2, rVal3, rVal4); if (pEffect2) { // effect denied (iResult = -1), added to an effect (iResult = Number of that effect) @@ -102,21 +106,21 @@ C4Effect * C4Effect::New(C4Object * pForObj, C4String * szName, int32_t iPrio, i // because that would cause a wrong initialization order // (hardly ever causing trouble, however...) C4Effect *pLastRemovedEffect=NULL; - if (fRemoveUpper && pEffect->pNext && pEffect->pFnStart) - pEffect->TempRemoveUpperEffects(pForObj, false, &pLastRemovedEffect); + if (fRemoveUpper && pNext && pFnStart) + TempRemoveUpperEffects(pForObj, false, &pLastRemovedEffect); // bad things may happen if (pForObj && !pForObj->Status) return 0; // this will be invalid! - pEffect->iPriority = iPrio; // validate effect now - if (pEffect->CallStart(pForObj, 0, rVal1, rVal2, rVal3, rVal4) == C4Fx_Start_Deny) + iPriority = iPrio; // validate effect now + if (CallStart(pForObj, 0, rVal1, rVal2, rVal3, rVal4) == C4Fx_Start_Deny) // the effect denied to start: assume it hasn't, and mark it dead - pEffect->SetDead(); - if (fRemoveUpper && pEffect->pNext && pEffect->pFnStart) - pEffect->TempReaddUpperEffects(pForObj, pLastRemovedEffect); + SetDead(); + if (fRemoveUpper && pNext && pFnStart) + TempReaddUpperEffects(pForObj, pLastRemovedEffect); if (pForObj && !pForObj->Status) return 0; // this will be invalid! // Update OnFire cache - if (!pEffect->IsDead() && pForObj && WildcardMatch(C4Fx_AnyFire, szName->GetCStr())) + if (!IsDead() && pForObj && WildcardMatch(C4Fx_AnyFire, GetName())) pForObj->SetOnFire(true); - return pEffect; + return this; } C4Effect::C4Effect() @@ -255,13 +259,12 @@ C4Effect* C4Effect::Check(C4PropList *pForObj, const char *szCheckEffect, int32_ return 0; } -void C4Effect::Execute(C4Object *pObj) +void C4Effect::Execute(C4PropList *pObj, C4Effect **ppEffectList) { // get effect list - C4Effect **ppEffectList = pObj ? &pObj->pEffects : &Game.pGlobalEffects; // execute all effects not marked as dead - C4Effect *pEffect = this, **ppPrevEffect=ppEffectList; - do + C4Effect *pEffect = *ppEffectList, **ppPrevEffect=ppEffectList; + while (pEffect) { // effect dead? if (pEffect->IsDead()) @@ -295,7 +298,6 @@ void C4Effect::Execute(C4Object *pObj) pEffect = pEffect->pNext; } } - while (pEffect); } void C4Effect::Kill(C4PropList *pObj) diff --git a/src/script/C4Effect.h b/src/script/C4Effect.h index 2e674e589..46163ec3f 100644 --- a/src/script/C4Effect.h +++ b/src/script/C4Effect.h @@ -90,15 +90,16 @@ protected: void CallDamage(C4PropList * obj, int32_t & damage, int damagetype, int plr); int CallEffect(const char * effect, C4PropList * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); - C4Effect(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget); + C4Effect(C4Effect **ppEffectList, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget); C4Effect(const C4Effect &); // unimplemented, do not use C4Effect(); // for the StdCompiler + C4Effect * Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); friend void CompileNewFunc(C4Effect *&, StdCompiler *, C4ValueNumbers * const &); public: - static C4Effect * New(C4Object * pForObj, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); + static C4Effect * New(C4PropList *pForObj, C4Effect **ppEffectList, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); ~C4Effect(); // dtor - deletes all following effects - void Register(C4Object *pForObj, int32_t iPrio); // add into effect list of object or global effect list + void Register(C4Effect **ppEffectList, int32_t iPrio); // add into effect list of object or global effect list void Denumerate(C4ValueNumbers *); // numbers to object pointers void ClearPointers(C4PropList *pObj); // clear all pointers to object - may kill some effects w/o callback, because the callback target is lost @@ -113,7 +114,7 @@ public: C4Effect *Check(C4PropList *pForObj, const char *szCheckEffect, int32_t iPrio, int32_t iTimer, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); // do some effect callbacks C4PropList * GetCallbackScript(); // get script context for effect callbacks - void Execute(C4Object *pObj); // execute all effects + static void Execute(C4PropList *pObj, C4Effect **ppEffectList); // execute all effects void Kill(C4PropList *pObj); // mark this effect deleted and do approprioate calls void ClearAll(C4PropList *pObj, int32_t iClearFlag);// kill all effects doing removal calls w/o reagard of inactive effects void DoDamage(C4PropList *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr); // ask all effects for damage From e8811a7b21ede7235f6f73c49d240adc8334e75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sat, 19 Dec 2015 19:53:08 +0100 Subject: [PATCH 10/25] Move global effects from ::Game to ::ScriptEngine This even enables some simplification in the CompileFuncs, since the global effects were already put in the same section as the other ScriptEngine parts. The callback for updates due to relinks also fits nicely. The reset in C4Game::Default was redundant with C4Game::Clear already. --- src/game/C4Game.cpp | 56 +++++++----------------------------- src/game/C4Game.h | 1 - src/game/C4GameScript.cpp | 2 +- src/object/C4GameObjects.cpp | 2 -- src/object/C4Object.cpp | 3 +- src/script/C4Aul.cpp | 48 ++++++++++++++++++++++++++----- src/script/C4Aul.h | 4 ++- src/script/C4AulLink.cpp | 4 +++ 8 files changed, 61 insertions(+), 59 deletions(-) diff --git a/src/game/C4Game.cpp b/src/game/C4Game.cpp index 2bfe5e3d2..5b3de79f4 100644 --- a/src/game/C4Game.cpp +++ b/src/game/C4Game.cpp @@ -574,7 +574,6 @@ void C4Game::Clear() ::Definitions.Clear(); Landscape.Clear(); PXS.Clear(); - if (pGlobalEffects) { delete pGlobalEffects; pGlobalEffects=NULL; } ScriptGuiRoot.reset(); Particles.Clear(); ::MaterialMap.Clear(); @@ -721,7 +720,7 @@ bool C4Game::Execute() // Returns true if the game is over // Game EXEC_S( ExecObjects(); , ExecObjectsStat ) - EXEC_S_DR( C4Effect::Execute(NULL, &Game.pGlobalEffects); + EXEC_S_DR( C4Effect::Execute(NULL, &ScriptEngine.pGlobalEffects); , GEStats , "GEEx\0"); EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx") EXEC_S_DR( MassMover.Execute(); , MassMoverStat , "MMvEx") @@ -909,8 +908,8 @@ void C4Game::ClearPointers(C4Object * pObj) ::MouseControl.ClearPointers(pObj); ScriptGuiRoot->ClearPointers(pObj); TransferZones.ClearPointers(pObj); - if (pGlobalEffects) - pGlobalEffects->ClearPointers(pObj); + if (::ScriptEngine.pGlobalEffects) + ::ScriptEngine.pGlobalEffects->ClearPointers(pObj); if (::Landscape.pFoW) ::Landscape.pFoW->Remove(pObj); } @@ -1464,7 +1463,6 @@ void C4Game::Default() pParentGroup=NULL; pScenarioSections=pCurrentScenarioSection=NULL; *CurrentScenarioSection=0; - pGlobalEffects=NULL; fResortAnyObject=false; pNetworkStatistics.reset(); ::Application.MusicSystem.ClearGame(); @@ -1724,40 +1722,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumber pComp->Value(mkParAdapt(Objects, !comp.fExact, numbers)); - pComp->Name("Script"); - if (!comp.fScenarioSection) - { - pComp->Value(mkParAdapt(ScriptEngine, numbers)); - } - if (comp.fScenarioSection && pComp->isCompiler()) - { - // loading scenario section: Merge effects - // Must keep old effects here even if they're dead, because the LoadScenarioSection call typically came from execution of a global effect - // and otherwise dead pointers would remain on the stack - C4Effect *pOldGlobalEffects, *pNextOldGlobalEffects=pGlobalEffects; - pGlobalEffects = NULL; - try - { - pComp->Value(mkParAdapt(mkNamingPtrAdapt(pGlobalEffects, "Effects"), numbers)); - } - catch (...) - { - delete pNextOldGlobalEffects; - throw; - } - while ((pOldGlobalEffects=pNextOldGlobalEffects)) - { - pNextOldGlobalEffects = pOldGlobalEffects->pNext; - pOldGlobalEffects->Register(&pGlobalEffects, Abs(pOldGlobalEffects->iPriority)); - } - } - else - { - // Otherwise, just compile effects - pComp->Value(mkParAdapt(mkNamingPtrAdapt(pGlobalEffects, "Effects"), numbers)); - } - pComp->Value(mkNamingAdapt(*numbers, "Values")); - pComp->NameEnd(); + pComp->Value(mkNamingAdapt(mkParAdapt(ScriptEngine, comp.fScenarioSection, numbers), "Script")); } bool C4Game::CompileRuntimeData(C4Group &hGroup, bool fLoadSection, bool exact, bool sync, C4ValueNumbers * numbers) @@ -2278,7 +2243,6 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value // Denumerate game data pointers if (!fLoadSection) ScriptEngine.Denumerate(numbers); - if (!fLoadSection && pGlobalEffects) pGlobalEffects->Denumerate(numbers); if (!fLoadSection) GlobalSoundModifier.Denumerate(numbers); numbers->Denumerate(); if (!fLoadSection) ScriptGuiRoot->Denumerate(numbers); @@ -3513,12 +3477,12 @@ bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags) } DeleteObjects(false); // remove global effects - if (pGlobalEffects) if (!(dwFlags & C4S_KEEP_EFFECTS)) - { - pGlobalEffects->ClearAll(NULL, C4FxCall_RemoveClear); - // scenario section call might have been done from a global effect - // rely on dead effect removal for actually removing the effects; do not clear the array here! - } + if (::ScriptEngine.pGlobalEffects && !(dwFlags & C4S_KEEP_EFFECTS)) + { + ::ScriptEngine.pGlobalEffects->ClearAll(NULL, C4FxCall_RemoveClear); + // scenario section call might have been done from a global effect + // rely on dead effect removal for actually removing the effects; do not clear the array here! + } // del particles as well Particles.ClearAllParticles(); // clear transfer zones diff --git a/src/game/C4Game.h b/src/game/C4Game.h index 032218b6d..448dd5b94 100644 --- a/src/game/C4Game.h +++ b/src/game/C4Game.h @@ -83,7 +83,6 @@ public: C4Extra Extra; class C4ScenarioObjectsScriptHost *pScenarioObjectsScript; C4ScenarioSection *pScenarioSections, *pCurrentScenarioSection; - C4Effect *pGlobalEffects; C4PlayerControlDefs PlayerControlDefs; C4PlayerControlAssignmentSets PlayerControlUserAssignmentSets, PlayerControlDefaultAssignmentSets; C4Scoreboard Scoreboard; diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index eb72b176d..d71942c82 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -45,7 +45,7 @@ C4Effect ** FnGetEffectsFor(C4Object * pTarget) { - return pTarget ? &pTarget->pEffects : &Game.pGlobalEffects; + return pTarget ? &pTarget->pEffects : &ScriptEngine.pGlobalEffects; } // undocumented! diff --git a/src/object/C4GameObjects.cpp b/src/object/C4GameObjects.cpp index 0b05d9918..993598bef 100644 --- a/src/object/C4GameObjects.cpp +++ b/src/object/C4GameObjects.cpp @@ -346,8 +346,6 @@ void C4GameObjects::UpdateScriptPointers() // call in sublists C4ObjectList::UpdateScriptPointers(); InactiveObjects.UpdateScriptPointers(); - // adjust global effects - if (Game.pGlobalEffects) Game.pGlobalEffects->ReAssignAllCallbackFunctions(); } C4Value C4GameObjects::GRBroadcast(const char *szFunction, C4AulParSet *pPars, bool fPassError, bool fRejectTest) diff --git a/src/object/C4Object.cpp b/src/object/C4Object.cpp index d318c13cb..c05bc58b1 100644 --- a/src/object/C4Object.cpp +++ b/src/object/C4Object.cpp @@ -1195,7 +1195,8 @@ bool C4Object::ChangeDef(C4ID idNew) SetOCF(); // Any effect callbacks to this object might need to reinitialize their target functions // This is ugly, because every effect there is must be updated... - if (Game.pGlobalEffects) Game.pGlobalEffects->OnObjectChangedDef(this); + if (::ScriptEngine.pGlobalEffects) + ::ScriptEngine.pGlobalEffects->OnObjectChangedDef(this); for (C4Object *obj : Objects) if (obj->pEffects) obj->pEffects->OnObjectChangedDef(this); // Containment (no Entrance) diff --git a/src/script/C4Aul.cpp b/src/script/C4Aul.cpp index 09d13180f..1252a00ff 100644 --- a/src/script/C4Aul.cpp +++ b/src/script/C4Aul.cpp @@ -17,11 +17,12 @@ #include #include + #include #include - #include #include +#include #include #include #include @@ -84,6 +85,7 @@ void C4AulScriptEngine::Clear() RegisterGlobalConstant("Global", C4VPropList(this)); GlobalNamed.Reset(); GlobalNamed.SetNameList(&GlobalNamedNames); + delete pGlobalEffects; pGlobalEffects=NULL; UserFiles.clear(); } @@ -115,15 +117,47 @@ void C4AulScriptEngine::Denumerate(C4ValueNumbers * numbers) // runtime data only: don't denumerate consts GameScript.ScenPropList.Denumerate(numbers); C4PropListStaticMember::Denumerate(numbers); + if (pGlobalEffects) pGlobalEffects->Denumerate(numbers); } -void C4AulScriptEngine::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers) +void C4AulScriptEngine::CompileFunc(StdCompiler *pComp, bool fScenarioSection, C4ValueNumbers * numbers) { - assert(UserFiles.empty()); // user files must not be kept open - C4ValueMapData GlobalNamedDefault; - GlobalNamedDefault.SetNameList(&GlobalNamedNames); - pComp->Value(mkNamingAdapt(mkParAdapt(GlobalNamed, numbers), "StaticVariables", GlobalNamedDefault)); - pComp->Value(mkNamingAdapt(mkParAdapt(*GameScript.ScenPropList._getPropList(), numbers), "Scenario")); + if (!fScenarioSection) + { + assert(UserFiles.empty()); // user files must not be kept open + C4ValueMapData GlobalNamedDefault; + GlobalNamedDefault.SetNameList(&GlobalNamedNames); + pComp->Value(mkNamingAdapt(mkParAdapt(GlobalNamed, numbers), "StaticVariables", GlobalNamedDefault)); + pComp->Value(mkNamingAdapt(mkParAdapt(*GameScript.ScenPropList._getPropList(), numbers), "Scenario")); + } + if (fScenarioSection && pComp->isCompiler()) + { + // loading scenario section: Merge effects + // Must keep old effects here even if they're dead, because the LoadScenarioSection call typically came from execution of a global effect + // and otherwise dead pointers would remain on the stack + C4Effect *pOldGlobalEffects, *pNextOldGlobalEffects=pGlobalEffects; + pGlobalEffects = NULL; + try + { + pComp->Value(mkParAdapt(mkNamingPtrAdapt(pGlobalEffects, "Effects"), numbers)); + } + catch (...) + { + delete pNextOldGlobalEffects; + throw; + } + while ((pOldGlobalEffects=pNextOldGlobalEffects)) + { + pNextOldGlobalEffects = pOldGlobalEffects->pNext; + pOldGlobalEffects->Register(&pGlobalEffects, Abs(pOldGlobalEffects->iPriority)); + } + } + else + { + // Otherwise, just compile effects + pComp->Value(mkParAdapt(mkNamingPtrAdapt(pGlobalEffects, "Effects"), numbers)); + } + pComp->Value(mkNamingAdapt(*numbers, "Values")); } std::list C4AulScriptEngine::GetFunctionNames(C4PropList * p) diff --git a/src/script/C4Aul.h b/src/script/C4Aul.h index 29b7fb320..7f0e6bef5 100644 --- a/src/script/C4Aul.h +++ b/src/script/C4Aul.h @@ -121,6 +121,8 @@ public: C4ValueMapNames GlobalConstNames; C4ValueMapData GlobalConsts; + C4Effect * pGlobalEffects = NULL; + C4AulScriptEngine(); // constructor ~C4AulScriptEngine(); // destructor void Clear(); // clear data @@ -139,7 +141,7 @@ public: void UnLink(); // called when a script is being reloaded (clears string table) // Compile scenario script data (without strings and constants) - void CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers); + void CompileFunc(StdCompiler *pComp, bool fScenarioSection, C4ValueNumbers * numbers); // Handle user files int32_t CreateUserFile(); // create new file and return handle diff --git a/src/script/C4AulLink.cpp b/src/script/C4AulLink.cpp index edeef6aa9..9c224652e 100644 --- a/src/script/C4AulLink.cpp +++ b/src/script/C4AulLink.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -189,6 +190,9 @@ void C4AulScriptEngine::ReLink(C4DefList *rDefs) // display state LogF("C4AulScriptEngine linked - %d line%s, %d warning%s, %d error%s", lineCnt, (lineCnt != 1 ? "s" : ""), warnCnt, (warnCnt != 1 ? "s" : ""), errCnt, (errCnt != 1 ? "s" : "")); + + // adjust global effects + if (pGlobalEffects) pGlobalEffects->ReAssignAllCallbackFunctions(); } bool C4AulScriptEngine::ReloadScript(const char *szScript, const char *szLanguage) From 2cdf553953bf4a654f0faa324d7dd91ec53bf176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sun, 20 Dec 2015 01:06:02 +0100 Subject: [PATCH 11/25] Change effect list getting function parameter type to C4PropList* This makes it possible to move the effect functions to C4Script.cpp. --- src/game/C4GameScript.cpp | 25 ++++++++++++++++--------- src/script/C4AulDefFunc.h | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index d71942c82..0964d8c52 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -43,9 +43,16 @@ #include #include -C4Effect ** FnGetEffectsFor(C4Object * pTarget) +C4Effect ** FnGetEffectsFor(C4PropList * pTarget) { - return pTarget ? &pTarget->pEffects : &ScriptEngine.pGlobalEffects; + if (pTarget) + { + C4Object * Obj = pTarget->GetObject(); + if (!Obj) + throw C4AulExecError("Effect target has to be an object"); + return &Obj->pEffects; + } + return &ScriptEngine.pGlobalEffects; } // undocumented! @@ -2149,8 +2156,8 @@ static long FnLoadScenarioSection(C4PropList * _this, C4String *pstrSection, lon return Game.LoadScenarioSection(szSection, dwFlags); } -static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4Object * pTarget, - int iPrio, int iTimerInterval, C4Object * pCmdTarget, C4Def * idCmdTarget, +static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4PropList * pTarget, + int iPrio, int iTimerInterval, C4PropList * pCmdTarget, C4Def * idCmdTarget, const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) { // safety @@ -2167,7 +2174,7 @@ static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4Object * p return C4VPropList(pEffect); } -static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4Object *pTarget, int index, int iMaxPriority) +static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, int index, int iMaxPriority) { const char *szEffect = FnStringPar(psEffectName); // get effects @@ -2179,7 +2186,7 @@ static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4Obje return NULL; } -static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4Object *pTarget, C4Effect * pEffect2, bool fDoNoCalls) +static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, C4Effect * pEffect2, bool fDoNoCalls) { // evaluate parameters const char *szEffect = FnStringPar(psEffectName); @@ -2208,7 +2215,7 @@ static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4Object return true; } -static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4Object * pTarget, +static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4PropList * pTarget, int iPrio, int iTimerInterval, const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) { @@ -2226,7 +2233,7 @@ static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4Obje return C4VPropList(r); } -static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4Object *pTarget, long iMaxPriority) +static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, long iMaxPriority) { // evaluate parameters const char *szEffect = FnStringPar(psEffectName); @@ -2241,7 +2248,7 @@ static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4Objec static C4Value FnEffectCall(C4PropList * _this, C4Value * Pars) { // evaluate parameters - C4Object *pTarget = Pars[0].getObj(); + C4PropList *pTarget = Pars[0].getPropList(); C4Effect * pEffect = Pars[1].getPropList() ? Pars[1].getPropList()->GetEffect() : 0; const char *szCallFn = FnStringPar(Pars[2].getStr()); // safety diff --git a/src/script/C4AulDefFunc.h b/src/script/C4AulDefFunc.h index cce09ff2e..23c07afaa 100644 --- a/src/script/C4AulDefFunc.h +++ b/src/script/C4AulDefFunc.h @@ -37,7 +37,7 @@ inline C4Object * Object(C4PropList * _this) return _this ? _this->GetObject() : NULL; } StdStrBuf FnStringFormat(C4PropList * _this, C4String *szFormatPar, C4Value * Pars, int ParCount); -C4Effect ** FnGetEffectsFor(C4Object * pTarget); +C4Effect ** FnGetEffectsFor(C4PropList * pTarget); // Nillable: Allow integer and boolean parameters to be nil // pointer parameters represent nil via plain NULL From 5a470f51f88ada5024af0d22edf8578c735f7822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Mon, 21 Dec 2015 23:55:03 +0100 Subject: [PATCH 12/25] Move Effect functions to C4Script.cpp --- src/game/C4GameScript.cpp | 136 ------------------------ src/script/C4Script.cpp | 140 +++++++++++++++++++++++++ src/script/C4ScriptStandaloneStubs.cpp | 7 ++ 3 files changed, 147 insertions(+), 136 deletions(-) diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index 0964d8c52..aa6249892 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -2156,109 +2156,6 @@ static long FnLoadScenarioSection(C4PropList * _this, C4String *pstrSection, lon return Game.LoadScenarioSection(szSection, dwFlags); } -static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4PropList * pTarget, - int iPrio, int iTimerInterval, C4PropList * pCmdTarget, C4Def * idCmdTarget, - const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) -{ - // safety - if (pTarget && !pTarget->Status) return C4Value(); - if (!szEffect || !*szEffect->GetCStr() || !iPrio) return C4Value(); - // create effect - C4PropList * p = pCmdTarget; - if (!p) p = idCmdTarget; - if (!p) p = ::ScriptEngine.GetPropList(); - C4Effect * pEffect = C4Effect::New(pTarget, FnGetEffectsFor(pTarget), - szEffect, iPrio, iTimerInterval, p, Val1, Val2, Val3, Val4); - // return effect - may be 0 if the effect has been denied by another effect - if (!pEffect) return C4Value(); - return C4VPropList(pEffect); -} - -static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, int index, int iMaxPriority) -{ - const char *szEffect = FnStringPar(psEffectName); - // get effects - C4Effect *pEffect = *FnGetEffectsFor(pTarget); - if (!pEffect) return NULL; - // name/wildcard given: find effect by name and index - if (szEffect && *szEffect) - return pEffect->Get(szEffect, index, iMaxPriority); - return NULL; -} - -static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, C4Effect * pEffect2, bool fDoNoCalls) -{ - // evaluate parameters - const char *szEffect = FnStringPar(psEffectName); - // if the user passed an effect, it can be used straight-away - C4Effect *pEffect = pEffect2; - // otherwise, the correct effect will be searched in the target's effects or in the global ones - if (!pEffect) - { - pEffect = *FnGetEffectsFor(pTarget); - // the object has no effects attached, nothing to look for - if (!pEffect) return 0; - // name/wildcard given: find effect by name - if (szEffect && *szEffect) - pEffect = pEffect->Get(szEffect, 0); - } - - // neither passed nor found - nothing to remove! - if (!pEffect) return 0; - - // kill it - if (fDoNoCalls) - pEffect->SetDead(); - else - pEffect->Kill(pTarget); - // done, success - return true; -} - -static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4PropList * pTarget, - int iPrio, int iTimerInterval, - const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) -{ - const char *szEffect = FnStringPar(psEffectName); - // safety - if (pTarget && !pTarget->Status) return C4Value(); - if (!szEffect || !*szEffect) return C4Value(); - // get effects - C4Effect *pEffect = *FnGetEffectsFor(pTarget); - if (!pEffect) return C4Value(); - // let them check - C4Effect * r = pEffect->Check(pTarget, szEffect, iPrio, iTimerInterval, Val1, Val2, Val3, Val4); - if (r == (C4Effect *)C4Fx_Effect_Deny) return C4VInt(C4Fx_Effect_Deny); - if (r == (C4Effect *)C4Fx_Effect_Annul) return C4VInt(C4Fx_Effect_Annul); - return C4VPropList(r); -} - -static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, long iMaxPriority) -{ - // evaluate parameters - const char *szEffect = FnStringPar(psEffectName); - // get effects - C4Effect *pEffect = *FnGetEffectsFor(pTarget); - if (!pEffect) return false; - // count effects - if (!*szEffect) szEffect = 0; - return pEffect->GetCount(szEffect, iMaxPriority); -} - -static C4Value FnEffectCall(C4PropList * _this, C4Value * Pars) -{ - // evaluate parameters - C4PropList *pTarget = Pars[0].getPropList(); - C4Effect * pEffect = Pars[1].getPropList() ? Pars[1].getPropList()->GetEffect() : 0; - const char *szCallFn = FnStringPar(Pars[2].getStr()); - // safety - if (pTarget && !pTarget->Status) return C4Value(); - if (!szCallFn || !*szCallFn) return C4Value(); - if (!pEffect) return C4Value(); - // do call - return pEffect->DoCall(pTarget, szCallFn, Pars[3], Pars[4], Pars[5], Pars[6], Pars[7], Pars[8], Pars[9]); -} - static bool FnSetViewOffset(C4PropList * _this, long iPlayer, long iX, long iY) { if (!ValidPlr(iPlayer)) return false; @@ -2882,8 +2779,6 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine) F(RemoveUnusedTexMapEntries); F(SimFlight); F(LoadScenarioSection); - F(RemoveEffect); - F(GetEffect); F(SetViewOffset); ::AddFunc(p, "SetPreSend", FnSetPreSend, false); F(GetPlayerID); @@ -2903,7 +2798,6 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine) F(AddEvaluationData); F(HideSettlementScoreInEvaluation); F(ExtractMaterialAmount); - F(GetEffectCount); F(CustomMessage); F(GuiOpen); F(GuiUpdateTag); @@ -2935,8 +2829,6 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine) F(GetMaterialVal); F(SetPlrExtraData); F(GetPlrExtraData); - F(AddEffect); - F(CheckEffect); F(PV_Linear); F(PV_Random); F(PV_Direction); @@ -2958,33 +2850,6 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine) C4ScriptConstDef C4ScriptGameConstMap[]= { - { "FX_OK" ,C4V_Int, C4Fx_OK }, // generic standard behaviour for all effect callbacks - { "FX_Effect_Deny" ,C4V_Int, C4Fx_Effect_Deny }, // delete effect - { "FX_Effect_Annul" ,C4V_Int, C4Fx_Effect_Annul }, // delete effect, because it has annulled a countereffect - { "FX_Effect_AnnulDoCalls" ,C4V_Int, C4Fx_Effect_AnnulCalls }, // delete effect, because it has annulled a countereffect; temp readd countereffect - { "FX_Execute_Kill" ,C4V_Int, C4Fx_Execute_Kill }, // execute callback: Remove effect now - { "FX_Stop_Deny" ,C4V_Int, C4Fx_Stop_Deny }, // deny effect removal - { "FX_Start_Deny" ,C4V_Int, C4Fx_Start_Deny }, // deny effect start - - { "FX_Call_Normal" ,C4V_Int, C4FxCall_Normal }, // normal call; effect is being added or removed - { "FX_Call_Temp" ,C4V_Int, C4FxCall_Temp }, // temp call; effect is being added or removed in responce to a lower-level effect change - { "FX_Call_TempAddForRemoval" ,C4V_Int, C4FxCall_TempAddForRemoval }, // temp call; effect is being added because it had been temp removed and is now removed forever - { "FX_Call_RemoveClear" ,C4V_Int, C4FxCall_RemoveClear }, // effect is being removed because object is being removed - { "FX_Call_RemoveDeath" ,C4V_Int, C4FxCall_RemoveDeath }, // effect is being removed because object died - return -1 to avoid removal - { "FX_Call_DmgScript" ,C4V_Int, C4FxCall_DmgScript }, // damage through script call - { "FX_Call_DmgBlast" ,C4V_Int, C4FxCall_DmgBlast }, // damage through blast - { "FX_Call_DmgFire" ,C4V_Int, C4FxCall_DmgFire }, // damage through fire - { "FX_Call_DmgChop" ,C4V_Int, C4FxCall_DmgChop }, // damage through chopping - { "FX_Call_Energy" ,C4V_Int, 32 }, // bitmask for generic energy loss - { "FX_Call_EngScript" ,C4V_Int, C4FxCall_EngScript }, // energy loss through script call - { "FX_Call_EngBlast" ,C4V_Int, C4FxCall_EngBlast }, // energy loss through blast - { "FX_Call_EngObjHit" ,C4V_Int, C4FxCall_EngObjHit }, // energy loss through object hitting the living - { "FX_Call_EngFire" ,C4V_Int, C4FxCall_EngFire }, // energy loss through fire - { "FX_Call_EngBaseRefresh" ,C4V_Int, C4FxCall_EngBaseRefresh }, // energy reload in base (also by base object, but that's normally not called) - { "FX_Call_EngAsphyxiation" ,C4V_Int, C4FxCall_EngAsphyxiation }, // energy loss through asphyxiaction - { "FX_Call_EngCorrosion" ,C4V_Int, C4FxCall_EngCorrosion }, // energy loss through corrosion (acid) - { "FX_Call_EngGetPunched" ,C4V_Int, C4FxCall_EngGetPunched }, // energy loss from punch - { "NO_OWNER" ,C4V_Int, NO_OWNER }, // invalid player number // material density @@ -3118,7 +2983,6 @@ C4ScriptFnDef C4ScriptGameFnMap[]= { "PlayerMessage", 1, C4V_Int, { C4V_Int ,C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnPlayerMessage }, { "Message", 1, C4V_Bool, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnMessage }, { "AddMessage", 1, C4V_Bool, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnAddMessage }, - { "EffectCall", 1, C4V_Any, { C4V_Object ,C4V_PropList,C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnEffectCall }, { "PV_KeyFrames", 1, C4V_Array, { C4V_Int ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnPV_KeyFrames }, { NULL, 0, C4V_Nil, { C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil ,C4V_Nil}, 0 } diff --git a/src/script/C4Script.cpp b/src/script/C4Script.cpp index 16384600a..6cd6739dc 100644 --- a/src/script/C4Script.cpp +++ b/src/script/C4Script.cpp @@ -251,6 +251,8 @@ static C4Value FnTrans_Mul(C4PropList * _this, C4Value *pars) #undef MAKE_AND_RETURN_ARRAY +/* PropLists */ + static C4PropList * FnCreatePropList(C4PropList * _this, C4PropList * prototype) { return C4PropList::New(prototype); @@ -325,6 +327,111 @@ static C4Value FnCall(C4PropList * _this, C4Value * Pars) return fn->Exec(_this, &ParSet, true); } +/* Effects */ + +static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4PropList * pTarget, + int iPrio, int iTimerInterval, C4PropList * pCmdTarget, C4Def * idCmdTarget, + const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) +{ + // safety + if (pTarget && !pTarget->Status) return C4Value(); + if (!szEffect || !*szEffect->GetCStr() || !iPrio) return C4Value(); + // create effect + C4PropList * p = pCmdTarget; + if (!p) p = idCmdTarget; + if (!p) p = ::ScriptEngine.GetPropList(); + C4Effect * pEffect = C4Effect::New(pTarget, FnGetEffectsFor(pTarget), + szEffect, iPrio, iTimerInterval, p, Val1, Val2, Val3, Val4); + // return effect - may be 0 if the effect has been denied by another effect + if (!pEffect) return C4Value(); + return C4VPropList(pEffect); +} + +static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, int index, int iMaxPriority) +{ + const char *szEffect = FnStringPar(psEffectName); + // get effects + C4Effect *pEffect = *FnGetEffectsFor(pTarget); + if (!pEffect) return NULL; + // name/wildcard given: find effect by name and index + if (szEffect && *szEffect) + return pEffect->Get(szEffect, index, iMaxPriority); + return NULL; +} + +static bool FnRemoveEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, C4Effect * pEffect2, bool fDoNoCalls) +{ + // evaluate parameters + const char *szEffect = FnStringPar(psEffectName); + // if the user passed an effect, it can be used straight-away + C4Effect *pEffect = pEffect2; + // otherwise, the correct effect will be searched in the target's effects or in the global ones + if (!pEffect) + { + pEffect = *FnGetEffectsFor(pTarget); + // the object has no effects attached, nothing to look for + if (!pEffect) return 0; + // name/wildcard given: find effect by name + if (szEffect && *szEffect) + pEffect = pEffect->Get(szEffect, 0); + } + + // neither passed nor found - nothing to remove! + if (!pEffect) return 0; + + // kill it + if (fDoNoCalls) + pEffect->SetDead(); + else + pEffect->Kill(pTarget); + // done, success + return true; +} + +static C4Value FnCheckEffect(C4PropList * _this, C4String * psEffectName, C4PropList * pTarget, + int iPrio, int iTimerInterval, + const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) +{ + const char *szEffect = FnStringPar(psEffectName); + // safety + if (pTarget && !pTarget->Status) return C4Value(); + if (!szEffect || !*szEffect) return C4Value(); + // get effects + C4Effect *pEffect = *FnGetEffectsFor(pTarget); + if (!pEffect) return C4Value(); + // let them check + C4Effect * r = pEffect->Check(pTarget, szEffect, iPrio, iTimerInterval, Val1, Val2, Val3, Val4); + if (r == (C4Effect *)C4Fx_Effect_Deny) return C4VInt(C4Fx_Effect_Deny); + if (r == (C4Effect *)C4Fx_Effect_Annul) return C4VInt(C4Fx_Effect_Annul); + return C4VPropList(r); +} + +static long FnGetEffectCount(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, long iMaxPriority) +{ + // evaluate parameters + const char *szEffect = FnStringPar(psEffectName); + // get effects + C4Effect *pEffect = *FnGetEffectsFor(pTarget); + if (!pEffect) return false; + // count effects + if (!*szEffect) szEffect = 0; + return pEffect->GetCount(szEffect, iMaxPriority); +} + +static C4Value FnEffectCall(C4PropList * _this, C4Value * Pars) +{ + // evaluate parameters + C4PropList *pTarget = Pars[0].getPropList(); + C4Effect * pEffect = Pars[1].getPropList() ? Pars[1].getPropList()->GetEffect() : 0; + const char *szCallFn = FnStringPar(Pars[2].getStr()); + // safety + if (pTarget && !pTarget->Status) return C4Value(); + if (!szCallFn || !*szCallFn) return C4Value(); + if (!pEffect) return C4Value(); + // do call + return pEffect->DoCall(pTarget, szCallFn, Pars[3], Pars[4], Pars[5], Pars[6], Pars[7], Pars[8], Pars[9]); +} + static C4Value FnLog(C4PropList * _this, C4Value * Pars) { Log(FnStringFormat(_this, Pars[0].getStr(), &Pars[1], 9).getData()); @@ -738,6 +845,33 @@ static bool FnFileWrite(C4PropList * _this, int32_t file_handle, C4String *data) C4ScriptConstDef C4ScriptConstMap[]= { + { "FX_OK" ,C4V_Int, C4Fx_OK }, // generic standard behaviour for all effect callbacks + { "FX_Effect_Deny" ,C4V_Int, C4Fx_Effect_Deny }, // delete effect + { "FX_Effect_Annul" ,C4V_Int, C4Fx_Effect_Annul }, // delete effect, because it has annulled a countereffect + { "FX_Effect_AnnulDoCalls" ,C4V_Int, C4Fx_Effect_AnnulCalls }, // delete effect, because it has annulled a countereffect; temp readd countereffect + { "FX_Execute_Kill" ,C4V_Int, C4Fx_Execute_Kill }, // execute callback: Remove effect now + { "FX_Stop_Deny" ,C4V_Int, C4Fx_Stop_Deny }, // deny effect removal + { "FX_Start_Deny" ,C4V_Int, C4Fx_Start_Deny }, // deny effect start + + { "FX_Call_Normal" ,C4V_Int, C4FxCall_Normal }, // normal call; effect is being added or removed + { "FX_Call_Temp" ,C4V_Int, C4FxCall_Temp }, // temp call; effect is being added or removed in responce to a lower-level effect change + { "FX_Call_TempAddForRemoval" ,C4V_Int, C4FxCall_TempAddForRemoval }, // temp call; effect is being added because it had been temp removed and is now removed forever + { "FX_Call_RemoveClear" ,C4V_Int, C4FxCall_RemoveClear }, // effect is being removed because object is being removed + { "FX_Call_RemoveDeath" ,C4V_Int, C4FxCall_RemoveDeath }, // effect is being removed because object died - return -1 to avoid removal + { "FX_Call_DmgScript" ,C4V_Int, C4FxCall_DmgScript }, // damage through script call + { "FX_Call_DmgBlast" ,C4V_Int, C4FxCall_DmgBlast }, // damage through blast + { "FX_Call_DmgFire" ,C4V_Int, C4FxCall_DmgFire }, // damage through fire + { "FX_Call_DmgChop" ,C4V_Int, C4FxCall_DmgChop }, // damage through chopping + { "FX_Call_Energy" ,C4V_Int, 32 }, // bitmask for generic energy loss + { "FX_Call_EngScript" ,C4V_Int, C4FxCall_EngScript }, // energy loss through script call + { "FX_Call_EngBlast" ,C4V_Int, C4FxCall_EngBlast }, // energy loss through blast + { "FX_Call_EngObjHit" ,C4V_Int, C4FxCall_EngObjHit }, // energy loss through object hitting the living + { "FX_Call_EngFire" ,C4V_Int, C4FxCall_EngFire }, // energy loss through fire + { "FX_Call_EngBaseRefresh" ,C4V_Int, C4FxCall_EngBaseRefresh }, // energy reload in base (also by base object, but that's normally not called) + { "FX_Call_EngAsphyxiation" ,C4V_Int, C4FxCall_EngAsphyxiation }, // energy loss through asphyxiaction + { "FX_Call_EngCorrosion" ,C4V_Int, C4FxCall_EngCorrosion }, // energy loss through corrosion (acid) + { "FX_Call_EngGetPunched" ,C4V_Int, C4FxCall_EngGetPunched }, // energy loss from punch + { "C4V_Nil", C4V_Int, C4V_Nil}, { "C4V_Int", C4V_Int, C4V_Int}, { "C4V_Bool", C4V_Int, C4V_Bool}, @@ -758,6 +892,7 @@ C4ScriptConstDef C4ScriptConstMap[]= C4ScriptFnDef C4ScriptFnMap[]= { { "Call", 1, C4V_Any, { C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnCall }, + { "EffectCall", 1, C4V_Any, { C4V_Object ,C4V_PropList,C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnEffectCall }, { "Log", 1, C4V_Bool, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnLog }, { "DebugLog", 1, C4V_Bool, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnDebugLog }, { "Format", 1, C4V_String, { C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any}, FnFormat }, @@ -799,6 +934,11 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine) F(GetProperty); F(SetProperty); F(ResetProperty); + F(AddEffect); + F(CheckEffect); + F(RemoveEffect); + F(GetEffect); + F(GetEffectCount); F(Distance); F(Angle); F(GetChar); diff --git a/src/script/C4ScriptStandaloneStubs.cpp b/src/script/C4ScriptStandaloneStubs.cpp index d2ef24c02..0fc894408 100644 --- a/src/script/C4ScriptStandaloneStubs.cpp +++ b/src/script/C4ScriptStandaloneStubs.cpp @@ -34,6 +34,13 @@ int32_t C4PropListNumbered::EnumerationIndex = 0; C4StringTable Strings; C4AulScriptEngine ScriptEngine; +/* Avoid a C4Object dependency */ +C4Effect ** FnGetEffectsFor(C4PropList * pTarget) +{ + if (pTarget) throw C4AulExecError("Only global effects are supported"); + return &ScriptEngine.pGlobalEffects; +} + /* Stubs */ C4Config Config; C4Config::C4Config() {} From e5cfb1858fb51689d9375e1bc65a17058e606928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sun, 20 Apr 2014 22:05:50 +0200 Subject: [PATCH 13/25] Script: GetName() can return the property a proplist was defined in This means that the ActMaps do not need to repeat their name anymore, provided that all Scripts use GetName() instead of directly accessing .Name. --- docs/sdk/script/fn/GetName.xml | 24 +++++++++++++++++++++--- src/object/C4ObjectScript.cpp | 9 --------- src/script/C4PropList.cpp | 8 ++++++++ src/script/C4PropList.h | 5 +++-- src/script/C4Script.cpp | 11 +++++++++++ 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/docs/sdk/script/fn/GetName.xml b/docs/sdk/script/fn/GetName.xml index a363ee119..41e83b240 100644 --- a/docs/sdk/script/fn/GetName.xml +++ b/docs/sdk/script/fn/GetName.xml @@ -7,8 +7,26 @@ GetName Objects 5.1 OC - string - Returns the name of an object or of an object definition. If the object does not have a name of its own, the definition name is returned anyway. + + string + + + bool + truename + Returns only the constant in which it was defined, ignoring the Name property. + + + + Returns the name of a proplist. This is either the contents of the Name property, or if that doesn't exist or the true name was requested, the name of the constant in which it was defined. + + + +static const Bee = { Buzz = func() {} }; +func Poke(proplist animal) { + if (animal->GetName(true) == "Bee") animal->Buzz(); +} + + - jwk2002-06 + Günther2014 diff --git a/src/object/C4ObjectScript.cpp b/src/object/C4ObjectScript.cpp index b0191cd99..58c6e5c73 100644 --- a/src/object/C4ObjectScript.cpp +++ b/src/object/C4ObjectScript.cpp @@ -196,14 +196,6 @@ static long FnGetCon(C4Object *Obj, long iPrec) return iPrec*Obj->GetCon()/FullCon; } -static C4String *FnGetName(C4PropList * _this) -{ - if (!_this) - throw NeedNonGlobalContext("GetName"); - else - return String(_this->GetName()); -} - static bool FnSetName(C4PropList * _this, C4String *pNewName, bool fSetInInfo, bool fMakeValidIfExists) { if (!Object(_this)) @@ -2611,7 +2603,6 @@ void InitObjectFunctionMap(C4AulScriptEngine *pEngine) ::AddFunc(p, "SetContactDensity", FnSetContactDensity, false); F(GetController); F(SetController); - F(GetName); F(SetName); F(GetKiller); F(SetKiller); diff --git a/src/script/C4PropList.cpp b/src/script/C4PropList.cpp index f25234531..57bc15581 100644 --- a/src/script/C4PropList.cpp +++ b/src/script/C4PropList.cpp @@ -264,6 +264,14 @@ StdStrBuf C4PropListStatic::GetDataString() const return r; } +const char *C4PropListStatic::GetName() const +{ + const C4String * s = GetPropertyStr(P_Name); + if (!s) s = ParentKeyName; + if (!s) return ""; + return s->GetCStr(); +} + C4PropList::C4PropList(C4PropList * prototype): FirstRef(NULL), prototype(prototype), constant(false), Status(1) diff --git a/src/script/C4PropList.h b/src/script/C4PropList.h index a76dd2dcb..8476131e1 100644 --- a/src/script/C4PropList.h +++ b/src/script/C4PropList.h @@ -66,7 +66,7 @@ class C4PropList { public: void Clear() { constant = false; Properties.Clear(); prototype.Set0(); } - const char *GetName() const; + virtual const char *GetName() const; virtual void SetName (const char *NewName = 0); virtual void SetOnFire(bool OnFire) { } @@ -255,8 +255,9 @@ public: virtual C4PropListStatic * IsStatic() { return this; } void RefCompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers) const; StdStrBuf GetDataString() const; + virtual const char *GetName() const; const C4PropListStatic * GetParent() { return Parent; } - const C4String * GetParentKeyName() { return ParentKeyName; } + C4String * GetParentKeyName() { return ParentKeyName; } protected: const C4PropListStatic * Parent; C4RefCntPointer ParentKeyName; // property in parent this proplist was created in diff --git a/src/script/C4Script.cpp b/src/script/C4Script.cpp index 6cd6739dc..c78a4fe15 100644 --- a/src/script/C4Script.cpp +++ b/src/script/C4Script.cpp @@ -327,6 +327,16 @@ static C4Value FnCall(C4PropList * _this, C4Value * Pars) return fn->Exec(_this, &ParSet, true); } +static C4String *FnGetName(C4PropList * _this, bool truename) +{ + if (!_this) + throw NeedNonGlobalContext("GetName"); + else if(truename) + return _this->IsStatic() ? _this->IsStatic()->GetParentKeyName() : nullptr; + else + return String(_this->GetName()); +} + /* Effects */ static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4PropList * pTarget, @@ -934,6 +944,7 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine) F(GetProperty); F(SetProperty); F(ResetProperty); + F(GetName); F(AddEffect); F(CheckEffect); F(RemoveEffect); From c167e990a51f2766567afa773abe2a289ea4f137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sun, 20 Apr 2014 22:06:47 +0200 Subject: [PATCH 14/25] Add GetName function to Actions --- planet/System.ocg/Action.c | 1 + 1 file changed, 1 insertion(+) diff --git a/planet/System.ocg/Action.c b/planet/System.ocg/Action.c index 6c765de1f..643fe2166 100644 --- a/planet/System.ocg/Action.c +++ b/planet/System.ocg/Action.c @@ -22,6 +22,7 @@ static const DFA_ATTACH = "ATTACH"; static const DFA_CONNECT = "CONNECT"; static const DFA_PULL = "PULL"; static const Action = { + GetName = Global.GetName, Length = 1, Directions = 1, Step = 1, From 50378ffda0a7fb6173af0f1deddafeb9dbcc7ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 23 Dec 2015 00:40:16 +0100 Subject: [PATCH 15/25] Script: CreateEffect starts effects that receive callbacks themselves --- docs/sdk/script/Effects.xml | 255 +++++++++++++------------ docs/sdk/script/fn/AddEffect.xml | 4 +- docs/sdk/script/fn/CheckEffect.xml | 2 +- docs/sdk/script/fn/CreateEffect.xml | 69 +++++++ docs/sdk/script/fn/EffectCall.xml | 2 +- docs/sdk/script/fn/GetEffect.xml | 2 +- docs/sdk/script/fn/GetEffectCount.xml | 2 +- docs/sdk/script/fn/RemoveEffect.xml | 2 +- src/game/C4Game.cpp | 2 +- src/game/C4GameScript.cpp | 2 + src/script/C4Effect.cpp | 60 +++++- src/script/C4Effect.h | 2 + src/script/C4Script.cpp | 13 ++ src/script/C4ScriptStandaloneStubs.cpp | 2 + src/script/C4StringTable.cpp | 5 + src/script/C4StringTable.h | 5 + 16 files changed, 289 insertions(+), 140 deletions(-) create mode 100644 docs/sdk/script/fn/CreateEffect.xml diff --git a/docs/sdk/script/Effects.xml b/docs/sdk/script/Effects.xml index e0bb8dfca..5be378ae2 100644 --- a/docs/sdk/script/Effects.xml +++ b/docs/sdk/script/Effects.xml @@ -51,7 +51,8 @@ func Destruction() The magic spell object exists until the spell has ended and then makes the clonk visible again. Also, if the spell object is deleted for other reasons (e.g. a scenario section change), the clonk is made visible in the Destruction callback (if this wasn't so, the clonk would remain invisible for ever). Also there is a Timer (defined in the DefCore) called every second. Notice you couldn't just have a single timer call to mark the end of the spell because timer intervals are marked in the engine beginning with the start of the round and you wouldn't know at what point within an engine timer interval the spell would start. However, there are some problems with this implementation: for example, the magician can not cast a second invisibility spell while he's already invisible - the second spell would have practically no effect, because the end of the first spell would make the clonk visible again. The spell script would have to do some special handling for this case - but not only for multiple invisibility spells, but also for any other spell or script that might affect visibility or coloration of the clonk. Even if this spell would remember the previous value e.g. for coloration it could not handle a situation in which other scripts change the color of their own in the middle of the spell. The same problems occur when multiple scripts modify temporary clonk physcials such as jumping, walking speed, fight strength or visibility range, energy, magic energy etc. Using effects, these conflicts can be avoided. Application - Effects are created using AddEffect and removed with RemoveEffect. If an effect was successfully created, the callback Fx*Start is made (* is replaced with the effect name). Depending on the parameters, there can also be an Fx*Timer call for continuous activity such as casting sparks, adjusting energy etc. Finally, when the effect is deleted, the callback Fx*Stop is made. Now, the invisibility spell implemented using effects: + Effects are created using CreateEffect and removed with RemoveEffect. If an effect was successfully created, the callback Construction is made. Depending on the parameters, there can also be an Timer call for continuous activity such as casting sparks, adjusting energy etc. Finally, when the effect is deleted, the callback Destruction is made. + Now, the invisibility spell implemented using effects: /* Invisibility spell with effect system */ // visibility - previous visibility @@ -59,92 +60,89 @@ func Destruction() func Activate(object caster, object caster2) { - // get caster - if (caster2) caster = caster2; + // get caster + if (caster2) caster = caster2; - // start effect - AddEffect("InvisPSpell", caster, 200, 1111, nil, GetID()); - // done - the spell object is not needed anymore - RemoveObject(); - return true; + // start effect + caster->CreateEffect(InvisPSpell, 200, 1111); + // done - the spell object is not needed anymore + RemoveObject(); + return true; } -func FxInvisPSpellStart(object target, proplist effect) +local InvisPSpell = { - // Save the casters previous visibility - effect.visibility = target.Visibility; - effect.old_mod = target->GetClrModulation(); - // Make the caster invisible - target.Visibility = VIS_Owner | VIS_Allies | VIS_God; - // Semitransparent and slightly blue for owner and allies - target->SetClrModulation(ModulateColor(effect.old_mod, RGBa(127,127,255,127))); - // Fertig - return true; -} - -func FxInvisPSpellStop(object target, proplist effect) -{ - // restore previous values - target->SetClrModulation(effect.old_mod); - target.Visibility = effect.visibility; - // done - return true; + Start = func(object target) + { + // Save the casters previous visibility + this.visibility = target.Visibility; + this.old_mod = target->GetClrModulation(); + // Make the caster invisible + target.Visibility = VIS_Owner | VIS_Allies | VIS_God; + // Semitransparent and slightly blue for owner and allies + target->SetClrModulation(ModulateColor(this.old_mod, RGBa(127,127,255,127))); + }, + Stop = func(object target) + { + // restore previous values + target->SetClrModulation(this.old_mod); + target.Visibility = this.visibility; + } } In this case, the magic spell object only starts the effect, then deletes itself immediately. The engine ensures that there are no conflicts with multiple effects modifying the visibility: effects are stored in a stack which ensures that effects are always removed in the opposite order of their addition. For this, there are a couple of extra Start and Stop calls to be made which are explained in detail later. This effects does not have a timer function. It does, however, define a timer interval of 1111 which will invoke the standard timer function after 1111 frames which will delete the effect. Alternatively, you could define a timer function as such: - func FxInvisPSpellTimer() + Timer = func() { - // return value of -1 means that the effect should be removed - return -1; + // return value of -1 means that the effect should be removed + return -1; } - To store the previous status of the target object, properties of the effect are used. This is necessary because in this case the effect callbacks do not have any object script context. So you cannot access any object local variables in the effect callbacks - remember that the magic spell object which has created the effect is already deleted. If you require an object context in the effect callbacks you can specify one in AddEffect(). In that case, effect callbacks would be in object local context and the effect would automatically be deleted if the target object is destroyed. + To store the previous status of the target object, properties of the effect are used. This way effects are independant of other objects and effects - remember that the magic spell object which has created the effect is already deleted. If you need to call functions in the context of the target object or other objects, use ->. Priorities - When creating an effect you always specify a priority value which determines the effect order. The engine ensures that effects with lower priority are added before effects with a higher priority - even if this means deleting an existing effect of higher priority. So if one effect colors the clonk green and another colors the clonk red, the result will be that of the effect with higher priority. If two effects have the same priority, the order is undefined. However, it is guaranteed that effects added later always notify the Fx*Effect callback of the same priority. - In the case of the red and green color, one effect could also determine the previous coloring and then mix a result using ModulateColor. But priorities also have another function: an effect of higher priority can prevent the addition of other effects of lower priority. This is done through the Fx*Effect callback. If any existing effect reacts to this callback with the return value -1, the new effect is not added (the same applies to the Start callback of the effect itself). Here an example: + When creating an effect you always specify a priority value which determines the effect order. The engine ensures that effects with lower priority are added before effects with a higher priority - even if this means deleting an existing effect of higher priority. So if one effect colors the clonk green and another colors the clonk red, the result will be that of the effect with higher priority. If two effects have the same priority, the order is undefined. However, it is guaranteed that effects added later always notify the Effect callback of the same priority. + In the case of the red and green color, one effect could also determine the previous coloring and then mix a result using ModulateColor. But priorities also have another function: an effect of higher priority can prevent the addition of other effects of lower priority. This is done through the Effect callback. If any existing effect reacts to this callback with the return value -1, the new effect is not added (the same applies to the Start callback of the effect itself). Here an example: /* Spell of immunity against fire */ func Activate(object caster, object caster2) { - // get caster - if (caster2) caster = caster2; + // get caster + if (caster2) caster = caster2; - // start effect - AddEffect("BanBurnPSpell", caster, 180, 1111, nil, GetID()); + // start effect + caster->CreateEffect(BanBurnPSpell, 180, 1111); - // done - the spell object is not needed anymore - RemoveObject(); - return true; + // done - the spell object is not needed anymore + RemoveObject(); + return true; } -func FxBanBurnPSpellStart(object target, proplist effect, bool temporary) -{ - // On start of the effect: extinguish clonk - if (!temporary) target->Extinguish(); - return true; -} - -func FxBanBurnPSpellEffect(string new_name) -{ - // block fire - if (WildcardMatch(new_name, "*Fire*")) return -1; - // everything else is ok - return 0; -} - This effect makes the clonk fire-proof for 30 seconds. The effect is implemented without any Timer or Stop callbacks as the complete functionality is achieved by simply blocking other effects which might have "Fire" as part of their name. This especially applies to the engine internal fire which has exactly the name "Fire". Of course, you could still add a Timer callback for graphic effects so the player can see that his clonk is immune. Also, you could create special visual effects when preventing incineration in FxBanBurnPSpellEffect. For the like: +local BanBurnPSpell = { + Construction = func(object target) + { + // On start of the effect: extinguish clonk + target->Extinguish(); + }, + Effect = func(string new_name) + { + // block fire + if (WildcardMatch(new_name, "*Fire*")) return -1; + // everything else is ok + return 0; + } +}; + This effect makes the clonk fire-proof for 30 seconds. The effect is implemented without any Timer or Stop callbacks as the complete functionality is achieved by simply blocking other effects which might have "Fire" as part of their name. This especially applies to the engine internal fire which has exactly the name "Fire". Of course, you could still add a Timer callback for graphic effects so the player can see that his clonk is immune. Also, you could create special visual effects when preventing incineration in Effect. For the like: [...] - -func FxBanBurnPSpellEffect(string new_name, object target, proplist effect, var1, var2, var3) +Effect = func(string new_name, object target, var1, var2, var3, var4) { - // only handle fire - if (!WildcardMatch(new_name, "*Fire*")) return 0; - // with fire, the three extra parameters have the following meaning: - // var1: caused_by - player that is responsible for the fire - // var2: blasted - bool: if the fire has been created by an explosion - // var3: burning_object - object: incineratable object - // extinguish burning object - if (var3 && GetType(var3) == C4V_C4Object) var3->Extinguish(); - // block fire - return -1; + // only handle fire + if (!WildcardMatch(new_name, "*Fire*")) return 0; + // with fire, the three extra parameters have the following meaning: + // var1: caused_by - player that is responsible for the fire + // var2: blasted - bool: if the fire has been created by an explosion + // var3: burning_object - object: incineratable object + // extinguish burning object + if (var3 && GetType(var3) == C4V_C4Object) var3->Extinguish(); + // block fire + return -1; } This would even delete all burning objects which would otherwise incinerate the target object. The type check for var3 avoids possible conflicts with other "Fire" effects that might have differing parameters. Obviously, conflict situations like this should be avoided at all cost. The following table contains general guidelines for priorities in effects of the original pack: @@ -210,44 +208,46 @@ func FxBanBurnPSpellEffect(string new_name, object target, proplist effect, var1 func Activate(object caster, object caster2) { - // get caster - if (caster2) caster = caster2; + // get caster + if (caster2) caster = caster2; - // start effect - AddEffect("ReincarnationPSpell", caster, 180, 0, nil, GetID()); - - // done - the spell object is not needed anymore - RemoveObject(); - return true; + // start effect + caster->CreateEffect(ReincarnationPSpell, 180, 0); + + // done - the spell object is not needed anymore + RemoveObject(); + return true; } -func FxReincarnationPSpellStart(object target, proplist effect, bool temporary) -{ - // Only at the first start: message - if (!temporary) target->Message("%s gets an extra life", target->GetName()); - return true; -} +local ReincarnationPSpell = { + Construction = func(object target) + { + // Only at the first start: message + target->Message("%s gets an extra life", target->GetName()); + return true; + }, -func FxReincarnationPSpellStop(object target, proplist effect, int reason, bool temporary) -{ - // only when the clonk died - if (reason != 4) return true; - - // the clonk has already been resurrected - if (target->GetAlive()) return -1; - // resurrect clonk - target->SetAlive(true); - // give energy - target->DoEnergy(100); - // message - target->Message("%s has been resurrected.", target->GetName()); + func Stop(object target, int reason, bool temporary) + { + // only when the clonk died + if (reason != 4) return true; + + // the clonk has already been resurrected + if (target->GetAlive()) return -1; + // resurrect clonk + target->SetAlive(true); + // give energy + target->DoEnergy(100); + // message + target->Message("%s has been resurrected.", target->GetName()); - // remove - return true; -} + // remove + return true; + } +}; This effect reanimates the clonk as many times as he has cast the reanimation spell. Global Effects - Global effects are effects that are not bound to any target object. With global effects, too, priorities are observed and temporary Add/Remove calls might be necessary to ensure order. Simply imagine all global effects are attached to an imaginary object. Global effects are accessed whenever you specify nil for the target object. + There are two global effect types: Scenerio effects and global effects. They are bound to the Scenario and Global proplists. With these effects, too, priorities are observed and temporary Add/Remove calls might be necessary to ensure order. This can be used to make changes to gravity, sky color, etc. Here's an example for a spell that temporarily reduces gravity and then resets the original value: /* Gravitation spell */ @@ -488,19 +488,20 @@ global func FxExplodeOnDeathCurseStop(object target, proplist effect, int reason - Warning: as function names may not be more than 100 characters in length (and you will lose oversight eventually), you should not stuff too much information into the effect name. Effect names are case sensitive. Also, you should avoid using any of these identifiers in your effect names if your effect doesn't have anything to do with them. + Effect names are case sensitive. Also, you should avoid using any of these identifiers in your effect names if your effect doesn't have anything to do with them. Callback Reference - The following callbacks are made by the engine and should be implemented in your script according to necessity. * is to be replaced by your effect name. - Fx*Start - int Fx*Start (object target, proplist effect, int temporary, any var1, any var2, any var3, any var4); - Called at the start of the effect. target is the target object of the effect. effect is the effect itself. effect can be used to manipulate the effect, for example with effect.Interval=newinterval. + The following callbacks are made by the engine and should be implemented in your effect prototype as necessary. + All parameters receive the target as their first or second parameter. This is either an object, the Global or the Scenario proplist. + Start + int Start (object target, int temporary, any var1, any var2, any var3, any var4); + Called at the start of the effect. target is the target object of the effect. this is the effect itself. It can be used to manipulate the effect, for example with this.Interval=newinterval. In normal operation the parameter temporary is 0. It is 1 if the effect is re-added after having been temporarily removed and 2 if the effect was temporarily removed and is now to be deleted (in this case a Remove call will follow). - If temporary is 0, var1 to var4 are the additional parameters passed to AddEffect(). - If temporary is 0 and this callback returns -1 the effect is not created and the corrsponding AddEffect() call returns 0. - Fx*Stop - int Fx*Stop (object target, proplist effect, int reason, bool temporary); - When the effect is temporarily or permanently removed. target again is the target object and effect the effect itself. + If temporary is 0, var1 to var4 are the additional parameters passed to CreateEffect(). + If temporary is 0 and this callback returns -1 the effect is not created and the corrsponding CreateEffect() call returns nil. + Stop + int Stop (object target, int reason, bool temporary); + When the effect is temporarily or permanently removed. target again is the target object and this the effect itself. reason contains the cause of the removal and can be one of the following values: @@ -537,25 +538,33 @@ global func FxExplodeOnDeathCurseStop(object target, proplist effect, int reason
The effect can prevent removal by returning -1. This will not help, however, in temporary removals or if the target object has been deleted. - Fx*Timer - int Fx*Timer (object target, proplist effect, int time); - Periodic timer call, if a timer interval has been specified at effect creation. target and effect as usual. + Construction + int Construction (object target, any var1, any var2, any var3, any var4); + Called when the effect is first created, before it is started. The parameters var1 to var4 are passed through from CreateEffect. + The return value is ignored. + Destruction + nil Destruction (object target, int reason); + Callback when the effect is removed. reason is the same as in the preceding Stop call, see above. + The return value is ignored. + Timer + int Timer (object target, int time); + Periodic timer call, if a timer interval has been specified at effect creation. time specifies how long the effect has now been active. This might alternatively be determined using effect.Time. If this function is not implemented or returns -1, the effect will be deleted after this call. - Fx*Effect - int Fx*Effect (string new_name, object target, proplist effect, any var1, any var2, any var3, any var4); - A call to all effects of higher priority if a new effect is to be added to the same target object. new_name is the name of the new effect; effect is the effect being called. + Effect + int Effect (string new_name, object target, any var1, any var2, any var3, any var4); + A call to all effects of higher priority if a new effect is to be added to the same target object. new_name is the name of the new effect; this is the effect being called. Warning: the new effect is not yet properly initialized and should not be manipulated in any way. Especially the priority field might not yet have been set. This function can return -1 to reject the new effect. As the new effect might also be rejected by other effects, this callback should not try to add effects or similar (see gravitation spell). Generally you should not try to manipulate any effects during this callback. - Return -2 or -3 to accept the new effect. As long as the new effect is not rejected by any other effect, the Fx*Add call is then made to the accepting effect, the new effect is not actually created, and the calling AddEffect function returns the effect index of the accepting effect. The return value -3 will also temporarily remove all higher prioriy effects just before the Fx*Add callback and re-add them later. - var1 bis var4 are the parameters passed to AddEffect() - Fx*Add - int Fx*Add (object target, proplist effect, string new_name, int new_timer, any var1, any var2, any var3, any var4); - Callback to the accepting effect if that has returned -2 or -3 to a prior Fx*Effect call. effect identifies the accepting effect to which the consequences of the new effect will be added; target is the target object (0 for global effects). + Return -2 or -3 to accept the new effect. As long as the new effect is not rejected by any other effect, the Add call is then made to the accepting effect, the new effect is not actually created, and the calling CreateEffect function returns the accepting effect. The return value -3 will also temporarily remove all higher prioriy effects just before the Add callback and re-add them later. + var1 bis var4 are the parameters passed to CreateEffect() + Add + int Add (object target, string new_name, int new_timer, any var1, any var2, any var3, any var4); + Callback to the accepting effect if that has returned -2 or -3 to a prior Effect call. this identifies the accepting effect to which the consequences of the new effect will be added; target is the target object (0 for global effects). new_timer is the timer interval of the new effect; var1 to var4 are the parameters from AddEffect. Notice: in temporary calls, these parameters are not available - here they will be 0. - If -1 is returned, the accepting effect is deleted also. Logically, the calling AddEffect function will then return -2. - Fx*Damage - int Fx*Damage (object target, proplist effect, int damage, int cause, int by_player); + If -1 is returned, the accepting effect is deleted also. Logically, the calling CreateEffect function will then return nil. + Damage + int Damage (object target, int damage, int cause, int by_player); Every effect receives this callback whenever the energy or damage value of the target object is to change. If the function is defined, it should then return the damage to be done to the target. This callback is made upon life energy changes in living beings and damage value changes in non-livings - but not vice versa. cause contains the value change and reason: @@ -639,7 +648,7 @@ global func FxExplodeOnDeathCurseStop(object target, proplist effect, int reason There are the following functions for manipulation of effects:
    -
  • AddEffect() - for effect creation
  • +
  • CreateEffect() - for effect creation
  • RemoveEffect() - for effect removal
  • GetEffect() - to search for effects
  • GetEffectCount() - for effect counting
  • diff --git a/docs/sdk/script/fn/AddEffect.xml b/docs/sdk/script/fn/AddEffect.xml index 6c5aa8ef4..aba43a545 100644 --- a/docs/sdk/script/fn/AddEffect.xml +++ b/docs/sdk/script/fn/AddEffect.xml @@ -7,6 +7,7 @@ AddEffect Effects 5.1 OC + proplist @@ -18,7 +19,7 @@ object target - Target object for the effect. If nil, a global effect is created. + Target object for the effect. If nil, Global is used, but the target parameter of the callbacks will get nil. @@ -74,6 +75,7 @@ For examples and more information see the effects documentation. Effects Documentation + CreateEffect CheckEffect GetEffectCount EffectCall diff --git a/docs/sdk/script/fn/CheckEffect.xml b/docs/sdk/script/fn/CheckEffect.xml index 7dbd05451..9ca7f7130 100644 --- a/docs/sdk/script/fn/CheckEffect.xml +++ b/docs/sdk/script/fn/CheckEffect.xml @@ -63,7 +63,7 @@ For examples and more information see the effects documentation. Effects Documentation - AddEffect + CreateEffect GetEffectCount EffectCall GetEffect diff --git a/docs/sdk/script/fn/CreateEffect.xml b/docs/sdk/script/fn/CreateEffect.xml new file mode 100644 index 000000000..01d2719d4 --- /dev/null +++ b/docs/sdk/script/fn/CreateEffect.xml @@ -0,0 +1,69 @@ + + + + + + CreateEffect + Effects + 5.5 OC + + proplist + + + proplist + prototype + A proplist containing the callback functions for the new Effect. The name (GetName) of this proplist becomes the name of the effect. + + + int + priority + Effect priority. Must be greater than zero. + + + int + timer + + Interval for the timer calls. With nil, no timer calls are made and the effect stays on permanently until it is deleted by other calls. + + + any + var1 + + First extra parameter to be passed to Construction, Start and Effect callbacks. + + + any + var2 + + Second extra parameter to be passed to Construction, Start and Effect callbacks. + + + any + var3 + + Third extra parameter to be passed to Construction, Start and Effect callbacks. + + + any + var4 + + Fourth extra parameter to be passed to Construction, Start and Effect callbacks. + + + + Creates an effect. Returns the effect if successful or nil if not (e.g. because the effect was rejected). If the effect was accepted by another effect which is deleting itself within the same call, the return value is probably nil. Effects can be created on objects, Global and Scenario. This is passed as the first parameter to the effect callbacks. + + For examples and more information see the effects documentation. + + Effects Documentation + CheckEffect + GetEffectCount + EffectCall + GetEffect + RemoveEffect + + + Sven22004-03 + Günther2014 + diff --git a/docs/sdk/script/fn/EffectCall.xml b/docs/sdk/script/fn/EffectCall.xml index 5bf4683ad..4da78a910 100644 --- a/docs/sdk/script/fn/EffectCall.xml +++ b/docs/sdk/script/fn/EffectCall.xml @@ -37,7 +37,7 @@ For examples and more information see the effects documentation. Effects Documentation - AddEffect + CreateEffect CheckEffect GetEffectCount GetEffect diff --git a/docs/sdk/script/fn/GetEffect.xml b/docs/sdk/script/fn/GetEffect.xml index 2c80d48be..b70c06e11 100644 --- a/docs/sdk/script/fn/GetEffect.xml +++ b/docs/sdk/script/fn/GetEffect.xml @@ -59,7 +59,7 @@ i = GetEffectCount(); Effects Documentation - AddEffect + CreateEffect CheckEffect GetEffectCount EffectCall diff --git a/docs/sdk/script/fn/GetEffectCount.xml b/docs/sdk/script/fn/GetEffectCount.xml index a02f6ea9f..5c63c0bb9 100644 --- a/docs/sdk/script/fn/GetEffectCount.xml +++ b/docs/sdk/script/fn/GetEffectCount.xml @@ -34,7 +34,7 @@ For an example see GetEffect. Effects Documentation - AddEffect + CreateEffect CheckEffect EffectCall GetEffect diff --git a/docs/sdk/script/fn/RemoveEffect.xml b/docs/sdk/script/fn/RemoveEffect.xml index 6becb7630..38c15a8b1 100644 --- a/docs/sdk/script/fn/RemoveEffect.xml +++ b/docs/sdk/script/fn/RemoveEffect.xml @@ -40,7 +40,7 @@ See GetEffect for an example. Warning: if an effect is meant to delete itself using this function, only use effect, not name! Effects Documentation - AddEffect + CreateEffect CheckEffect GetEffectCount EffectCall diff --git a/src/game/C4Game.cpp b/src/game/C4Game.cpp index 5b3de79f4..f5754326e 100644 --- a/src/game/C4Game.cpp +++ b/src/game/C4Game.cpp @@ -720,7 +720,7 @@ bool C4Game::Execute() // Returns true if the game is over // Game EXEC_S( ExecObjects(); , ExecObjectsStat ) - EXEC_S_DR( C4Effect::Execute(NULL, &ScriptEngine.pGlobalEffects); + EXEC_S_DR( C4Effect::Execute(ScriptEngine.GetPropList(), &ScriptEngine.pGlobalEffects); , GEStats , "GEEx\0"); EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx") EXEC_S_DR( MassMover.Execute(); , MassMoverStat , "MMvEx") diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index aa6249892..83fafb346 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -47,6 +47,8 @@ C4Effect ** FnGetEffectsFor(C4PropList * pTarget) { if (pTarget) { + if (pTarget == ScriptEngine.GetPropList()) + return &ScriptEngine.pGlobalEffects; C4Object * Obj = pTarget->GetObject(); if (!Obj) throw C4AulExecError("Effect target has to be an object"); diff --git a/src/script/C4Effect.cpp b/src/script/C4Effect.cpp index 19c65980e..47064b0e7 100644 --- a/src/script/C4Effect.cpp +++ b/src/script/C4Effect.cpp @@ -54,6 +54,18 @@ C4Effect::C4Effect(C4Effect **ppEffectList, C4String *szName, int32_t iPrio, int SetProperty(P_Name, C4VString(szName)); } +C4Effect::C4Effect(C4Effect **ppEffectList, C4PropList * prototype, int32_t iPrio, int32_t iTimerInterval): + C4PropListNumbered(prototype) +{ + // assign values + iPriority = 0; // effect is not yet valid; some callbacks to other effects are done before + iInterval = iTimerInterval; + iTime = 0; + CommandTarget.Set0(); + AcquireNumber(); + Register(ppEffectList, iPrio); +} + void C4Effect::Register(C4Effect **ppEffectList, int32_t iPrio) { // get effect target @@ -80,6 +92,12 @@ C4Effect * C4Effect::New(C4PropList *pForObj, C4Effect **ppEffectList, C4String return pEffect->Init(pForObj, iPrio, rVal1, rVal2, rVal3, rVal4); } +C4Effect * C4Effect::New(C4PropList *pForObj, C4Effect **ppEffectList, C4PropList * prototype, int32_t iPrio, int32_t iTimerInterval, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) +{ + C4Effect * pEffect = new C4Effect(ppEffectList, prototype, iPrio, iTimerInterval); + return pEffect->Init(pForObj, iPrio, rVal1, rVal2, rVal3, rVal4); +} + C4Effect * C4Effect::Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) { // ask all effects with higher priority first - except for prio 1 effects, which are considered out of the priority call chain (as per doc) @@ -106,7 +124,12 @@ C4Effect * C4Effect::Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVa // because that would cause a wrong initialization order // (hardly ever causing trouble, however...) C4Effect *pLastRemovedEffect=NULL; - if (fRemoveUpper && pNext && pFnStart) + C4AulFunc * pFn; + if (!GetCallbackScript()) + pFn = GetFunc(P_Start); + else + pFn = pFnStart; + if (fRemoveUpper && pNext && pFn) TempRemoveUpperEffects(pForObj, false, &pLastRemovedEffect); // bad things may happen if (pForObj && !pForObj->Status) return 0; // this will be invalid! @@ -114,7 +137,7 @@ C4Effect * C4Effect::Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVa if (CallStart(pForObj, 0, rVal1, rVal2, rVal3, rVal4) == C4Fx_Start_Deny) // the effect denied to start: assume it hasn't, and mark it dead SetDead(); - if (fRemoveUpper && pNext && pFnStart) + if (fRemoveUpper && pNext && pFn) TempReaddUpperEffects(pForObj, pLastRemovedEffect); if (pForObj && !pForObj->Status) return 0; // this will be invalid! // Update OnFire cache @@ -360,43 +383,60 @@ void C4Effect::DoDamage(C4PropList *pObj, int32_t &riDamage, int32_t iDamageType while ((pEff = pEff->pNext) && riDamage); } +static C4Object * Obj(C4PropList * p) { return p ? p->GetObject() : NULL; } + C4Value C4Effect::DoCall(C4PropList *pObj, const char *szFn, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4, const C4Value &rVal5, const C4Value &rVal6, const C4Value &rVal7) { - // def script or global only? C4PropList *p = GetCallbackScript(); + if (!p) return Call(szFn, &C4AulParSet(pObj, rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); + // old variant // compose function name char fn[C4AUL_MAX_Identifier+1]; sprintf(fn, PSF_FxCustom, GetName(), szFn); - return p->Call(fn, &C4AulParSet(pObj, this, rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); + return p->Call(fn, &C4AulParSet(Obj(pObj), this, rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); } int C4Effect::CallStart(C4PropList * obj, int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { + if (!GetCallbackScript()) + return Call(P_Start, &C4AulParSet(obj, temporary, var1, var2, var3, var4)).getInt(); if (pFnStart) - return pFnStart->Exec(GetCallbackScript(), &C4AulParSet(obj, this, temporary, var1, var2, var3, var4)).getInt(); + return pFnStart->Exec(GetCallbackScript(), &C4AulParSet(Obj(obj), this, temporary, var1, var2, var3, var4)).getInt(); return C4Fx_OK; } int C4Effect::CallStop(C4PropList * obj, int reason, bool temporary) { + if (!GetCallbackScript()) + return Call(P_Stop, &C4AulParSet(obj, reason, temporary)).getInt(); if (pFnStop) - return pFnStop->Exec(GetCallbackScript(), &C4AulParSet(obj, this, reason, temporary)).getInt(); + return pFnStop->Exec(GetCallbackScript(), &C4AulParSet(Obj(obj), this, reason, temporary)).getInt(); return C4Fx_OK; } int C4Effect::CallTimer(C4PropList * obj, int time) { + if (!GetCallbackScript()) + return Call(P_Timer, &C4AulParSet(obj, time)).getInt(); if (pFnTimer) - return pFnTimer->Exec(GetCallbackScript(), &C4AulParSet(obj, this, time)).getInt(); + return pFnTimer->Exec(GetCallbackScript(), &C4AulParSet(Obj(obj), this, time)).getInt(); return C4Fx_Execute_Kill; } void C4Effect::CallDamage(C4PropList * obj, int32_t & damage, int damagetype, int plr) { - if (pFnDamage) - damage = pFnDamage->Exec(GetCallbackScript(), &C4AulParSet(obj, this, damage, damagetype, plr)).getInt(); + if (!GetCallbackScript()) + { + C4AulFunc *pFn = GetFunc(P_Damage); + if (pFn) + damage = pFn->Exec(this, &C4AulParSet(obj, damage, damagetype, plr)).getInt(); + } + else if (pFnDamage) + damage = pFnDamage->Exec(GetCallbackScript(), &C4AulParSet(Obj(obj), this, damage, damagetype, plr)).getInt(); } int C4Effect::CallEffect(const char * effect, C4PropList * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { + if (!GetCallbackScript()) + return Call(P_Effect, &C4AulParSet(effect, obj, var1, var2, var3, var4)).getInt(); if (pFnEffect) - return pFnEffect->Exec(GetCallbackScript(), &C4AulParSet(effect, obj, this, var1, var2, var3, var4)).getInt(); + return pFnEffect->Exec(GetCallbackScript(), &C4AulParSet(effect, Obj(obj), this, var1, var2, var3, var4)).getInt(); return C4Fx_OK; } diff --git a/src/script/C4Effect.h b/src/script/C4Effect.h index 46163ec3f..b92f7e002 100644 --- a/src/script/C4Effect.h +++ b/src/script/C4Effect.h @@ -91,12 +91,14 @@ protected: int CallEffect(const char * effect, C4PropList * obj, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4); C4Effect(C4Effect **ppEffectList, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget); + C4Effect(C4Effect **ppEffectList, C4PropList * prototype, int32_t iPrio, int32_t iTimerInterval); C4Effect(const C4Effect &); // unimplemented, do not use C4Effect(); // for the StdCompiler C4Effect * Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); friend void CompileNewFunc(C4Effect *&, StdCompiler *, C4ValueNumbers * const &); public: static C4Effect * New(C4PropList *pForObj, C4Effect **ppEffectList, C4String * szName, int32_t iPrio, int32_t iTimerInterval, C4PropList * pCmdTarget, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); + static C4Effect * New(C4PropList *pForObj, C4Effect **ppEffectList, C4PropList * prototype, int32_t iPrio, int32_t iTimerInterval, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4); ~C4Effect(); // dtor - deletes all following effects void Register(C4Effect **ppEffectList, int32_t iPrio); // add into effect list of object or global effect list diff --git a/src/script/C4Script.cpp b/src/script/C4Script.cpp index c78a4fe15..d3b121a3d 100644 --- a/src/script/C4Script.cpp +++ b/src/script/C4Script.cpp @@ -357,6 +357,18 @@ static C4Value FnAddEffect(C4PropList * _this, C4String * szEffect, C4PropList * return C4VPropList(pEffect); } +static C4Effect * FnCreateEffect(C4PropList * _this, C4PropList * prototype, int iPrio, int iTimerInterval, + const C4Value & Val1, const C4Value & Val2, const C4Value & Val3, const C4Value & Val4) +{ + if (!prototype || !(prototype->GetName()[0])) throw C4AulExecError("CreateEffect needs a prototype with a name"); + if (!iPrio) throw C4AulExecError("CreateEffect needs a nonzero priority"); + // create effect + C4Effect * pEffect = C4Effect::New(_this, FnGetEffectsFor(_this), prototype, iPrio, iTimerInterval, + Val1, Val2, Val3, Val4); + // return effect - may be 0 if the effect has been denied by another effect + return pEffect; +} + static C4Effect * FnGetEffect(C4PropList * _this, C4String *psEffectName, C4PropList *pTarget, int index, int iMaxPriority) { const char *szEffect = FnStringPar(psEffectName); @@ -946,6 +958,7 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine) F(ResetProperty); F(GetName); F(AddEffect); + F(CreateEffect); F(CheckEffect); F(RemoveEffect); F(GetEffect); diff --git a/src/script/C4ScriptStandaloneStubs.cpp b/src/script/C4ScriptStandaloneStubs.cpp index 0fc894408..3823f9a93 100644 --- a/src/script/C4ScriptStandaloneStubs.cpp +++ b/src/script/C4ScriptStandaloneStubs.cpp @@ -37,6 +37,8 @@ C4AulScriptEngine ScriptEngine; /* Avoid a C4Object dependency */ C4Effect ** FnGetEffectsFor(C4PropList * pTarget) { + if (pTarget == ScriptEngine.GetPropList()) + return &ScriptEngine.pGlobalEffects; if (pTarget) throw C4AulExecError("Only global effects are supported"); return &ScriptEngine.pGlobalEffects; } diff --git a/src/script/C4StringTable.cpp b/src/script/C4StringTable.cpp index eb0ba6ce3..ee70ae544 100644 --- a/src/script/C4StringTable.cpp +++ b/src/script/C4StringTable.cpp @@ -87,6 +87,11 @@ C4StringTable::C4StringTable() P[P_Interval] = "Interval"; P[P_CommandTarget] = "CommandTarget"; P[P_Time] = "Time"; + P[P_Start] = "Start"; + P[P_Stop] = "Stop"; + P[P_Timer] = "Timer"; + P[P_Effect] = "Effect"; + P[P_Damage] = "Damage"; P[P_Collectible] = "Collectible"; P[P_Touchable] = "Touchable"; P[P_ActMap] = "ActMap"; diff --git a/src/script/C4StringTable.h b/src/script/C4StringTable.h index cf436585a..61a318874 100644 --- a/src/script/C4StringTable.h +++ b/src/script/C4StringTable.h @@ -294,6 +294,11 @@ enum C4PropertyName P_Interval, P_CommandTarget, P_Time, + P_Start, + P_Stop, + P_Timer, + P_Effect, + P_Damage, P_Collectible, P_Touchable, P_ActMap, From 97406d24d2be0c7a3684089ca7b3dc0b865d7615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 23 Dec 2015 01:17:31 +0100 Subject: [PATCH 16/25] Script: Add Construction and Destruction effect callbacks --- src/script/C4Effect.cpp | 8 ++++++++ src/script/C4StringTable.cpp | 2 ++ src/script/C4StringTable.h | 2 ++ 3 files changed, 12 insertions(+) diff --git a/src/script/C4Effect.cpp b/src/script/C4Effect.cpp index 47064b0e7..3c885db55 100644 --- a/src/script/C4Effect.cpp +++ b/src/script/C4Effect.cpp @@ -126,7 +126,11 @@ C4Effect * C4Effect::Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVa C4Effect *pLastRemovedEffect=NULL; C4AulFunc * pFn; if (!GetCallbackScript()) + { + Call(P_Construction, &C4AulParSet(pForObj, rVal1, rVal2, rVal3, rVal4)).getInt(); + if (pForObj && !pForObj->Status) return 0; pFn = GetFunc(P_Start); + } else pFn = pFnStart; if (fRemoveUpper && pNext && pFn) @@ -345,6 +349,8 @@ void C4Effect::Kill(C4PropList *pObj) if (pObj && WildcardMatch(C4Fx_AnyFire, GetName())) if (!Get(C4Fx_AnyFire)) pObj->SetOnFire(false); + if (IsDead() && !GetCallbackScript()) + Call(P_Destruction, &C4AulParSet(pObj, C4FxCall_Normal)); } void C4Effect::ClearAll(C4PropList *pObj, int32_t iClearFlag) @@ -368,6 +374,8 @@ void C4Effect::ClearAll(C4PropList *pObj, int32_t iClearFlag) if (pObj && WildcardMatch(C4Fx_AnyFire, GetName()) && IsDead()) if (!Get(C4Fx_AnyFire)) pObj->SetOnFire(false); + if (IsDead() && !GetCallbackScript()) + Call(P_Destruction, &C4AulParSet(pObj, iClearFlag)); } void C4Effect::DoDamage(C4PropList *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr) diff --git a/src/script/C4StringTable.cpp b/src/script/C4StringTable.cpp index ee70ae544..49046e9ac 100644 --- a/src/script/C4StringTable.cpp +++ b/src/script/C4StringTable.cpp @@ -87,6 +87,8 @@ C4StringTable::C4StringTable() P[P_Interval] = "Interval"; P[P_CommandTarget] = "CommandTarget"; P[P_Time] = "Time"; + P[P_Construction] = "Construction"; + P[P_Destruction] = "Destruction"; P[P_Start] = "Start"; P[P_Stop] = "Stop"; P[P_Timer] = "Timer"; diff --git a/src/script/C4StringTable.h b/src/script/C4StringTable.h index 61a318874..c2d1e19b6 100644 --- a/src/script/C4StringTable.h +++ b/src/script/C4StringTable.h @@ -294,6 +294,8 @@ enum C4PropertyName P_Interval, P_CommandTarget, P_Time, + P_Construction, + P_Destruction, P_Start, P_Stop, P_Timer, From efd472408425f39bd4d4e920dc7800b4d8781d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 23 Dec 2015 23:10:48 +0100 Subject: [PATCH 17/25] Script: Add Scenario effects These work just like global effects, except that effects from CreateEffect get the scenario as target parameter in the callbacks. --- src/game/C4Game.cpp | 5 ++++ src/game/C4GameScript.cpp | 2 ++ src/object/C4Object.cpp | 2 ++ src/script/C4Aul.cpp | 41 +++++++++++++++----------- src/script/C4AulLink.cpp | 1 + src/script/C4ScriptHost.cpp | 8 +++++ src/script/C4ScriptHost.h | 2 ++ src/script/C4ScriptStandaloneStubs.cpp | 4 ++- 8 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/game/C4Game.cpp b/src/game/C4Game.cpp index f5754326e..4001f43d8 100644 --- a/src/game/C4Game.cpp +++ b/src/game/C4Game.cpp @@ -721,6 +721,7 @@ bool C4Game::Execute() // Returns true if the game is over EXEC_S( ExecObjects(); , ExecObjectsStat ) EXEC_S_DR( C4Effect::Execute(ScriptEngine.GetPropList(), &ScriptEngine.pGlobalEffects); + C4Effect::Execute(GameScript.GetPropList(), &GameScript.pScenarioEffects); , GEStats , "GEEx\0"); EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx") EXEC_S_DR( MassMover.Execute(); , MassMoverStat , "MMvEx") @@ -910,6 +911,8 @@ void C4Game::ClearPointers(C4Object * pObj) TransferZones.ClearPointers(pObj); if (::ScriptEngine.pGlobalEffects) ::ScriptEngine.pGlobalEffects->ClearPointers(pObj); + if (::GameScript.pScenarioEffects) + ::GameScript.pScenarioEffects->ClearPointers(pObj); if (::Landscape.pFoW) ::Landscape.pFoW->Remove(pObj); } @@ -3483,6 +3486,8 @@ bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags) // scenario section call might have been done from a global effect // rely on dead effect removal for actually removing the effects; do not clear the array here! } + if (::GameScript.pScenarioEffects && !(dwFlags & C4S_KEEP_EFFECTS)) + ::GameScript.pScenarioEffects->ClearAll(NULL, C4FxCall_RemoveClear); // del particles as well Particles.ClearAllParticles(); // clear transfer zones diff --git a/src/game/C4GameScript.cpp b/src/game/C4GameScript.cpp index 83fafb346..9fc1a65db 100644 --- a/src/game/C4GameScript.cpp +++ b/src/game/C4GameScript.cpp @@ -49,6 +49,8 @@ C4Effect ** FnGetEffectsFor(C4PropList * pTarget) { if (pTarget == ScriptEngine.GetPropList()) return &ScriptEngine.pGlobalEffects; + if (pTarget == GameScript.ScenPrototype.getPropList() || pTarget == GameScript.ScenPropList.getPropList()) + return &GameScript.pScenarioEffects; C4Object * Obj = pTarget->GetObject(); if (!Obj) throw C4AulExecError("Effect target has to be an object"); diff --git a/src/object/C4Object.cpp b/src/object/C4Object.cpp index c05bc58b1..25fe8e079 100644 --- a/src/object/C4Object.cpp +++ b/src/object/C4Object.cpp @@ -1197,6 +1197,8 @@ bool C4Object::ChangeDef(C4ID idNew) // This is ugly, because every effect there is must be updated... if (::ScriptEngine.pGlobalEffects) ::ScriptEngine.pGlobalEffects->OnObjectChangedDef(this); + if (::GameScript.pScenarioEffects) + ::GameScript.pScenarioEffects->OnObjectChangedDef(this); for (C4Object *obj : Objects) if (obj->pEffects) obj->pEffects->OnObjectChangedDef(this); // Containment (no Entrance) diff --git a/src/script/C4Aul.cpp b/src/script/C4Aul.cpp index 1252a00ff..b53d472fa 100644 --- a/src/script/C4Aul.cpp +++ b/src/script/C4Aul.cpp @@ -115,11 +115,31 @@ void C4AulScriptEngine::Denumerate(C4ValueNumbers * numbers) { GlobalNamed.Denumerate(numbers); // runtime data only: don't denumerate consts - GameScript.ScenPropList.Denumerate(numbers); + GameScript.Denumerate(numbers); C4PropListStaticMember::Denumerate(numbers); if (pGlobalEffects) pGlobalEffects->Denumerate(numbers); } +static void GlobalEffectsMergeCompileFunc(StdCompiler *pComp, C4Effect * & pEffects, const char * name, C4ValueNumbers * numbers) +{ + C4Effect *pOldEffect, *pNextOldEffect=pEffects; + pEffects = NULL; + try + { + pComp->Value(mkParAdapt(mkNamingPtrAdapt(pEffects, name), numbers)); + } + catch (...) + { + delete pNextOldEffect; + throw; + } + while ((pOldEffect=pNextOldEffect)) + { + pNextOldEffect = pOldEffect->pNext; + pOldEffect->Register(&pEffects, Abs(pOldEffect->iPriority)); + } +} + void C4AulScriptEngine::CompileFunc(StdCompiler *pComp, bool fScenarioSection, C4ValueNumbers * numbers) { if (!fScenarioSection) @@ -135,27 +155,14 @@ void C4AulScriptEngine::CompileFunc(StdCompiler *pComp, bool fScenarioSection, C // loading scenario section: Merge effects // Must keep old effects here even if they're dead, because the LoadScenarioSection call typically came from execution of a global effect // and otherwise dead pointers would remain on the stack - C4Effect *pOldGlobalEffects, *pNextOldGlobalEffects=pGlobalEffects; - pGlobalEffects = NULL; - try - { - pComp->Value(mkParAdapt(mkNamingPtrAdapt(pGlobalEffects, "Effects"), numbers)); - } - catch (...) - { - delete pNextOldGlobalEffects; - throw; - } - while ((pOldGlobalEffects=pNextOldGlobalEffects)) - { - pNextOldGlobalEffects = pOldGlobalEffects->pNext; - pOldGlobalEffects->Register(&pGlobalEffects, Abs(pOldGlobalEffects->iPriority)); - } + GlobalEffectsMergeCompileFunc(pComp, pGlobalEffects, "Effects", numbers); + GlobalEffectsMergeCompileFunc(pComp, GameScript.pScenarioEffects, "ScenarioEffects", numbers); } else { // Otherwise, just compile effects pComp->Value(mkParAdapt(mkNamingPtrAdapt(pGlobalEffects, "Effects"), numbers)); + pComp->Value(mkParAdapt(mkNamingPtrAdapt(GameScript.pScenarioEffects, "ScenarioEffects"), numbers)); } pComp->Value(mkNamingAdapt(*numbers, "Values")); } diff --git a/src/script/C4AulLink.cpp b/src/script/C4AulLink.cpp index 9c224652e..91c7c8ee5 100644 --- a/src/script/C4AulLink.cpp +++ b/src/script/C4AulLink.cpp @@ -193,6 +193,7 @@ void C4AulScriptEngine::ReLink(C4DefList *rDefs) // adjust global effects if (pGlobalEffects) pGlobalEffects->ReAssignAllCallbackFunctions(); + if (GameScript.pScenarioEffects) GameScript.pScenarioEffects->ReAssignAllCallbackFunctions(); } bool C4AulScriptEngine::ReloadScript(const char *szScript, const char *szLanguage) diff --git a/src/script/C4ScriptHost.cpp b/src/script/C4ScriptHost.cpp index bd79a6615..1daf9461c 100644 --- a/src/script/C4ScriptHost.cpp +++ b/src/script/C4ScriptHost.cpp @@ -21,6 +21,7 @@ #include #include +#include /*--- C4ScriptHost ---*/ @@ -300,6 +301,7 @@ void C4GameScriptHost::Clear() C4ScriptHost::Clear(); ScenPropList.Set0(); ScenPrototype.Set0(); + delete pScenarioEffects; pScenarioEffects=NULL; } C4PropListStatic * C4GameScriptHost::GetPropList() @@ -308,6 +310,12 @@ C4PropListStatic * C4GameScriptHost::GetPropList() return p ? p->IsStatic() : 0; } +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); diff --git a/src/script/C4ScriptHost.h b/src/script/C4ScriptHost.h index ddaa6914f..5abf6b08a 100644 --- a/src/script/C4ScriptHost.h +++ b/src/script/C4ScriptHost.h @@ -130,9 +130,11 @@ public: virtual bool LoadData(const char *, const char *, C4LangStringTable *); void Clear(); virtual C4PropListStatic * GetPropList(); + void Denumerate(C4ValueNumbers * numbers); C4Value Call(const char *szFunction, C4AulParSet *pPars=0, bool fPassError=false); C4Value ScenPropList; C4Value ScenPrototype; + C4Effect * pScenarioEffects = NULL; }; extern C4GameScriptHost GameScript; diff --git a/src/script/C4ScriptStandaloneStubs.cpp b/src/script/C4ScriptStandaloneStubs.cpp index 3823f9a93..33acab34b 100644 --- a/src/script/C4ScriptStandaloneStubs.cpp +++ b/src/script/C4ScriptStandaloneStubs.cpp @@ -39,7 +39,9 @@ C4Effect ** FnGetEffectsFor(C4PropList * pTarget) { if (pTarget == ScriptEngine.GetPropList()) return &ScriptEngine.pGlobalEffects; - if (pTarget) throw C4AulExecError("Only global effects are supported"); + if (pTarget == GameScript.ScenPrototype.getPropList() || pTarget == GameScript.ScenPropList.getPropList()) + return &GameScript.pScenarioEffects; + if (pTarget) throw C4AulExecError("Only global and scenario effects are supported"); return &ScriptEngine.pGlobalEffects; } From d8e8d25ab4cd21c4ccea45032ea56302cdf51864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Thu, 28 Apr 2016 02:51:59 +0200 Subject: [PATCH 18/25] Aul tests: CreateEffect --- tests/aul/AulPredefinedFunctionTest.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/aul/AulPredefinedFunctionTest.cpp b/tests/aul/AulPredefinedFunctionTest.cpp index b45acfffb..7d98c1d96 100644 --- a/tests/aul/AulPredefinedFunctionTest.cpp +++ b/tests/aul/AulPredefinedFunctionTest.cpp @@ -141,6 +141,12 @@ TEST_F(AulPredefFunctionTest, Abs) EXPECT_EQ(C4VINT_MAX, RunExpr("Abs(2147483647)")); } +TEST_F(AulPredefFunctionTest, CreateEffect) +{ + EXPECT_EQ(C4VInt(3), RunCode("local A = { Start=func() { this.Magicnumber = 3; } } func Main() { return CreateEffect(A, 1).Magicnumber; }", false)); + EXPECT_EQ(C4VInt(3), RunCode("local A = { Construction=func() { this.Magicnumber = 3; } } func Main() { return CreateEffect(A, 1).Magicnumber; }", false)); +} + TEST_F(AulPredefFunctionTest, Trivial) { EXPECT_EQ(C4VInt(100), RunExpr("Sin(900,100,10)")); From 071feaa78ff8531b35c1a82269622ed7d18ef9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sun, 24 Apr 2016 23:40:49 +0200 Subject: [PATCH 19/25] Deduplicate loop control handling in the script parser --- src/script/C4AulParse.cpp | 56 ++++++++++++--------------------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 3a5999da3..2ac38f04d 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -140,7 +140,7 @@ public: pLoopStack(NULL) { } ~C4AulParse() - { while (pLoopStack) PopLoop(); ClearToken(); } + { while (pLoopStack) PopLoop(0); ClearToken(); } void Parse_DirectExec(); void Parse_Script(C4ScriptHost *); @@ -223,7 +223,7 @@ private: Loop *pLoopStack; void PushLoop(); - void PopLoop(); + void PopLoop(int ContinueJump); void AddLoopControl(bool fBreak); friend class C4AulParseError; }; @@ -1175,9 +1175,15 @@ void C4AulParse::PushLoop() pLoopStack = pNew; } -void C4AulParse::PopLoop() +void C4AulParse::PopLoop(int ContinueJump) { if (Type != PARSER) return; + // Set targets for break/continue + for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) + if (pCtrl->Break) + SetJumpHere(pCtrl->Pos); + else + SetJump(pCtrl->Pos, ContinueJump); // Delete loop controls Loop *pLoop = pLoopStack; while (pLoop->Controls) @@ -1196,11 +1202,15 @@ void C4AulParse::PopLoop() void C4AulParse::AddLoopControl(bool fBreak) { if (Type != PARSER) return; + // Insert code + if (pLoopStack->StackSize != iStack) + AddBCC(AB_STACK, pLoopStack->StackSize - iStack); Loop::Control *pNew = new Loop::Control(); pNew->Break = fBreak; pNew->Pos = Fn->GetCodePos(); pNew->Next = pLoopStack->Controls; pLoopStack->Controls = pNew; + AddBCC(AB_JUMP); } const char * C4AulParse::GetTokenName(C4AulTokenType TokenType) @@ -1675,11 +1685,7 @@ void C4AulParse::Parse_Statement() } else { - // Insert code - if (pLoopStack->StackSize != iStack) - AddBCC(AB_STACK, pLoopStack->StackSize - iStack); AddLoopControl(true); - AddBCC(AB_JUMP); } } } @@ -1695,11 +1701,7 @@ void C4AulParse::Parse_Statement() } else { - // Insert code - if (pLoopStack->StackSize != iStack) - AddBCC(AB_STACK, pLoopStack->StackSize - iStack); AddLoopControl(false); - AddBCC(AB_JUMP); } } } @@ -1963,13 +1965,7 @@ void C4AulParse::Parse_DoWhile() // Jump back AddJump(AB_COND, Start); if (Type != PARSER) return; - // Set targets for break/continue - for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) - if (pCtrl->Break) - SetJumpHere(pCtrl->Pos); - else - SetJump(pCtrl->Pos, BeforeCond); - PopLoop(); + PopLoop(BeforeCond); } void C4AulParse::Parse_While() @@ -1992,13 +1988,7 @@ void C4AulParse::Parse_While() AddJump(AB_JUMP, iStart); // Set target for conditional jump SetJumpHere(iCond); - // Set targets for break/continue - for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) - if (pCtrl->Break) - SetJumpHere(pCtrl->Pos); - else - SetJump(pCtrl->Pos, iStart); - PopLoop(); + PopLoop(iStart); } void C4AulParse::Parse_If() @@ -2090,13 +2080,7 @@ void C4AulParse::Parse_For() // Set target for condition if (iJumpOut != -1) SetJumpHere(iJumpOut); - // Set targets for break/continue - for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) - if (pCtrl->Break) - SetJumpHere(pCtrl->Pos); - else - SetJump(pCtrl->Pos, iJumpBack); - PopLoop(); + PopLoop(iJumpBack); } void C4AulParse::Parse_ForEach() @@ -2139,13 +2123,7 @@ void C4AulParse::Parse_ForEach() AddJump(AB_JUMP, iStart); // set condition jump target SetJumpHere(iCond); - // set jump targets for break/continue - for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) - if (pCtrl->Break) - SetJumpHere(pCtrl->Pos); - else - SetJump(pCtrl->Pos, iStart); - PopLoop(); + PopLoop(iStart); // remove array and counter from stack AddBCC(AB_STACK, -2); } From 525cdc11a32541e3d308c7472e1b59fb3baaace2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Sun, 24 Apr 2016 23:41:30 +0200 Subject: [PATCH 20/25] Deduplicate calculation of the relative stack position of local variables --- src/script/C4AulParse.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 2ac38f04d..1e9deaa15 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -195,6 +195,7 @@ private: int GetStackValue(C4AulBCCType eType, intptr_t X = 0); int AddBCC(C4AulBCCType eType, intptr_t X = 0); + int AddVarAccess(C4AulBCCType eType, intptr_t varnum); void DebugChunk(); void RemoveLastBCC(); C4V_Type GetLastRetType(C4V_Type to); // for warning purposes @@ -1128,6 +1129,11 @@ C4AulBCC C4AulParse::MakeSetter(bool fLeaveValue) return Setter; } +int C4AulParse::AddVarAccess(C4AulBCCType eType, intptr_t varnum) +{ + return AddBCC(eType, 1 + varnum - (iStack + Fn->VarNamed.iSize)); +} + int C4AulParse::JumpHere() { // Set flag so the next generated code chunk won't get joined @@ -1744,7 +1750,7 @@ int C4AulParse::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc) { if (size >= iMaxCnt) break; - AddBCC(AB_DUP, 1 + i - (iStack + Fn->VarNamed.iSize + Fn->GetParCount())); + AddVarAccess(AB_DUP, i - Fn->GetParCount()); ++size; } // Do not allow more parameters even if there is space left @@ -2110,7 +2116,7 @@ void C4AulParse::Parse_ForEach() // push initial position (0) AddBCC(AB_INT); // get array element - int iStart = AddBCC(AB_FOREACH_NEXT, 1 + iVarID - (iStack + Fn->VarNamed.iSize)); + int iStart = AddVarAccess(AB_FOREACH_NEXT, iVarID); // jump out (FOREACH_NEXT will jump over this if // we're not at the end of the array yet) int iCond = AddBCC(AB_JUMP); @@ -2148,14 +2154,14 @@ void C4AulParse::Parse_Expression(int iParentPrio) if (Fn->ParNamed.GetItemNr(Idtf) != -1) { // insert variable by id - AddBCC(AB_DUP, 1 + Fn->ParNamed.GetItemNr(Idtf) - (iStack + Fn->VarNamed.iSize + Fn->GetParCount())); + AddVarAccess(AB_DUP, Fn->ParNamed.GetItemNr(Idtf) - Fn->GetParCount()); Shift(); } // check for variable (var) else if (Fn->VarNamed.GetItemNr(Idtf) != -1) { // insert variable by id - AddBCC(AB_DUP, 1 + Fn->VarNamed.GetItemNr(Idtf) - (iStack + Fn->VarNamed.iSize)); + AddVarAccess(AB_DUP, Fn->VarNamed.GetItemNr(Idtf)); Shift(); } else if (ContextToExecIn && (ndx = ContextToExecIn->Func->ParNamed.GetItemNr(Idtf)) != -1) @@ -2583,7 +2589,7 @@ void C4AulParse::Parse_Var() // insert initialization in byte code Shift(); Parse_Expression(); - AddBCC(AB_POP_TO, 1 + iVarID - (iStack + Fn->VarNamed.iSize)); + AddVarAccess(AB_POP_TO, iVarID); } if (TokenType == ATT_SCOLON) return; From 3760363a3be0828f12ca3dfe0bd2cb3c0642ac30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 27 Apr 2016 20:42:59 +0200 Subject: [PATCH 21/25] Script: Add GetPrototype and SetPrototype functions This should eventually replace the Prototype property, so that proplists can be used as a key-value-storage without any hidden gotchas. The Prototype is such a magical property that any code dealing with all properties has to special-case it anyway, and isn't even returned by GetProperties(). --- docs/sdk/definition/properties.xml | 2 +- docs/sdk/script/fn/GetPrototype.xml | 26 ++++++++++++++++++++++ docs/sdk/script/fn/SetPrototype.xml | 34 +++++++++++++++++++++++++++++ src/script/C4Script.cpp | 16 ++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 docs/sdk/script/fn/GetPrototype.xml create mode 100644 docs/sdk/script/fn/SetPrototype.xml diff --git a/docs/sdk/definition/properties.xml b/docs/sdk/definition/properties.xml index 2bec17e5f..936440044 100644 --- a/docs/sdk/definition/properties.xml +++ b/docs/sdk/definition/properties.xml @@ -28,7 +28,7 @@ Stand = { Prototype proplist - + Deprecated. Use SetPrototype and GetPrototype. Name diff --git a/docs/sdk/script/fn/GetPrototype.xml b/docs/sdk/script/fn/GetPrototype.xml new file mode 100644 index 000000000..351a0d306 --- /dev/null +++ b/docs/sdk/script/fn/GetPrototype.xml @@ -0,0 +1,26 @@ + + + + + + GetPrototype + Objects + Properties + 8.0 OC + + nil + + + proplist + obj + The Object whose prototype is returned. Can be nil in local calls. + + + + + When properties of a proplist are read and not set on that proplist, the property is looked up in the proplist's prototype(s). The immediate prototype is returned by this function. + SetPrototype + + Günther2016-04 + diff --git a/docs/sdk/script/fn/SetPrototype.xml b/docs/sdk/script/fn/SetPrototype.xml new file mode 100644 index 000000000..43b5a4bb0 --- /dev/null +++ b/docs/sdk/script/fn/SetPrototype.xml @@ -0,0 +1,34 @@ + + + + + + SetPrototype + Objects + Properties + 8.0 OC + + nil + + + proplist + prototype + The new prototype. + + + + proplist + obj + Object to be changed. Can be nil in local calls. + + + + + This function changes the prototype of a proplist. + When properties of a proplist are read and not set on that proplist, the property is looked up in the proplist's prototype(s). + This can be used for inheritance. + GetPrototype + + Günther2016-04 + diff --git a/src/script/C4Script.cpp b/src/script/C4Script.cpp index d3b121a3d..15dd566a8 100644 --- a/src/script/C4Script.cpp +++ b/src/script/C4Script.cpp @@ -300,6 +300,20 @@ static C4ValueArray * FnGetProperties(C4PropList * _this, C4PropList * p) return r; } +static C4PropList * FnGetPrototype(C4PropList * _this, C4PropList * p) +{ + if (!p) p = _this; + if (!p) throw NeedNonGlobalContext("GetPrototype"); + return p->GetPrototype(); +} + +static void FnSetPrototype(C4PropList * _this, C4PropList * prototype, C4PropList * p) +{ + if (!p) p = _this; + if (!p) throw NeedNonGlobalContext("GetPrototype"); + p->SetProperty(P_Prototype, C4Value(prototype)); +} + static C4Value FnCall(C4PropList * _this, C4Value * Pars) { if (!_this) _this = ::ScriptEngine.GetPropList(); @@ -955,6 +969,8 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine) F(GetProperties); F(GetProperty); F(SetProperty); + F(GetPrototype); + F(SetPrototype); F(ResetProperty); F(GetName); F(AddEffect); From 072661239d9c9e2405afa4a60cbfe0c953053c32 Mon Sep 17 00:00:00 2001 From: Nicolas Hake Date: Wed, 27 Apr 2016 13:19:20 +0200 Subject: [PATCH 22/25] GRBroadcast: Don't noisily fail when a callback function does not exist Since GRBroadcast calls all goal and rule objects and the scenario, there is a high likelyhood that not all of them implement whatever arbitrary callback is called. Instead of throwing script errors about calling undefined functions, just swallow that error and continue. --- src/game/C4Game.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/game/C4Game.cpp b/src/game/C4Game.cpp index 4001f43d8..27a809103 100644 --- a/src/game/C4Game.cpp +++ b/src/game/C4Game.cpp @@ -3716,12 +3716,16 @@ bool C4Game::ToggleChat() C4Value C4Game::GRBroadcast(const char *szFunction, C4AulParSet *pPars, bool fPassError, bool fRejectTest) { + std::string func{ szFunction }; + if (func[0] != '~') + func.insert(0, 1, '~'); + // call objects first - scenario script might overwrite hostility, etc... - C4Value vResult = ::Objects.GRBroadcast(szFunction, pPars, fPassError, fRejectTest); + C4Value vResult = ::Objects.GRBroadcast(func.c_str(), pPars, fPassError, fRejectTest); // rejection tests abort on first nonzero result if (fRejectTest) if (!!vResult) return vResult; // scenario script call - return ::GameScript.Call(szFunction, pPars, fPassError); + return ::GameScript.Call(func.c_str(), pPars, fPassError); } void C4Game::SetDefaultGamma() From 4b43576f838b8135a47b7d9f2628e566ce40b66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Wed, 21 May 2014 19:44:18 +0200 Subject: [PATCH 23/25] Script: Complain about some attempts to Call nonexistant function again This code got lost in the function pointer rewrite. --- src/script/C4PropList.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/script/C4PropList.cpp b/src/script/C4PropList.cpp index 57bc15581..14f23543c 100644 --- a/src/script/C4PropList.cpp +++ b/src/script/C4PropList.cpp @@ -643,7 +643,17 @@ C4Value C4PropList::Call(const char * s, C4AulParSet *Pars, bool fPassErrors) if (!Status) return C4Value(); assert(s && s[0]); C4AulFunc *pFn = GetFunc(s); - if (!pFn) return C4Value(); + if (!pFn) + { + if (s[0] != '~') + { + C4AulExecError err(FormatString("Undefined function: %s", s).getData()); + if (fPassErrors) + throw err; + err.show(); + } + return C4Value(); + } return pFn->Exec(this, Pars, fPassErrors); } From a8f4bd3960b9c4cea1ed369847a37a09354fd4d2 Mon Sep 17 00:00:00 2001 From: Nicolas Hake Date: Wed, 27 Apr 2016 11:55:40 +0200 Subject: [PATCH 24/25] C4AulParseError: Avoid the second string parameter While I'm not a big fan of hiding flow control in a function, Error() handles formatting and saves me from calling FormatString().getData() a lot. --- src/script/C4AulParse.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 1e9deaa15..0a630f802 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -1350,7 +1350,7 @@ void C4AulParse::Parse_Script(C4ScriptHost * scripthost) } else // -> unknown directive - throw C4AulParseError(this, "unknown directive: ", Idtf); + Error("unknown directive: %s", Idtf); break; case ATT_IDTF: // need a keyword here to avoid parsing random function contents @@ -1420,7 +1420,7 @@ void C4AulParse::Parse_Function() // check for func declaration if (!SEqual(Idtf, C4AUL_Func)) - throw C4AulParseError(this, "Declaration expected, but found identifier ", Idtf); + Error("Declaration expected, but found identifier: %s", Idtf); Shift(); // get next token, must be func name Check(ATT_IDTF, "function name"); @@ -1433,7 +1433,7 @@ void C4AulParse::Parse_Function() if (is_global || !Host->GetPropList()) { if (Host != pOrgScript) - throw C4AulParseError(this, "global func in appendto/included script: ", Idtf); + Error("global func in appendto/included script: %s", Idtf); if (Engine->GlobalNamedNames.GetItemNr(Idtf) != -1) throw C4AulParseError(this, "function definition: name already in use (global variable)"); if (Engine->GlobalConstNames.GetItemNr(Idtf) != -1) @@ -2333,7 +2333,7 @@ void C4AulParse::Parse_Expression(int iParentPrio) else { // identifier could not be resolved - throw C4AulParseError(this, "unknown identifier: ", Idtf); + Error("unknown identifier: %s", Idtf); } break; case ATT_INT: // constant in cInt @@ -2432,7 +2432,7 @@ void C4AulParse::Parse_Expression(int iParentPrio) // not found? if (!postfixop->Identifier) { - throw C4AulParseError(this, "unexpected prefix operator: ", op->Identifier); + Error("unexpected prefix operator: %s", op->Identifier); } // otherwise use the new-found correct postfix operator op = postfixop; From d45ac946feb5c51c5c3cd8dfdb642559f6188cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Brammer?= Date: Thu, 28 Apr 2016 00:11:46 +0200 Subject: [PATCH 25/25] Add C4AulParseError constructor and cease reusing it for warnings The nontrivial code that formats the position is now a function of C4AulParse used by both warnings and errors. --- src/script/C4Aul.h | 5 +- src/script/C4AulParse.cpp | 114 +++++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/script/C4Aul.h b/src/script/C4Aul.h index 7f0e6bef5..1e1a80eb6 100644 --- a/src/script/C4Aul.h +++ b/src/script/C4Aul.h @@ -45,8 +45,9 @@ public: class C4AulParseError : public C4AulError { public: - C4AulParseError(C4ScriptHost *pScript, const char *pMsg, const char *pIdtf = NULL, bool Warn = false); // constructor - C4AulParseError(class C4AulParse * state, const char *pMsg, const char *pIdtf = NULL, bool Warn = false); // constructor + C4AulParseError(C4ScriptHost *pScript, const char *pMsg); // constructor + C4AulParseError(class C4AulParse * state, const char *pMsg); // constructor + C4AulParseError(C4AulScriptFunc * Fn, const char *SPos, const char *pMsg); }; // execution error diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 0a630f802..0aa72e838 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -189,6 +189,7 @@ private: void Warn(const char *pMsg, ...) GNUC_FORMAT_ATTRIBUTE_O; void Error(const char *pMsg, ...) GNUC_FORMAT_ATTRIBUTE_O; + void AppendPosition(StdStrBuf & Buf); bool fJump; int iStack; @@ -233,11 +234,10 @@ void C4ScriptHost::Warn(const char *pMsg, ...) { va_list args; va_start(args, pMsg); StdStrBuf Buf; - Buf.FormatV(pMsg, args); - - C4AulParseError warning(this, Buf.getData(), 0, true); - // display it - warning.show(); + Buf.Ref("WARNING: "); + Buf.AppendFormatV(pMsg, args); + Buf.AppendFormat(" (%s)", ScriptName.getData()); + DebugLog(Buf.getData()); // count warnings ++Engine->warnCnt; } @@ -246,12 +246,11 @@ void C4AulParse::Warn(const char *pMsg, ...) { va_list args; va_start(args, pMsg); StdStrBuf Buf; - Buf.FormatV(pMsg, args); + Buf.Ref("WARNING: "); + Buf.AppendFormatV(pMsg, args); + AppendPosition(Buf); + DebugLog(Buf.getData()); - C4AulParseError warning(this, Buf.getData(), 0, true); - warning.show(); - if (pOrgScript != Host) - DebugLogF(" (as #appendto/#include to %s)", Host->ScriptName.getData()); // count warnings ++Engine->warnCnt; } @@ -265,49 +264,49 @@ void C4AulParse::Error(const char *pMsg, ...) throw C4AulParseError(this, Buf.getData()); } -C4AulParseError::C4AulParseError(C4AulParse * state, const char *pMsg, const char *pIdtf, bool Warn) +void C4AulParse::AppendPosition(StdStrBuf & Buf) +{ + if (Fn && Fn->GetName()) + { + // Show function name + Buf.AppendFormat(" (in %s", Fn->GetName()); + + // Exact position + if (Fn->pOrgScript && TokenSPos) + Buf.AppendFormat(", %s:%d:%d)", + Fn->pOrgScript->ScriptName.getData(), + SGetLine(Fn->pOrgScript->GetScript(), TokenSPos), + SLineGetCharacters(Fn->pOrgScript->GetScript(), TokenSPos)); + else + Buf.AppendChar(')'); + } + else if (pOrgScript) + { + // Script name + Buf.AppendFormat(" (%s:%d:%d)", + pOrgScript->ScriptName.getData(), + SGetLine(pOrgScript->GetScript(), TokenSPos), + SLineGetCharacters(pOrgScript->GetScript(), TokenSPos)); + } + // show a warning if the error is in a remote script + if (pOrgScript != Host && Host) + Buf.AppendFormat(" (as #appendto/#include to %s)", Host->ScriptName.getData()); +} + +C4AulParseError::C4AulParseError(C4AulParse * state, const char *pMsg) : C4AulError() { // compose error string - sMessage.Format("%s: %s%s", - Warn ? "WARNING" : "ERROR", - pMsg, - pIdtf ? pIdtf : ""); - if (state->Fn && state->Fn->GetName()) - { - // Show function name - sMessage.AppendFormat(" (in %s", state->Fn->GetName()); - - // Exact position - if (state->Fn->pOrgScript && state->TokenSPos) - sMessage.AppendFormat(", %s:%d:%d)", - state->Fn->pOrgScript->ScriptName.getData(), - SGetLine(state->Fn->pOrgScript->GetScript(), state->TokenSPos), - SLineGetCharacters(state->Fn->pOrgScript->GetScript(), state->TokenSPos)); - else - sMessage.AppendChar(')'); - } - else if (state->pOrgScript) - { - // Script name - sMessage.AppendFormat(" (%s:%d:%d)", - state->pOrgScript->ScriptName.getData(), - SGetLine(state->pOrgScript->GetScript(), state->TokenSPos), - SLineGetCharacters(state->pOrgScript->GetScript(), state->TokenSPos)); - } - // show a warning if the error is in a remote script - if (state->pOrgScript != state->Host && state->Host) - sMessage.AppendFormat(" (as #appendto/#include to %s)", state->Host->ScriptName.getData()); - + sMessage.Ref("ERROR: "); + sMessage.Append(pMsg); + state->AppendPosition(sMessage); } -C4AulParseError::C4AulParseError(C4ScriptHost *pScript, const char *pMsg, const char *pIdtf, bool Warn) +C4AulParseError::C4AulParseError(C4ScriptHost *pScript, const char *pMsg) { // compose error string - sMessage.Format("%s: %s%s", - Warn ? "WARNING" : "ERROR", - pMsg, - pIdtf ? pIdtf : ""); + sMessage.Ref("ERROR: "); + sMessage.Append(pMsg); if (pScript) { // Script name @@ -316,6 +315,29 @@ C4AulParseError::C4AulParseError(C4ScriptHost *pScript, const char *pMsg, const } } +C4AulParseError::C4AulParseError(C4AulScriptFunc * Fn, const char *SPos, const char *pMsg) + : C4AulError() +{ + // compose error string + sMessage.Ref("ERROR: "); + sMessage.Append(pMsg); + if (!Fn) return; + sMessage.Append(" ("); + // Show function name + if (Fn->GetName()) + sMessage.AppendFormat("in %s", Fn->GetName()); + if (Fn->GetName() && Fn->pOrgScript && SPos) + sMessage.Append(", "); + // Exact position + if (Fn->pOrgScript && SPos) + sMessage.AppendFormat("%s:%d:%d)", + Fn->pOrgScript->ScriptName.getData(), + SGetLine(Fn->pOrgScript->GetScript(), SPos), + SLineGetCharacters(Fn->pOrgScript->GetScript(), SPos)); + else + sMessage.AppendChar(')'); +} + bool C4AulParse::AdvanceSpaces() { if (!SPos)