void C4RoundResults::EvaluateGoals(C4IDList &GoalList, C4IDList &FulfilledGoalList, int32_t iPlayerNumber) { // clear prev GoalList.Clear(); FulfilledGoalList.Clear(); // Items int32_t cnt; C4ID idGoal; for (cnt=0; (idGoal=::Objects.GetListID(C4D_Goal,cnt)); cnt++) { // determine if the goal is fulfilled - do the calls even if the menu is not to be opened to ensure synchronization bool fFulfilled = false;; C4Object *pObj = C4Id2Def(idGoal) ? ::Objects.Find(::Definitions.ID2Def(idGoal)) : NULL; if (pObj) { // Check fulfilled per player, this enables the possibility of rivalry. C4AulParSet pars(C4VInt(iPlayerNumber)); fFulfilled = !!pObj->Call(PSF_IsFulfilled, &pars); } GoalList.SetIDCount(idGoal, cnt, true); if (fFulfilled) FulfilledGoalList.SetIDCount(idGoal, 1, true); } }
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 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); }
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)); }
void C4Object::DoMovement() { int32_t iContact=0; bool fAnyContact=false; int iContacts = 0; BYTE fTurned=0,fRedirectYR=0,fNoAttach=0; // Restrictions if (Def->NoHorizontalMove) xdir=0; // Dig free target area C4PropList* pActionDef = GetAction(); if (pActionDef) if (pActionDef->GetPropertyInt(P_DigFree)) { int ctcox, ctcoy; // Shape size square if (pActionDef->GetPropertyInt(P_DigFree)==1) { ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir); ::Landscape.DigFreeRect(ctcox+Shape.GetX(),ctcoy+Shape.GetY(),Shape.Wdt,Shape.Hgt,this); } // Free size round (variable size) else { ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir); int32_t rad = pActionDef->GetPropertyInt(P_DigFree); if (Con<FullCon) rad = rad*6*Con/5/FullCon; ::Landscape.DigFree(ctcox,ctcoy-1,rad,this); } } // store previous movement and ocf C4Real oldxdir(xdir), oldydir(ydir); uint32_t old_ocf = OCF; bool fMoved = false; C4Real new_x = fix_x + xdir; C4Real new_y = fix_y + ydir; SideBounds(new_x); if (!Action.t_attach) // Unattached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = { // Horizontal movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Move to target while (fixtoi(new_x) != fixtoi(fix_x)) { // Next step int step = Sign(new_x - fix_x); uint32_t border_hack_contacts = 0; iContact=ContactCheck(GetX() + step, GetY(), &border_hack_contacts); if (iContact || border_hack_contacts) { fAnyContact=true; iContacts |= t_contact | border_hack_contacts; } if (iContact) { // Abort horizontal movement new_x = fix_x; // Vertical redirection (always) RedirectForce(xdir,ydir,-1); ApplyFriction(ydir,ContactVtxFriction(this)); } else // Free horizontal movement { DoMotion(step, 0); fMoved = true; } } // Vertical movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Movement target new_y = fix_y + ydir; // Movement bounds (vertical) VerticalBounds(new_y); // Move to target while (fixtoi(new_y) != fixtoi(fix_y)) { // Next step int step = Sign(new_y - fix_y); if ((iContact=ContactCheck(GetX(), GetY() + step, nullptr, ydir > 0))) { fAnyContact=true; iContacts |= t_contact; new_y = fix_y; // Vertical contact horizontal friction ApplyFriction(xdir,ContactVtxFriction(this)); // Redirection slide or rotate if (!ContactVtxCNAT(this,CNAT_Left)) RedirectForce(ydir,xdir,-1); else if (!ContactVtxCNAT(this,CNAT_Right)) RedirectForce(ydir,xdir,+1); else { // living things are always capable of keeping their rotation if (OCF & OCF_Rotate) if (iContact==1) if (!Alive) { RedirectForce(ydir,rdir,-ContactVtxWeight(this)); fRedirectYR=1; } ydir=0; } } else // Free vertical movement { DoMotion(0,step); fMoved = true; } } } if (Action.t_attach) // Attached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = { VerticalBounds(new_y); // Move to target do { // Set next step target int step_x = 0, step_y = 0; if (fixtoi(new_x) != GetX()) step_x = Sign(fixtoi(new_x) - GetX()); else if (fixtoi(new_y) != GetY()) step_y = Sign(fixtoi(new_y) - GetY()); int32_t ctx = GetX() + step_x; int32_t cty = GetY() + step_y; // Attachment check if (!Shape.Attach(ctx,cty,Action.t_attach)) fNoAttach=1; else { // Attachment change to ctx/cty overrides target if (ctx != GetX() + step_x) { xdir = Fix0; new_x = itofix(ctx); } if (cty != GetY() + step_y) { ydir = Fix0; new_y = itofix(cty); } } // Contact check & evaluation uint32_t border_hack_contacts = 0; iContact=ContactCheck(ctx,cty,&border_hack_contacts); if (iContact || border_hack_contacts) { fAnyContact=true; iContacts |= border_hack_contacts | t_contact; } if (iContact) { // Abort movement if (ctx != GetX()) { ctx = GetX(); new_x = fix_x; } if (cty != GetY()) { cty = GetY(); new_y = fix_y; } } DoMotion(ctx - GetX(), cty - GetY()); fMoved = true; } while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY()); } if(fix_x != new_x || fix_y != new_y) { fMoved = true; if (pSolidMaskData) pSolidMaskData->Remove(true); fix_x = new_x; fix_y = new_y; } // Rotation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (OCF & OCF_Rotate && !!rdir) { C4Real target_r = fix_r + rdir * 5; // Rotation limit if (Def->Rotateable>1) { if (target_r > itofix(Def->Rotateable)) { target_r = itofix(Def->Rotateable); rdir=0; } if (target_r < itofix(-Def->Rotateable)) { target_r = itofix(-Def->Rotateable); rdir=0; } } int32_t ctx=GetX(); int32_t cty=GetY(); // Move to target while (fixtoi(fix_r) != fixtoi(target_r)) { // Save step undos C4Real lcobjr = fix_r; C4Shape lshape=Shape; // Try next step fix_r += Sign(target_r - fix_r); UpdateShape(); // attached rotation: rotate around attachment pos if (Action.t_attach && !fNoAttach) { // more accurately, attachment should be evaluated by a rotation around the attachment vertex // however, as long as this code is only used for some surfaces adjustment for large vehicles, // it's enough to assume rotation around the center ctx=GetX(); cty=GetY(); // evaluate attachment, but do not bother about attachment loss // that will then be done in next execution cycle Shape.Attach(ctx,cty,Action.t_attach); } // check for contact if ((iContact=ContactCheck(ctx,cty))) // Contact { fAnyContact=true; iContacts |= t_contact; // Undo step and abort movement Shape=lshape; target_r = fix_r = lcobjr; // last UpdateShape-call might have changed sector lists! UpdatePos(); // Redirect to GetY() if (iContact==1) if (!fRedirectYR) RedirectForce(rdir,ydir,-1); // Stop rotation rdir=0; } else { fTurned=1; if (ctx != GetX() || cty != GetY()) { fix_x = itofix(ctx); fix_y = itofix(cty); } } } // Circle bounds if (target_r < -FixHalfCircle) { target_r += FixFullCircle; } if (target_r > +FixHalfCircle) { target_r -= FixFullCircle; } fix_r = target_r; } // Reput solid mask if moved by motion if (fMoved || fTurned) UpdateSolidMask(true); // Misc checks =========================================================================================== // InLiquid check // this equals C4Object::UpdateLiquid, but the "fNoAttach=false;"-line if (IsInLiquidCheck()) // In Liquid { if (!InLiquid) // Enter liquid { if (OCF & OCF_HitSpeed2) if (Mass>3) Splash(GetX(),GetY()+1,std::min(Shape.Wdt*Shape.Hgt/10,20),this); fNoAttach=false; InLiquid=1; } } else // Out of liquid { if (InLiquid) // Leave liquid InLiquid=0; } // Contact Action if (fAnyContact) { t_contact = iContacts; ContactAction(); } // Attachment Loss Action if (fNoAttach) NoAttachAction(); // Movement Script Execution if (fAnyContact) { C4AulParSet pars(C4VInt(fixtoi(oldxdir, 100)), C4VInt(fixtoi(oldydir, 100))); if (old_ocf & OCF_HitSpeed1) Call(PSF_Hit, &pars); if (old_ocf & OCF_HitSpeed2) Call(PSF_Hit2, &pars); if (old_ocf & OCF_HitSpeed3) Call(PSF_Hit3, &pars); } // Rotation gfx if (fTurned) UpdateFace(true); else // pos changed? if (fMoved) UpdatePos(); }
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 C4GameObjects::CrossCheck() // Every Tick1 by ExecObjects { C4Object *obj1 = nullptr, *obj2 = nullptr; DWORD ocf1, ocf2, focf, tocf; // AtObject-Check: Checks for first match of obj1 at obj2 // Checks for this frame focf = tocf = OCF_None; // Medium level: Fight if (!Tick5) { focf |= OCF_FightReady; tocf |= OCF_FightReady; } // Very low level: Incineration if (!Tick35) { focf |= OCF_OnFire; tocf |= OCF_Inflammable; } if (focf && tocf) for (C4ObjectList::iterator iter = begin(); iter != end() && (obj1 = *iter); ++iter) if (obj1->Status && !obj1->Contained) if (obj1->OCF & focf) { ocf1 = obj1->OCF; ocf2 = tocf; if (obj2 = AtObject(obj1->x, obj1->y, ocf2, obj1)) { // Incineration if ((ocf1 & OCF_OnFire) && (ocf2 & OCF_Inflammable)) if (!Random(obj2->Def->ContactIncinerate)) { obj2->Incinerate(obj1->GetFireCausePlr(), FALSE, obj1); continue; } // Fight if ((ocf1 & OCF_FightReady) && (ocf2 & OCF_FightReady)) if (Game.Players.Hostile(obj1->Owner, obj2->Owner)) { // RejectFight callback C4AulParSet parset1(C4VObj(obj2)); C4AulParSet parset2(C4VObj(obj1)); if (obj1->Call(PSF_RejectFight, &parset1).getBool()) continue; if (obj2->Call(PSF_RejectFight, &parset2).getBool()) continue; ObjectActionFight(obj1, obj2); ObjectActionFight(obj2, obj1); continue; } } } // Reverse area check: Checks for all obj2 at obj1 focf = tocf = OCF_None; // High level: Collection, Hit if (!Tick3) { focf |= OCF_Collection; tocf |= OCF_Carryable; } focf |= OCF_Alive; tocf |= OCF_HitSpeed2; if (focf && tocf) for (C4ObjectList::iterator iter = begin(); iter != end() && (obj1 = *iter); ++iter) if (obj1->Status && !obj1->Contained && (obj1->OCF & focf)) { uint32_t Marker = GetNextMarker(); C4LSector *pSct; for (C4ObjectList *pLst = obj1->Area.FirstObjects(&pSct); pLst; pLst = obj1->Area.NextObjects(pLst, &pSct)) for (C4ObjectList::iterator iter2 = pLst->begin(); iter2 != pLst->end() && (obj2 = *iter2); ++iter2) if (obj2->Status && !obj2->Contained && (obj2 != obj1) && (obj2->OCF & tocf)) if (Inside<int32_t>(obj2->x - (obj1->x + obj1->Shape.x), 0, obj1->Shape.Wdt - 1)) if (Inside<int32_t>(obj2->y - (obj1->y + obj1->Shape.y), 0, obj1->Shape.Hgt - 1)) if (obj1->pLayer == obj2->pLayer) { // handle collision only once if (obj2->Marker == Marker) continue; obj2->Marker = Marker; // Hit if ((obj2->OCF & OCF_HitSpeed2) && (obj1->OCF & OCF_Alive) && (obj2->Category & C4D_Object)) if (!obj1->Call(PSF_QueryCatchBlow, &C4AulParSet(C4VObj(obj2)))) { if (true /* "realistic" hit energy */) { FIXED dXDir = obj2->xdir - obj1->xdir, dYDir = obj2->ydir - obj1->ydir; int32_t iHitEnergy = fixtoi( (dXDir * dXDir + dYDir * dYDir) * obj2->Mass / 5); iHitEnergy = Max<int32_t>( iHitEnergy / 3, !!iHitEnergy); // hit energy reduced to 1/3rd, // but do not drop to zero because // of this division obj1->DoEnergy(-iHitEnergy / 5, false, C4FxCall_EngObjHit, obj2->Controller); int tmass = Max<int32_t>(obj1->Mass, 50); if (!Tick3 || (obj1->Action.Act >= 0 && obj1->Def->ActMap[obj1->Action.Act].Procedure != DFA_FLIGHT)) obj1->Fling(obj2->xdir * 50 / tmass, -Abs(obj2->ydir / 2) * 50 / tmass, false, obj2->Controller); obj1->Call(PSF_CatchBlow, &C4AulParSet(C4VInt(-iHitEnergy / 5), C4VObj(obj2))); } else { obj1->DoEnergy(-obj2->Mass / 5, false, C4FxCall_EngObjHit, obj2->Controller); int tmass = Max<int32_t>(obj1->Mass, 50); obj1->Fling(obj2->xdir * 50 / tmass, -Abs(obj2->ydir / 2) * 50 / tmass, false, obj2->Controller); obj1->Call(PSF_CatchBlow, &C4AulParSet(C4VInt(-obj2->Mass / 5), C4VObj(obj2))); } // obj1 might have been tampered with if (!obj1->Status || obj1->Contained || !(obj1->OCF & focf)) goto out1; continue; } // Collection if ((obj1->OCF & OCF_Collection) && (obj2->OCF & OCF_Carryable)) if (Inside<int32_t>( obj2->x - (obj1->x + obj1->Def->Collection.x), 0, obj1->Def->Collection.Wdt - 1)) if (Inside<int32_t>( obj2->y - (obj1->y + obj1->Def->Collection.y), 0, obj1->Def->Collection.Hgt - 1)) { // if(!pLst->First) BREAKPOINT_HERE; obj1->Collect(obj2); // if(!pLst->First) BREAKPOINT_HERE; // obj1 might have been tampered with if (!obj1->Status || obj1->Contained || !(obj1->OCF & focf)) goto out1; } } out1: ; } // Contained-Check: Checks for matching Contained // Checks for this frame focf = tocf = OCF_None; // Low level: Fight if (!Tick10) { focf |= OCF_FightReady; tocf |= OCF_FightReady; } if (focf && tocf) for (C4ObjectList::iterator iter = begin(); iter != end() && (obj1 = *iter); ++iter) if (obj1->Status && obj1->Contained && (obj1->OCF & focf)) { for (C4ObjectList::iterator iter2 = obj1->Contained->Contents.begin(); iter2 != end() && (obj2 = *iter2); ++iter2) if (obj2->Status && obj2->Contained && (obj2 != obj1) && (obj2->OCF & tocf)) if (obj1->pLayer == obj2->pLayer) { ocf1 = obj1->OCF; ocf2 = obj2->OCF; // Fight if ((ocf1 & OCF_FightReady) && (ocf2 & OCF_FightReady)) if (Game.Players.Hostile(obj1->Owner, obj2->Owner)) { ObjectActionFight(obj1, obj2); ObjectActionFight(obj2, obj1); // obj1 might have been tampered with if (!obj1->Status || obj1->Contained || !(obj1->OCF & focf)) goto out2; continue; } } out2: ; } }
int32_t FnFxFireStart(C4AulContext *ctx, C4Object *pObj, int32_t iNumber, int32_t iTemp, int32_t iCausedBy, bool fBlasted, C4Object *pIncineratingObject) { // safety if (!pObj) return -1; // temp readd if (iTemp) { pObj->SetOnFire(true); return 1; } // fail if already on fire if (pObj->GetOnFire()) return -1; // get associated effect C4Effect *pEffect; if (!(pEffect = pObj->pEffects)) return -1; if (!(pEffect = pEffect->Get(iNumber, true))) return -1; // structures must eject contents now, because DoCon is not guaranteed to be // executed! // In extinguishing material BOOL fFireCaused = TRUE; int32_t iMat; if (MatValid(iMat = GBackMat(pObj->x, pObj->y))) if (Game.Material.Map[iMat].Extinguisher) { // blasts should changedef in water, too! if (fBlasted) if (pObj->Def->BurnTurnTo != C4ID_None) pObj->ChangeDef(pObj->Def->BurnTurnTo); // no fire caused fFireCaused = FALSE; } // BurnTurnTo if (fFireCaused) if (pObj->Def->BurnTurnTo != C4ID_None) pObj->ChangeDef(pObj->Def->BurnTurnTo); // eject contents C4Object *cobj; if (!pObj->Def->IncompleteActivity && !pObj->Def->NoBurnDecay) while (cobj = pObj->Contents.GetObject()) { cobj->Controller = iCausedBy; // update controller, so incinerating a hut // full of flints attributes the damage to // the incinerator if (pObj->Contained) cobj->Enter(pObj->Contained); else cobj->Exit(cobj->x, cobj->y); } // Detach attached objects cobj = 0; if (!pObj->Def->IncompleteActivity && !pObj->Def->NoBurnDecay) while (cobj = Game.FindObject(0, 0, 0, 0, 0, OCF_All, 0, pObj, 0, 0, ANY_OWNER, cobj)) if ((cobj->Action.Act > ActIdle) && (cobj->Def->ActMap[cobj->Action.Act].Procedure == DFA_ATTACH)) cobj->SetAction(ActIdle); // fire caused? if (!fFireCaused) { // if object was blasted but not incinerated (i.e., inside extinguisher) // do a script callback if (fBlasted) pObj->Call(PSF_IncinerationEx, &C4AulParSet(C4VInt(iCausedBy))); return -1; } // determine fire appearance int32_t iFireMode; if (!(iFireMode = pObj->Call(PSF_FireMode).getInt())) { // set default fire modes DWORD dwCat = pObj->Category; if (dwCat & (C4D_Living | C4D_StaticBack)) // Tiere, Bäume iFireMode = C4Fx_FireMode_LivingVeg; else if (dwCat & (C4D_Structure | C4D_Vehicle)) // Gebäude und Fahrzeuge // sind unten meist kantig iFireMode = C4Fx_FireMode_StructVeh; else iFireMode = C4Fx_FireMode_Object; } else if (!Inside<int32_t>(iFireMode, 1, C4Fx_FireMode_Last)) { DebugLogF("Warning: FireMode %d of object %s (%s) is invalid!", iFireMode, pObj->GetName(), pObj->Def->GetName()); iFireMode = C4Fx_FireMode_Object; } // store causes in effect vars FxFireVarMode(pEffect).SetInt(iFireMode); FxFireVarCausedBy(pEffect) .SetInt(iCausedBy); // used in C4Object::GetFireCause and timer! FxFireVarBlasted(pEffect).SetBool(fBlasted); FxFireVarIncineratingObj(pEffect).SetObject(pIncineratingObject); // Set values pObj->SetOnFire(true); pObj->FirePhase = Random(MaxFirePhase); if (pObj->Shape.Wdt * pObj->Shape.Hgt > 500) StartSoundEffect("Inflame", false, 100, pObj); if (pObj->Def->Mass >= 100) StartSoundEffect("Fire", true, 100, pObj); // Engine script call pObj->Call(PSF_Incineration, &C4AulParSet(C4VInt(iCausedBy))); // Done, success return C4Fx_OK; }
C4Effect::C4Effect(C4Object *pForObj, const char *szName, int32_t iPrio, int32_t iTimerIntervall, C4Object *pCmdTarget, C4ID idCmdTarget, C4Value &rVal1, C4Value &rVal2, C4Value &rVal3, C4Value &rVal4, bool fDoCalls, int32_t &riStoredAsNumber) : EffectVars(0) { C4Effect *pPrev, *pCheck; // assign values SCopy(szName, Name, C4MaxDefString); iPriority = 0; // effect is not yet valid; some callbacks to other effects are // done before riStoredAsNumber = 0; iIntervall = iTimerIntervall; iTime = 0; pCommandTarget = pCmdTarget; idCommandTarget = idCmdTarget; AssignCallbackFunctions(); // get effect target C4Effect **ppEffectList = pForObj ? &pForObj->pEffects : &Game.pGlobalEffects; // assign a unique number for that object iNumber = 1; for (pCheck = *ppEffectList; pCheck; pCheck = pCheck->pNext) if (pCheck->iNumber >= iNumber) iNumber = pCheck->iNumber + 1; // register into object pPrev = *ppEffectList; if (pPrev && Abs(pPrev->iPriority) < iPrio) { while (pCheck = pPrev->pNext) if (Abs(pCheck->iPriority) >= iPrio) break; else pPrev = pCheck; // insert after previous pNext = pPrev->pNext; pPrev->pNext = this; } else { // insert as first effect pNext = *ppEffectList; *ppEffectList = this; } // no calls to be done: finished here if (!fDoCalls) return; // 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 && pNext) { int32_t iResult = pNext->Check(pForObj, Name, iPrio, iIntervall, rVal1, rVal2, rVal3, rVal4); if (iResult) { // effect denied (iResult = -1), added to an effect (iResult = Number of // that effect) // or added to an effect that destroyed itself (iResult = -2) if (iResult != C4Fx_Effect_Deny) riStoredAsNumber = iResult; // effect is still marked dead return; } } // 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 && pNext && pFnStart) TempRemoveUpperEffects(pForObj, false, &pLastRemovedEffect); // bad things may happen if (pForObj && !pForObj->Status) return; // this will be invalid! iPriority = iPrio; // validate effect now if (pFnStart) if (pFnStart->Exec(pCommandTarget, &C4AulParSet(C4VObj(pForObj), C4VInt(iNumber), C4VInt(0), rVal1, rVal2, rVal3, rVal4)).getInt() == C4Fx_Start_Deny) // the effect denied to start: assume it hasn't, and mark it dead SetDead(); if (fRemoveUpper && pNext && pFnStart) TempReaddUpperEffects(pForObj, pLastRemovedEffect); if (pForObj && !pForObj->Status) return; // this will be invalid! // this effect has been created; hand back the number riStoredAsNumber = iNumber; }
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; }
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; } }
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; } }
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); }
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); }
TEST_F(AulTest, Eval) { EXPECT_EQ(C4VInt(42), RunExpr("eval(\"42\")")); EXPECT_EQ(C4VInt(42), RunCode("local i = 42; func Main() { return eval(\"this.i\"); }", false)); EXPECT_EQ(C4VInt(42), RunCode("local i; func Main() { eval(\"this.i = 42\"); return i; }", false)); }