void C4Effect::Kill() { // active? C4Effect *pLastRemovedEffect=NULL; if (IsActive()) // then temp remove all higher priority effects TempRemoveUpperEffects(false, &pLastRemovedEffect); 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 (iPriority!=1) CallStart(C4FxCall_TempAddForRemoval, C4Value(), C4Value(), C4Value(), C4Value()); // remove this effect int32_t iPrevPrio = iPriority; SetDead(); if (CallStop(C4FxCall_Normal, false) == C4Fx_Stop_Deny) // effect denied to be removed: recover iPriority = iPrevPrio; // reactivate other effects TempReaddUpperEffects(pLastRemovedEffect); // Update OnFire cache if (Target && WildcardMatch(C4Fx_AnyFire, GetName())) if (!Get(C4Fx_AnyFire)) Target->SetOnFire(false); if (IsDead() && !GetCallbackScript()) Call(P_Destruction, &C4AulParSet(C4FxCall_Normal)); }
C4Value C4PropList::Call(C4String * k, C4AulParSet *Pars, bool fPassErrors) { if (!Status) return C4Value(); C4AulFunc *pFn = GetFunc(k); if (!pFn) return C4Value(); return pFn->Exec(this, Pars, fPassErrors); }
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(); return pFn->Exec(this, Pars, fPassErrors); }
C4Value C4Effect::DoCall(C4Object *pObj, const char *szFn, C4Value &rVal1, C4Value &rVal2, C4Value &rVal3, C4Value &rVal4, C4Value &rVal5, C4Value &rVal6, C4Value &rVal7) { // def script or global only? C4AulScript *pSrcScript; C4Def *pDef; if (pCommandTarget) { pSrcScript = &pCommandTarget->Def->Script; // overwrite ID for sync safety in runtime join idCommandTarget = pCommandTarget->id; } else if (idCommandTarget && (pDef = Game.Defs.ID2Def(idCommandTarget))) pSrcScript = &pDef->Script; else pSrcScript = &Game.ScriptEngine; // compose function name char fn[C4AUL_MAX_Identifier + 1]; sprintf(fn, PSF_FxCustom, Name, szFn); // call it C4AulFunc *pFn = pSrcScript->GetFuncRecursive(fn); if (!pFn) return C4Value(); return pFn->Exec(pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(iNumber), rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); }
TEST(DirectExecTest, SanityTests) { C4AulScript * pScript = new C4AulScript(); ASSERT_TRUE(pScript); C4Value rVal(pScript->DirectExec(nullptr, "5*8", "unit test script", false, nullptr)); EXPECT_EQ(rVal, C4Value(5*8)); delete pScript; }
C4SortObject *C4SortObject::CreateByValue(const C4Value &DataVal) { // Must be an array const C4ValueArray *pArray = C4Value(DataVal).getArray(); if (!pArray) return NULL; const C4ValueArray &Data = *pArray; int32_t iType = Data[0].getInt(); return CreateByValue(iType, Data); }
void C4Effect::TempReaddUpperEffects(C4Effect *pLastReaddEffect) { // nothing to do? - this will also happen if TempRemoveUpperEffects did nothing due to priority==1 if (!pLastReaddEffect) return; if (Target && !Target->Status) return; // this will be invalid! // simply activate all following, inactive effects for (C4Effect *pEff = pNext; pEff; pEff = pEff->pNext) { if (pEff->IsInactiveAndNotDead()) { pEff->FlipActive(); if (pEff->iPriority!=1) pEff->CallStart(C4FxCall_Temp, C4Value(), C4Value(), C4Value(), C4Value()); if (Target && WildcardMatch(C4Fx_AnyFire, pEff->GetName())) Target->SetOnFire(true); } // done? if (pEff == pLastReaddEffect) break; } }
TEST_F(AulTest, Loops) { EXPECT_EQ(C4VInt(5), RunCode("var i = 0; do ++i; while (i < 5); return i;")); EXPECT_EQ(C4VInt(5), RunCode("var i = 0; while (i < 5) ++i; return i;")); EXPECT_EQ(C4VInt(5), RunCode("for(var i = 0; i < 5; ++i); return i;")); EXPECT_EQ(C4VInt(6), RunCode("var i = 0, b; do { b = i++ >= 5; } while (!b); return i;")); EXPECT_EQ(C4VInt(6), RunCode("var i = 0, b; while (!b) { b = i++ >= 5; } return i;")); EXPECT_EQ(C4Value(), RunCode("var a = [], sum; for(var i in a) sum += i; return sum;")); EXPECT_EQ(C4VInt(1), RunCode("var a = [1], sum; for(var i in a) sum += i; return sum;")); EXPECT_EQ(C4VInt(6), RunCode("var a = [1,2,3], sum; for(var i in a) sum += i; return sum;")); }
int32_t C4Effect::Check(C4Object *pForObj, const char *szCheckEffect, int32_t iPrio, int32_t iTimer, C4Value &rVal1, C4Value &rVal2, C4Value &rVal3, C4Value &rVal4) { // priority=1: always OK; no callbacks if (iPrio == 1) return 0; // check this and other effects C4Effect *pAddToEffect = NULL; bool fDoTempCallsForAdd = false; C4Effect *pLastRemovedEffect = NULL; for (C4Effect *pCheck = this; pCheck; pCheck = pCheck->pNext) { if (!pCheck->IsDead() && pCheck->pFnEffect && pCheck->iPriority >= iPrio) { int32_t iResult = pCheck->pFnEffect->Exec(pCheck->pCommandTarget, &C4AulParSet(C4VString(szCheckEffect), C4VObj(pForObj), C4VInt(pCheck->iNumber), C4Value(), rVal1, rVal2, rVal3, rVal4)).getInt(); if (iResult == C4Fx_Effect_Deny) // effect denied return C4Fx_Effect_Deny; // add to other effect if (iResult == C4Fx_Effect_Annul || iResult == C4Fx_Effect_AnnulCalls) { pAddToEffect = pCheck; fDoTempCallsForAdd = (iResult == C4Fx_Effect_AnnulCalls); } } } // adding to other effect? if (pAddToEffect) { // do temp remove calls if desired if (pAddToEffect->pNext && fDoTempCallsForAdd) pAddToEffect->TempRemoveUpperEffects(pForObj, false, &pLastRemovedEffect); C4Value Par1 = C4VString(szCheckEffect), Par2 = C4VInt(iTimer), Par8; int32_t iResult = pAddToEffect->DoCall(pForObj, PSFS_FxAdd, Par1, Par2, rVal1, rVal2, rVal3, rVal4, Par8).getInt(); // do temp readd calls if desired if (pAddToEffect->pNext && fDoTempCallsForAdd) pAddToEffect->TempReaddUpperEffects(pForObj, pLastRemovedEffect); // effect removed by this call? if (iResult == C4Fx_Start_Deny) { pAddToEffect->Kill(pForObj); return C4Fx_Effect_Annul; } else // other effect is the target effect number return pAddToEffect->iNumber; } // added to no effect and not denied return 0; }
TEST_F(AulTest, ValueReturn) { // Make sure primitive value returns work. EXPECT_EQ(C4VNull, RunCode("return;")); EXPECT_EQ(C4VNull, RunExpr("nil")); EXPECT_EQ(C4Value(true), RunExpr("true")); EXPECT_EQ(C4Value(false), RunExpr("false")); EXPECT_EQ(C4VInt(42), RunExpr("42")); EXPECT_EQ(C4VString("Hello World!"), RunExpr("\"Hello World!\"")); // Make sure array returns work. EXPECT_EQ(C4VArray(), RunExpr("[]")); EXPECT_EQ( C4VArray(C4VInt(0), C4VNull, C4VArray(C4VInt(1)), C4VString("Hi")), RunExpr("[0, nil, [1], \"Hi\"]")); // Make sure proplist returns work. EXPECT_EQ(C4VPropList(), RunExpr("{}")); EXPECT_EQ( C4VPropList("a", C4VInt(1), "b", C4VArray()), RunExpr("{\"a\": 1, \"b\"=[]}")); }
void C4ValueArray::CompileFunc(class StdCompiler *pComp, C4ValueNumbers * numbers) { int32_t inSize = iSize; // Size. Reset if not found. try { pComp->Value(inSize); } catch (StdCompiler::NotFoundException *pExc) { Reset(); delete pExc; return; } // Separator pComp->Separator(StdCompiler::SEP_SEP2); // Allocate if (pComp->isCompiler()) this->SetSize(inSize); // Values pComp->Value(mkArrayAdaptMap(pData, iSize, C4Value(), mkParAdaptMaker(numbers))); }
bool C4Effect::GetPropertyByS(C4String *k, C4Value *pResult) const { if (k >= &Strings.P[0] && k < &Strings.P[P_LAST]) { switch(k - &Strings.P[0]) { 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: *pResult = CommandTarget; return true; case P_Target: *pResult = C4Value(Target); return true; case P_Time: *pResult = C4VInt(iTime); return true; } } return C4PropListNumbered::GetPropertyByS(k, pResult); }
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) { C4PropList * p = GetCallbackScript(); if (!p) { C4AulFunc * fn = GetFunc(szFn); if (fn) return fn->Exec(this, &C4AulParSet(rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); } else { // old variant // compose function name C4AulFunc * fn = p->GetFunc(FormatString(PSF_FxCustom, GetName(), szFn).getData()); if (fn) return fn->Exec(p, &C4AulParSet(Obj(pObj), this, rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); } return C4Value(); }
void C4ScenarioParameterDefs::RegisterScriptConstants(const C4ScenarioParameters &values) { // register constants for all parameters in script engine // old-style: one constant per parameter for (const auto & Parameter : Parameters) { StdStrBuf constant_name; constant_name.Format("SCENPAR_%s", Parameter.GetID()); int32_t constant_value = values.GetValueByID(Parameter.GetID(), Parameter.GetDefault()); ::ScriptEngine.RegisterGlobalConstant(constant_name.getData(), C4VInt(constant_value)); } // new-style: all constants in a proplist auto scenpar = C4PropList::NewStatic(nullptr, nullptr, &Strings.P[P_SCENPAR]); for (const auto & Parameter : Parameters) { int32_t constant_value = values.GetValueByID(Parameter.GetID(), Parameter.GetDefault()); scenpar->SetPropertyByS(Strings.RegString(StdStrBuf(Parameter.GetID())), C4VInt(constant_value)); } scenpar->Freeze(); ::ScriptEngine.RegisterGlobalConstant("SCENPAR", C4Value(scenpar)); }
C4FindObject *C4FindObject::CreateByValue(const C4Value &DataVal, C4SortObject **ppSortObj) { // Must be an array C4ValueArray *pArray = C4Value(DataVal).getArray(); if (!pArray) return NULL; const C4ValueArray &Data = *pArray; int32_t iType = Data[0].getInt(); if (Inside<int32_t>(iType, C4SO_First, C4SO_Last)) { // this is not a FindObject but a sort condition! // sort condition not desired here? if (!ppSortObj) return NULL; // otherwise, create it! *ppSortObj = C4SortObject::CreateByValue(iType, Data); // done return NULL; } switch (iType) { case C4FO_Not: { // Create child condition C4FindObject *pCond = C4FindObject::CreateByValue(Data[1]); if (!pCond) return NULL; // wrap return new C4FindObjectNot(pCond); } case C4FO_And: case C4FO_Or: { // Trivial case (one condition) if (Data.GetSize() == 2) return C4FindObject::CreateByValue(Data[1]); // Create all childs int32_t i; C4FindObject **ppConds = new C4FindObject *[Data.GetSize() - 1]; for (i = 0; i < Data.GetSize() - 1; i++) ppConds[i] = C4FindObject::CreateByValue(Data[i + 1]); // Count real entries, move them to start of list int32_t iSize = 0; for (i = 0; i < Data.GetSize() - 1; i++) if (ppConds[i]) if (iSize++ != i) ppConds[iSize - 1] = ppConds[i]; // Create if (iType == C4FO_And) return new C4FindObjectAnd(iSize, ppConds); else return new C4FindObjectOr(iSize, ppConds); } case C4FO_Exclude: return new C4FindObjectExclude(Data[1].getObj()); case C4FO_ID: return new C4FindObjectID(Data[1].getC4ID()); case C4FO_InRect: return new C4FindObjectInRect(C4Rect(Data[1].getInt(), Data[2].getInt(), Data[3].getInt(), Data[4].getInt())); case C4FO_AtPoint: return new C4FindObjectAtPoint(Data[1].getInt(), Data[2].getInt()); case C4FO_AtRect: return new C4FindObjectAtRect(Data[1].getInt(), Data[2].getInt(), Data[3].getInt(), Data[4].getInt()); case C4FO_OnLine: return new C4FindObjectOnLine(Data[1].getInt(), Data[2].getInt(), Data[3].getInt(), Data[4].getInt()); case C4FO_Distance: return new C4FindObjectDistance(Data[1].getInt(), Data[2].getInt(), Data[3].getInt()); case C4FO_OCF: return new C4FindObjectOCF(Data[1].getInt()); case C4FO_Category: return new C4FindObjectCategory(Data[1].getInt()); case C4FO_Action: { C4String *pStr = Data[1].getStr(); if (!pStr) return NULL; // Don't copy, it should be safe return new C4FindObjectAction(pStr->Data.getData()); } case C4FO_Func: { // Get function name C4String *pStr = Data[1].getStr(); if (!pStr) return NULL; // Construct C4FindObjectFunc *pFO = new C4FindObjectFunc(pStr->Data.getData()); // Add parameters for (int i = 2; i < Data.GetSize(); i++) pFO->SetPar(i - 2, Data[i]); // Done return pFO; } case C4FO_ActionTarget: { int index = 0; if (Data.GetSize() >= 3) index = BoundBy(Data[2].getInt(), 0, 1); return new C4FindObjectActionTarget(Data[1].getObj(), index); } case C4FO_Container: return new C4FindObjectContainer(Data[1].getObj()); case C4FO_AnyContainer: return new C4FindObjectAnyContainer(); case C4FO_Owner: return new C4FindObjectOwner(Data[1].getInt()); case C4FO_Controller: return new C4FindObjectController(Data[1].getInt()); case C4FO_Layer: return new C4FindObjectLayer(Data[1].getObj()); } return NULL; }
C4FindObject *C4FindObject::CreateByValue(const C4Value &DataVal, C4SortObject **ppSortObj, const C4Object *context, bool *has_layer_check) { // Must be an array C4ValueArray *pArray = C4Value(DataVal).getArray(); if (!pArray) return NULL; const C4ValueArray &Data = *pArray; int32_t iType = Data[0].getInt(); if (Inside<int32_t>(iType, C4SO_First, C4SO_Last)) { // this is not a FindObject but a sort condition! // sort condition not desired here? if (!ppSortObj) return NULL; // otherwise, create it! *ppSortObj = C4SortObject::CreateByValue(iType, Data, context); // done return NULL; } switch (iType) { case C4FO_Not: { // Create child condition C4FindObject *pCond = C4FindObject::CreateByValue(Data[1], nullptr, context, has_layer_check); if (!pCond) return NULL; // wrap return new C4FindObjectNot(pCond); } case C4FO_And: case C4FO_Or: { // Trivial case (one condition) if (Data.GetSize() == 2) return C4FindObject::CreateByValue(Data[1], nullptr, context, has_layer_check); // Create all childs int32_t i; C4FindObject **ppConds = new C4FindObject *[Data.GetSize() - 1]; for (i = 0; i < Data.GetSize() - 1; i++) ppConds[i] = C4FindObject::CreateByValue(Data[i + 1], nullptr, context, has_layer_check); // Count real entries, move them to start of list int32_t iSize = 0; for (i = 0; i < Data.GetSize() - 1; i++) if (ppConds[i]) if (iSize++ != i) ppConds[iSize-1] = ppConds[i]; // Create if (iType == C4FO_And) return new C4FindObjectAnd(iSize, ppConds); else return new C4FindObjectOr(iSize, ppConds); } case C4FO_Exclude: return new C4FindObjectExclude(Data[1].getObj()); case C4FO_ID: return new C4FindObjectDef(Data[1].getPropList()); // #973: For all criteria using coordinates: If FindObject et al. are called in object context, offset by object center case C4FO_InRect: { int32_t x = Data[1].getInt(); int32_t y = Data[2].getInt(); int32_t w = Data[3].getInt(); int32_t h = Data[4].getInt(); if (context) { x += context->GetX(); y += context->GetY(); } return new C4FindObjectInRect(C4Rect(x, y, w, h)); } case C4FO_AtPoint: { int32_t x = Data[1].getInt(); int32_t y = Data[2].getInt(); if (context) { x += context->GetX(); y += context->GetY(); } return new C4FindObjectAtPoint(x, y); } case C4FO_AtRect: { int32_t x = Data[1].getInt(); int32_t y = Data[2].getInt(); int32_t w = Data[3].getInt(); int32_t h = Data[4].getInt(); if (context) { x += context->GetX(); y += context->GetY(); } return new C4FindObjectAtRect(x, y, w, h); } case C4FO_OnLine: { int32_t x1 = Data[1].getInt(); int32_t y1 = Data[2].getInt(); int32_t x2 = Data[3].getInt(); int32_t y2 = Data[4].getInt(); if (context) { x1 += context->GetX(); x2 += context->GetX(); y1 += context->GetY(); y2 += context->GetY(); } return new C4FindObjectOnLine(x1, y1, x2, y2); } case C4FO_Distance: { int32_t x = Data[1].getInt(); int32_t y = Data[2].getInt(); if (context) { x += context->GetX(); y += context->GetY(); } return new C4FindObjectDistance(x, y, Data[3].getInt()); } case C4FO_OCF: return new C4FindObjectOCF(Data[1].getInt()); case C4FO_Category: return new C4FindObjectCategory(Data[1].getInt()); case C4FO_Action: { C4String *pStr = Data[1].getStr(); if (!pStr) return NULL; // Don't copy, it should be safe return new C4FindObjectAction(pStr->GetCStr()); } case C4FO_Func: { // Get function name C4String *pStr = Data[1].getStr(); if (!pStr) return NULL; // Construct C4FindObjectFunc *pFO = new C4FindObjectFunc(pStr); // Add parameters for (int i = 2; i < Data.GetSize(); i++) pFO->SetPar(i - 2, Data[i]); // Done return pFO; } case C4FO_ActionTarget: { int index = 0; if (Data.GetSize() >= 3) index = Clamp(Data[2].getInt(), 0, 1); return new C4FindObjectActionTarget(Data[1].getObj(), index); } case C4FO_Procedure: return new C4FindObjectProcedure(Data[1].getStr()); case C4FO_Container: return new C4FindObjectContainer(Data[1].getObj()); case C4FO_AnyContainer: return new C4FindObjectAnyContainer(); case C4FO_Owner: return new C4FindObjectOwner(Data[1].getInt()); case C4FO_Controller: return new C4FindObjectController(Data[1].getInt()); case C4FO_Layer: // explicit layer check given. do not add implicit layer check if (has_layer_check) *has_layer_check = true; return new C4FindObjectLayer(Data[1].getObj()); case C4FO_InArray: return new C4FindObjectInArray(Data[1].getArray()); case C4FO_Property: { // Get property name C4String *pStr = Data[1].getStr(); if (!pStr) return NULL; // Construct C4FindObjectProperty *pFO = new C4FindObjectProperty(pStr); // Done return pFO; } case C4FO_AnyLayer: // do not add implicit layer check if (has_layer_check) *has_layer_check = true; return NULL; } return NULL; }
TEST(DirectExecTest, SanityTests) { C4Value rVal(AulExec.DirectExec(nullptr, "5*8", "unit test script", false, nullptr)); EXPECT_EQ(rVal, C4Value(5*8)); }