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(pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(iNumber), C4VInt(C4FxCall_TempAddForRemoval))); // remove this effect int32_t iPrevPrio = iPriority; SetDead(); if (pFnStop) if (pFnStop->Exec(pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(iNumber))).getInt() == C4Fx_Stop_Deny) // effect denied to be removed: recover iPriority = iPrevPrio; // reactivate other effects TempReaddUpperEffects(pObj, pLastRemovedEffect); }
bool C4Object::Exit(int32_t iX, int32_t iY, int32_t iR, C4Real iXDir, C4Real iYDir, C4Real iRDir, bool fCalls) { // 1. Exit the current container. // 2. Update Contents of container object and set Contained to nullptr. // 3. Set offset position/motion if desired. // 4. Call Ejection for container and Departure for object. // Not contained C4Object *pContainer=Contained; if (!pContainer) return false; // Remove object from container pContainer->Contents.Remove(this); pContainer->UpdateMass(); pContainer->SetOCF(); // No container Contained=nullptr; // Position/motion fix_x=itofix(iX); fix_y=itofix(iY); fix_r=itofix(iR); BoundsCheck(fix_x, fix_y); xdir=iXDir; ydir=iYDir; rdir=iRDir; // Misc updates Mobile=true; InLiquid=false; CloseMenu(true); UpdateFace(true); SetOCF(); // Object list callback (before script callbacks, because script callbacks may enter again) ObjectListChangeListener.OnObjectContainerChanged(this, pContainer, nullptr); // Engine calls if (fCalls) pContainer->Call(PSF_Ejection,&C4AulParSet(this)); if (fCalls) Call(PSF_Departure,&C4AulParSet(pContainer)); // Success (if the obj wasn't "re-entered" by script) return !Contained; }
BOOL ObjectComPunch(C4Object *cObj, C4Object *pTarget, int32_t punch) { if (!cObj || !pTarget) return FALSE; if (!punch) if (pTarget->GetPhysical()->Fight) punch=BoundBy<int32_t>(5*cObj->GetPhysical()->Fight/pTarget->GetPhysical()->Fight,0,10); if (!punch) return TRUE; bool fBlowStopped = !!pTarget->Call(PSF_QueryCatchBlow,&C4AulParSet(C4VObj(cObj))); if (fBlowStopped && punch>1) punch=punch/2; // half damage for caught blow, so shield+armor help in fistfight and vs monsters pTarget->DoEnergy(-punch, false, C4FxCall_EngGetPunched, cObj->Controller); int32_t tdir=+1; if (cObj->Action.Dir==DIR_Left) tdir=-1; pTarget->Action.ComDir=COMD_Stop; // No tumbles when blow was caught if (fBlowStopped) return FALSE; // Hard punch if (punch>=10) if (ObjectActionTumble(pTarget,pTarget->Action.Dir,FIXED100(150)*tdir,itofix(-2))) { pTarget->LastEnergyLossCausePlayer = cObj->Controller; // for kill tracing when pushing enemies off a cliff pTarget->Call(PSF_CatchBlow,&C4AulParSet(C4VInt(punch), C4VObj(cObj))); return TRUE; } // Regular punch if (ObjectActionGetPunched(pTarget,FIXED100(250)*tdir,Fix0)) { pTarget->LastEnergyLossCausePlayer = cObj->Controller; // for kill tracing when pushing enemies off a cliff pTarget->Call(PSF_CatchBlow,&C4AulParSet(C4VInt(punch), C4VObj(cObj))); return TRUE; } return FALSE; }
bool ObjectComPunch(C4Object *cObj, C4Object *pTarget, int32_t punch) { if (!cObj || !pTarget) return false; if (!punch) return true; bool fBlowStopped = !!pTarget->Call(PSF_QueryCatchBlow,&C4AulParSet(cObj)); if (fBlowStopped && punch>1) punch=punch/2; // half damage for caught blow, so shield+armor help in fistfight and vs monsters pTarget->DoEnergy(-punch, false, C4FxCall_EngGetPunched, cObj->Controller); int32_t tdir=+1; if (cObj->Action.Dir==DIR_Left) tdir=-1; pTarget->Action.ComDir=COMD_Stop; // No tumbles when blow was caught if (fBlowStopped) return false; // Hard punch if (punch>=10) if (ObjectActionTumble(pTarget,pTarget->Action.Dir,C4REAL100(150)*tdir,itofix(-2))) { pTarget->Call(PSF_CatchBlow,&C4AulParSet(punch, cObj)); return true; } // Regular punch if (ObjectActionGetPunched(pTarget,C4REAL100(250)*tdir,Fix0)) { pTarget->Call(PSF_CatchBlow,&C4AulParSet(punch, cObj)); return true; } return false; }
int C4Effect::CallTimer(int time) { if (!GetCallbackScript()) return Call(P_Timer, &C4AulParSet(time)).getInt(); if (pFnTimer) return pFnTimer->Exec(GetCallbackScript(), &C4AulParSet(Obj(Target), this, time)).getInt(); return C4Fx_Execute_Kill; }
int C4Effect::CallStop(int reason, bool temporary) { if (!GetCallbackScript()) return Call(P_Stop, &C4AulParSet(reason, temporary)).getInt(); if (pFnStop) return pFnStop->Exec(GetCallbackScript(), &C4AulParSet(Obj(Target), this, reason, temporary)).getInt(); return C4Fx_OK; }
int C4Effect::CallStart(int temporary, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { if (!GetCallbackScript()) return Call(P_Start, &C4AulParSet(temporary, var1, var2, var3, var4)).getInt(); if (pFnStart) return pFnStart->Exec(GetCallbackScript(), &C4AulParSet(Obj(Target), this, temporary, var1, var2, var3, var4)).getInt(); return C4Fx_OK; }
int C4Effect::CallEffect(const char * effect, const C4Value &var1, const C4Value &var2, const C4Value &var3, const C4Value &var4) { if (!GetCallbackScript()) return Call(P_Effect, &C4AulParSet(effect, var1, var2, var3, var4)).getInt(); if (pFnEffect) return pFnEffect->Exec(GetCallbackScript(), &C4AulParSet(effect, Obj(Target), this, var1, var2, var3, var4)).getInt(); return C4Fx_OK; }
bool ObjectComGrab(C4Object *cObj, C4Object *pTarget) { if (!pTarget) return false; if (cObj->GetProcedure()!=DFA_WALK) return false; if (!ObjectActionPush(cObj,pTarget)) return false; cObj->Call(PSF_Grab, &C4AulParSet(pTarget, true)); if (pTarget->Status && cObj->Status) pTarget->Call(PSF_Grabbed, &C4AulParSet(cObj, true)); return true; }
void C4Effect::CallDamage(int32_t & damage, int damagetype, int plr) { if (!GetCallbackScript()) { C4AulFunc *pFn = GetFunc(P_Damage); if (pFn) damage = pFn->Exec(this, &C4AulParSet(damage, damagetype, plr)).getInt(); } else if (pFnDamage) damage = pFnDamage->Exec(GetCallbackScript(), &C4AulParSet(Obj(Target), this, damage, damagetype, plr)).getInt(); }
BOOL ObjectComGrab(C4Object *cObj, C4Object *pTarget) { if (!pTarget) return FALSE; if (cObj->GetProcedure()!=DFA_WALK) return FALSE; if (!ObjectActionPush(cObj,pTarget)) return FALSE; cObj->Call(PSF_Grab, &C4AulParSet(C4VObj(pTarget), C4VBool(true))); if (pTarget->Status && cObj->Status) { pTarget->Controller = cObj->Controller; pTarget->Call(PSF_Grabbed, &C4AulParSet(C4VObj(cObj), C4VBool(true))); } return TRUE; }
int32_t C4Def::GetValue(C4Object *pInBase, int32_t iBuyPlayer) { C4Value r = Call(PSF_CalcDefValue, &C4AulParSet(pInBase, iBuyPlayer)); int32_t iValue = Value; if (r != C4VNull) iValue = r.getInt(); // do any adjustments based on where the item is bought if (pInBase) { r = pInBase->Call(PSF_CalcBuyValue, &C4AulParSet(this, iValue)); if (r != C4VNull) iValue = r.getInt(); } return iValue; }
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 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 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(); // temp callbacks only for higher priority effects if (pFnStop && iPriority != 1) pFnStop->Exec(pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(iNumber), C4VInt(C4FxCall_Temp), C4VBool(true))); if (!*ppLastRemovedEffect) *ppLastRemovedEffect = this; } }
bool ObjectComPut(C4Object *cObj, C4Object *pTarget, C4Object *pThing) { // No object specified, first from contents if (!pThing) pThing = cObj->Contents.GetObject(); // Nothing to put if (!pThing) return false; // No target if (!pTarget) return false; // Grabbing: check C4D_Grab_Put if (pTarget!=cObj->Contained) if (!(pTarget->Def->GrabPutGet & C4D_Grab_Put)) { // No grab put: fail return false; } // Target no fullcon if (!(pTarget->OCF & OCF_FullCon)) return false; // Transfer thing bool fRejectCollect; if (!pThing->Enter(pTarget, true, true, &fRejectCollect)) return false; // Put call to object script cObj->Call(PSF_Put); // Target collection call pTarget->Call(PSF_Collection,&C4AulParSet(pThing, true)); // Success return true; }
BOOL ObjectComPut(C4Object *cObj, C4Object *pTarget, C4Object *pThing) { // No object specified, first from contents if (!pThing) pThing = cObj->Contents.GetObject(); // Nothing to put if (!pThing) return FALSE; // No target if (!pTarget) return FALSE; // Grabbing: check C4D_Grab_Put if (pTarget!=cObj->Contained) if (!(pTarget->Def->GrabPutGet & C4D_Grab_Put)) { // Was meant to be a drop anyway if (ValidPlr(cObj->Owner)) if (Game.Players.Get(cObj->Owner)->LastComDownDouble) return ObjectComDrop(cObj, pThing); // No grab put: fail return FALSE; } // Target no fullcon if (!(pTarget->OCF & OCF_FullCon)) return FALSE; // Check target collection limit if (pTarget->Def->CollectionLimit && (pTarget->Contents.ObjectCount()>=pTarget->Def->CollectionLimit)) return FALSE; // Transfer thing bool fRejectCollect; if (!pThing->Enter(pTarget, TRUE, true, &fRejectCollect)) return FALSE; // Put call to object script cObj->Call(PSF_Put); // Target collection call pTarget->Call(PSF_Collection,&C4AulParSet(C4VObj(pThing), C4VBool(TRUE))); // Success return TRUE; }
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(); }
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)); }
BOOL ObjectComUnGrab(C4Object *cObj) { // Only if pushing, -> stand if (cObj->GetProcedure() == DFA_PUSH) { C4Object *pTarget = cObj->Action.Target; if (ObjectActionStand(cObj)) { if (!cObj->CloseMenu(false)) return FALSE; cObj->Call(PSF_Grab, &C4AulParSet(C4VObj(pTarget), C4VBool(false))); if (pTarget && pTarget->Status && cObj->Status) pTarget->Call(PSF_Grabbed, &C4AulParSet(C4VObj(cObj), C4VBool(false))); return TRUE; } } return FALSE; }
void ObjectComDigDouble(C4Object *cObj) // "Activation" by DFA_WALK, DFA_DIG, DFA_SWIM { C4Object *pTarget; DWORD ocf; C4PhysicalInfo *phys=cObj->GetPhysical(); // Contents activation (first contents object only) if (cObj->Contents.GetObject()) if (!! cObj->Contents.GetObject()->Call(PSF_Activate,&C4AulParSet(C4VObj(cObj)))) return; // Linekit: Line construction (move to linekit script...) if (cObj->Contents.GetObject() && (cObj->Contents.GetObject()->id==C4ID_Linekit)) { ObjectComLineConstruction(cObj); return; } // Chop ocf=OCF_Chop; if (phys->CanChop) if (cObj->GetProcedure()!=DFA_SWIM) if ((pTarget=Game.Objects.AtObject(cObj->x,cObj->y,ocf,cObj))) if (ocf & OCF_Chop) { PlayerObjectCommand(cObj->Owner,C4CMD_Chop,pTarget); return; } // Line construction pick up ocf=OCF_LineConstruct; if (phys->CanConstruct) if (!cObj->Contents.GetObject()) if ((pTarget=Game.Objects.AtObject(cObj->x,cObj->y,ocf,cObj))) if (ocf & OCF_LineConstruct) if (ObjectComLineConstruction(cObj)) return; // Own activation call if (!! cObj->Call(PSF_Activate, &C4AulParSet(C4VObj(cObj)))) return; }
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); }
C4Effect * C4Effect::Init(C4PropList *pForObj, int32_t iPrio, const C4Value &rVal1, const C4Value &rVal2, const C4Value &rVal3, const C4Value &rVal4) { Target = pForObj; // 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) { C4Effect * pEffect2 = pNext->Check(GetName(), iPrio, iInterval, 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; C4AulFunc * pFn; if (!GetCallbackScript()) { Call(P_Construction, &C4AulParSet(rVal1, rVal2, rVal3, rVal4)).getInt(); if (pForObj && !pForObj->Status) return 0; pFn = GetFunc(P_Start); } else pFn = pFnStart; if (fRemoveUpper && pNext && pFn) TempRemoveUpperEffects(false, &pLastRemovedEffect); // bad things may happen if (pForObj && !pForObj->Status) return 0; // this will be invalid! iPriority = iPrio; // validate effect now if (CallStart(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 && pFn) TempReaddUpperEffects(pLastRemovedEffect); if (pForObj && !pForObj->Status) return 0; // this will be invalid! // Update OnFire cache if (!IsDead() && pForObj && WildcardMatch(C4Fx_AnyFire, GetName())) pForObj->SetOnFire(true); return this; }
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; }
void C4Object::DirectComContents(C4Object *pTarget, bool fDoCalls) { // safety if (!pTarget || !pTarget->Status || pTarget->Contained != this) return; // Desired object already at front? if (Contents.GetObject() == pTarget) return; // select object via script? if (fDoCalls) if (Call("~ControlContents", &C4AulParSet(pTarget))) return; // default action if (!(Contents.ShiftContents(pTarget))) return; // Selection sound if (fDoCalls) if (!Contents.GetObject()->Call("~Selection", &C4AulParSet(this))) StartSoundEffect("Clonk::Action::Grab",false,100,this); // update menu with the new item in "put" entry if (Menu && Menu->IsActive() && Menu->IsContextMenu()) { Menu->Refill(); } // Done return; }
bool ObjectComUnGrab(C4Object *cObj) { // Only if pushing, -> stand if (cObj->GetProcedure() == DFA_PUSH) { C4Object *pTarget = cObj->Action.Target; if (ObjectActionStand(cObj)) { if (!cObj->CloseMenu(false)) return false; cObj->Call(PSF_Grab, &C4AulParSet(pTarget, false)); // clear action target cObj->Action.Target = nullptr; if (pTarget && pTarget->Status && cObj->Status) { pTarget->Call(PSF_Grabbed, &C4AulParSet(cObj, false)); } return true; } } return false; }
bool C4PlayerList::Remove(C4Player *pPlr, bool fDisconnect, bool fNoCalls) { if (!pPlr) return false; // inform script if (!fNoCalls) ::Game.GRBroadcast(PSF_RemovePlayer, &C4AulParSet(C4VInt(pPlr->Number), C4VInt(pPlr->Team))); // Transfer ownership of other objects to team members if (!fNoCalls) pPlr->NotifyOwnedObjects(); // NET2: update player info list if (pPlr->ID) { C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(pPlr->ID); if (pInfo) { pInfo->SetRemoved(); if (fDisconnect) pInfo->SetDisconnected(); } // if player wasn't evaluated, store round results anyway if (!pPlr->Evaluated) Game.RoundResults.EvaluatePlayer(pPlr); } C4Player *pPrev=First; while (pPrev && pPrev->Next!=pPlr) pPrev=pPrev->Next; if (pPrev) pPrev->Next=pPlr->Next; else First=pPlr->Next; // Remove eliminated crew if (!fNoCalls) pPlr->RemoveCrewObjects(); // Clear object info pointers pPlr->CrewInfoList.DetachFromObjects(); // Clear viewports ::Viewports.CloseViewport(pPlr->Number, fNoCalls); // Check fullscreen viewports FullScreen.ViewportCheck(); // Remove player delete pPlr; // Validate object owners ::Objects.ValidateOwners(); // Update console Console.UpdateMenus(); return true; }
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::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; }