void C4MapScriptHost::InitFunctionMap(C4AulScriptEngine *pEngine) { // Register script host. Add Map and MapLayer prototypes, related constants and engine functions assert(pEngine && pEngine->GetPropList()); Clear(); LayerPrototype = new C4PropListStaticMember(NULL, NULL, ::Strings.RegString("MapLayer")); MapPrototype = new C4PropListStaticMember(LayerPrototype, NULL, ::Strings.RegString("Map")); LayerPrototype->SetName("MapLayer"); MapPrototype->SetName("Map"); ::ScriptEngine.RegisterGlobalConstant("MapLayer", C4VPropList(LayerPrototype)); ::ScriptEngine.RegisterGlobalConstant("Map", C4VPropList(MapPrototype)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Layer", C4VInt(MAPALGO_Layer)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_RndChecker", C4VInt(MAPALGO_RndChecker)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_And", C4VInt(MAPALGO_And)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Or", C4VInt(MAPALGO_Or)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Xor", C4VInt(MAPALGO_Xor)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Not", C4VInt(MAPALGO_Not)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Scale", C4VInt(MAPALGO_Scale)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Rotate", C4VInt(MAPALGO_Rotate)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Offset", C4VInt(MAPALGO_Offset)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Rect", C4VInt(MAPALGO_Rect)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Ellipsis", C4VInt(MAPALGO_Ellipsis)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Polygon", C4VInt(MAPALGO_Polygon)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Lines", C4VInt(MAPALGO_Lines)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Turbulence", C4VInt(MAPALGO_Turbulence)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Border", C4VInt(MAPALGO_Border)); ::ScriptEngine.RegisterGlobalConstant("MAPALGO_Filter", C4VInt(MAPALGO_Filter)); Reg2List(pEngine); AddEngineFunctions(); }
void C4PropListScript::ClearScriptPropLists() { // empty all proplists to ensure safe deletion of proplists with circular references // note that this the call to Clear() might delete some prop lists. So it is assumed that // PropLists does not shrink its table as the number of values goes down // However, some values may be skipped due to table consolidation. Just fix it by iterating over the table until it's empty. C4PropListScript *const* p_next, *const* p; while ((p_next = PropLists.First())) { size_t prev_size = PropLists.GetSize(); while ((p = p_next)) { p_next = PropLists.Next(p); // check *p since it might have been deleted by clearing the previous prop list if (*p) { C4Value ref(C4VPropList(*p)); // keep a reference because prop list might delete itself within clearing method otherwise (*p)->Clear(); } } if (PropLists.GetSize() >= prev_size) { // Looks like there's a rogue C4Value pointer somewhere. // Could just delete the prop list and let ref counting do the job // However, it might be better to keep the dead pointer to find the leak in debug mode #ifdef _DEBUG assert(0); #endif break; } } }
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) { // def script or global only? C4PropList *p = GetCallbackScript(); // compose function name char fn[C4AUL_MAX_Identifier+1]; sprintf(fn, PSF_FxCustom, GetName(), szFn); return p->Call(fn, &C4AulParSet(C4VObj(pObj), C4VPropList(this), rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); }
void C4Effect::DoDamage(C4Object *pObj, int32_t &riDamage, int32_t iDamageType, int32_t iCausePlr) { // ask all effects for damage adjustments 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 (pObj && !pObj->Status) return; } while ((pEff = pEff->pNext) && riDamage); }
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\"=[]}")); }
C4AulScriptEngine::C4AulScriptEngine(): C4PropListStaticMember(nullptr, nullptr, ::Strings.RegString("Global")), ErrorHandler(&DefaultErrorHandler) { GlobalNamedNames.Reset(); GlobalNamed.Reset(); GlobalNamed.SetNameList(&GlobalNamedNames); GlobalConstNames.Reset(); GlobalConsts.Reset(); GlobalConsts.SetNameList(&GlobalConstNames); Child0 = ChildL = nullptr; RegisterGlobalConstant("Global", C4VPropList(this)); }
void C4ScriptHost::UnLink() { C4PropList * p = GetPropList(); if (p) { p->C4PropList::Clear(); p->SetProperty(P_Prototype, C4VPropList(Engine->GetPropList())); } // includes will have to be re-resolved now IncludesResolved = false; if (State > ASS_PREPARSED) State = ASS_PREPARSED; }
void C4Effect::Execute(C4Object *pObj) { // get effect list C4Effect **ppEffectList = pObj ? &pObj->pEffects : &Game.pGlobalEffects; // execute all effects not marked as dead C4Effect *pEffect = this, **ppPrevEffect=ppEffectList; do { // effect dead? if (pEffect->IsDead()) { // delete it, then C4Effect *pNextEffect = pEffect->pNext; pEffect->pNext = NULL; delete pEffect; // next effect *ppPrevEffect = pEffect = pNextEffect; } else { // execute effect: time elapsed ++pEffect->iTime; // check timer execution if (pEffect->iInterval && !(pEffect->iTime % pEffect->iInterval)) { if (pEffect->pFnTimer) { 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 pEffect->Kill(pObj); } // next effect ppPrevEffect = &pEffect->pNext; pEffect = pEffect->pNext; } } while (pEffect); }
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 * pEffect = new C4Effect(pForObj, szName, iPrio, iTimerInterval, pCmdTarget, idCmdTarget, rVal1, rVal2, rVal3, 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) 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) { C4Effect * pEffect2 = pEffect->pNext->Check(pForObj, szName->GetCStr(), iPrio, iTimerInterval, rVal1, rVal2, rVal3, rVal4); if (pEffect2) { // effect denied (iResult = -1), added to an effect (iResult = Number of that effect) // or added to an effect that destroyed itself (iResult = -2) if (pEffect2 != (C4Effect*)C4Fx_Effect_Deny && pEffect2 != (C4Effect*)C4Fx_Effect_Annul) return pEffect2; // effect is still marked dead return 0; } } // init effect // higher-priority effects must be deactivated temporarily, and then reactivated regarding the new effect // higher-level effects should not be inserted during the process of removing or adding a lower-level effect // 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); // 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 (fRemoveUpper && pEffect->pNext && pEffect->pFnStart) pEffect->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())) pForObj->SetOnFire(true); return pEffect; }
bool C4MapScriptHost::InitializeMap(C4SLandscape *pLandscape, C4TextureMap *pTexMap, C4MaterialMap *pMatMap, uint32_t iPlayerCount, std::unique_ptr<CSurface8> *pmap_fg_surface, std::unique_ptr <CSurface8>* pmap_bg_surface) { // Init scripted map by calling InitializeMap in the proper scripts. If *pmap_surface is given, it will pass the existing map to be modified by script. assert(pmap_fg_surface); assert(pmap_bg_surface); this->pTexMap = pTexMap; this->pMatMap = pMatMap; // Don't bother creating surfaces if the functions aren't defined if (!LayerPrototype->GetFunc(PSF_InitializeMap)) { C4PropList *scen_proplist = ::GameScript.ScenPropList._getPropList(); if (!scen_proplist || !scen_proplist->GetFunc(PSF_InitializeMap)) return false; } // Create proplist as script context std::unique_ptr<C4MapScriptMap> map(CreateMap()); // Drawing on existing map or create new? if (*pmap_fg_surface && *pmap_bg_surface) { // Existing map map->SetSurfaces(std::move(*pmap_fg_surface), std::move(*pmap_bg_surface)); } else { assert(!*pmap_fg_surface && !*pmap_bg_surface); // No existing map. Create new. int32_t map_wdt,map_hgt; pLandscape->GetMapSize(map_wdt, map_hgt, iPlayerCount); if (!map->CreateSurface(map_wdt, map_hgt)) return false; } C4AulParSet Pars(C4VPropList(map.get())); C4Value result = map->Call(PSF_InitializeMap, &Pars); if (!result) result = ::GameScript.Call(PSF_InitializeMap, &Pars); // Map creation done. if (result) { map->ConvertSkyToTransparent(); } std::tie(*pmap_fg_surface, *pmap_bg_surface) = map->ReleaseSurfaces(); return !!result; }
void C4Effect::ClearAll(C4Object *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, // because this could hang the engine with poorly coded effects if (pNext) pNext->ClearAll(pObj, 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; } // Update OnFire cache if (pObj && WildcardMatch(C4Fx_AnyFire, GetName()) && IsDead()) if (!Get(C4Fx_AnyFire)) pObj->SetOnFire(false); }
void C4AulScriptEngine::Clear() { #ifndef NOAULDEBUG // stop debugger delete C4AulDebug::GetDebugger(); #endif while (Child0) if (Child0->Delete()) delete Child0; else Child0->Unreg(); // clear own stuff C4PropListStaticMember::Clear(); // reset values warnCnt = errCnt = lineCnt = 0; // resetting name lists will reset all data lists, too // except not... GlobalNamedNames.Reset(); GlobalConstNames.Reset(); GlobalConsts.Reset(); GlobalConsts.SetNameList(&GlobalConstNames); RegisterGlobalConstant("Global", C4VPropList(this)); GlobalNamed.Reset(); GlobalNamed.SetNameList(&GlobalNamedNames); delete pGlobalEffects; pGlobalEffects=nullptr; UserFiles.clear(); // Delete all global proplists made static (breaks // cyclic references). for (C4Value& value: OwnedPropLists) { C4PropList* plist = value.getPropList(); if (plist) { if (plist->Delete()) delete plist; else plist->Clear(); } } OwnedPropLists.clear(); }
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: 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_Time: *pResult = C4VInt(iTime); return true; } } return C4PropListNumbered::GetPropertyByS(k, pResult); }
void C4Effect::Kill(C4Object *pObj) { // active? C4Effect *pLastRemovedEffect=NULL; if (IsActive()) // then temp remove all higher priority effects TempRemoveUpperEffects(pObj, 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 (pFnStart && iPriority!=1) pFnStart->Exec(CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(this), C4VInt(C4FxCall_TempAddForRemoval))); // 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; // reactivate other effects TempReaddUpperEffects(pObj, pLastRemovedEffect); // Update OnFire cache if (pObj && WildcardMatch(C4Fx_AnyFire, GetName())) if (!Get(C4Fx_AnyFire)) pObj->SetOnFire(false); }
void C4Effect::TempRemoveUpperEffects(C4Object *pObj, bool fTempRemoveThis, C4Effect **ppLastRemovedEffect) { if (pObj && !pObj->Status) return; // this will be invalid! // priority=1: no callbacks if (iPriority == 1) return; // remove from high to low priority // recursive implementation... C4Effect *pEff = pNext; while (pEff) if (pEff->IsActive()) break; else pEff = pEff->pNext; // temp remove active effects with higher priority if (pEff) pEff->TempRemoveUpperEffects(pObj, true, ppLastRemovedEffect); // temp remove this if (fTempRemoveThis) { FlipActive(); // Update OnFire cache if (pObj && WildcardMatch(C4Fx_AnyFire, GetName())) 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 (!*ppLastRemovedEffect) *ppLastRemovedEffect = this; } }
C4PropListScen(const C4PropListStatic * parent, C4String * key): C4PropListStatic(NULL, parent, key) { C4PropList * proto = C4PropList::NewStatic(ScriptEngine.GetPropList(), this, &::Strings.P[P_Prototype]); C4PropListStatic::SetPropertyByS(&::Strings.P[P_Prototype], C4VPropList(proto)); }
void C4ScriptLibrary::RegisterWithEngine(C4AulScriptEngine *engine) { engine->RegisterGlobalConstant(ParentKeyName->GetCStr(), C4VPropList(this)); CreateFunctions(); Freeze(); }
void C4Effect::TempReaddUpperEffects(C4Object *pObj, C4Effect *pLastReaddEffect) { // nothing to do? - this will also happen if TempRemoveUpperEffects did nothing due to priority==1 if (!pLastReaddEffect) return; if (pObj && !pObj->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->pFnStart && pEff->iPriority!=1) pEff->pFnStart->Exec(pEff->CommandTarget, &C4AulParSet(C4VObj(pObj), C4VPropList(pEff), C4VInt(C4FxCall_Temp))); if (pObj && WildcardMatch(C4Fx_AnyFire, pEff->GetName())) pObj->SetOnFire(true); } // done? if (pEff == pLastReaddEffect) break; } }
bool C4Def::Load(C4Group &hGroup, StdMeshSkeletonLoader &loader, DWORD dwLoadWhat, const char *szLanguage, C4SoundSystem *pSoundSystem, C4DefGraphicsPtrBackup *gfx_backup ) { bool AddFileMonitoring = false; if (Game.pFileMonitor && !SEqual(hGroup.GetFullName().getData(),Filename) && !hGroup.IsPacked()) AddFileMonitoring = true; // Store filename SCopy(hGroup.GetFullName().getData(),Filename); // Verbose log filename if (Config.Graphics.VerboseObjectLoading>=3) Log(hGroup.GetFullName().getData()); if (AddFileMonitoring) Game.pFileMonitor->AddDirectory(Filename); // Pre-read all images and shader stuff because they ar eaccessed in unpredictable order during loading hGroup.PreCacheEntries(C4CFN_ShaderFiles); hGroup.PreCacheEntries(C4CFN_ImageFiles); LoadMeshMaterials(hGroup, gfx_backup); bool fSuccess = LoadParticleDef(hGroup); // Read DefCore if (fSuccess) fSuccess = LoadDefCore(hGroup); // Skip def: don't even read sounds! if (fSuccess && Game.C4S.Definitions.SkipDefs.GetIDCount(id, 1)) return false; // Read sounds, even if not a valid def (for pure ocd sound folders) if (dwLoadWhat & C4D_Load_Sounds) LoadSounds(hGroup, pSoundSystem); // cancel if not a valid definition if (!fSuccess) return false; // Read and parse SolidMask bitmap if (!LoadSolidMask(hGroup)) return false; // Read surface bitmap, meshes, skeletons if ((dwLoadWhat & C4D_Load_Bitmap) && !LoadGraphics(hGroup, loader)) return false; // Read string table C4Language::LoadComponentHost(&StringTable, hGroup, C4CFN_ScriptStringTbl, szLanguage); // Register ID with script engine ::ScriptEngine.RegisterGlobalConstant(id.ToString(), C4VPropList(this)); ParentKeyName = ::Strings.RegString(id.ToString()); // Read script if (dwLoadWhat & C4D_Load_Script) LoadScript(hGroup, szLanguage); // Read clonknames if (dwLoadWhat & C4D_Load_ClonkNames) LoadClonkNames(hGroup, pClonkNames, szLanguage); // Read clonkranks if (dwLoadWhat & C4D_Load_RankNames) LoadRankNames(hGroup, szLanguage); // Read rankfaces if (dwLoadWhat & C4D_Load_RankFaces) LoadRankFaces(hGroup); // Temporary flag if (dwLoadWhat & C4D_Load_Temporary) Temporary=true; return true; }
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) { // 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->CommandTarget, &C4AulParSet(C4VString(szCheckEffect), C4VObj(pForObj), C4VPropList(pCheck), rVal1, rVal2, rVal3, rVal4)).getInt(); if (iResult == C4Fx_Effect_Deny) // effect denied return (C4Effect*)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 (C4Effect*)C4Fx_Effect_Annul; } else // other effect is the target effect number return pAddToEffect; } // added to no effect and not denied return 0; }