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 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; }
C4ValueArray *C4FindObject::FindMany(const C4ObjectList &Objs, const C4LSectors &Sct) { // Trivial case if (IsImpossible()) return new C4ValueArray(); C4Rect *pBounds = GetBounds(); if (!pBounds) return FindMany(Objs); // Prepare for array that may be generated C4ValueArray *pArray; int32_t iSize; // Check shape lists? if (UseShapes()) { // Get area C4LArea Area(&Game.Objects.Sectors, *pBounds); C4LSector *pSct; C4ObjectList *pLst = Area.FirstObjectShapes(&pSct); // Check if a single-sector check is enough if (!Area.Next(pSct)) return FindMany(pSct->ObjectShapes); // Set up array pArray = new C4ValueArray(32); iSize = 0; // Create marker, search all areas uint32_t iMarker = ::Game.Objects.GetNextMarker(); for (; pLst; pLst = Area.NextObjectShapes(pLst, &pSct)) for (C4ObjectLink *pLnk = pLst->First; pLnk; pLnk = pLnk->Next) if (pLnk->Obj->Status) if (pLnk->Obj->Marker != iMarker) { pLnk->Obj->Marker = iMarker; if (Check(pLnk->Obj)) { // Grow the array, if neccessary if (iSize >= pArray->GetSize()) pArray->SetSize(iSize * 2); // Add object (*pArray)[iSize++] = C4VObj(pLnk->Obj); } } } else { // Set up array pArray = new C4ValueArray(32); iSize = 0; // Search C4LArea Area(&Game.Objects.Sectors, *pBounds); C4LSector *pSct; for (C4ObjectList *pLst = Area.FirstObjects(&pSct); pLst; pLst = Area.NextObjects(pLst, &pSct)) for (C4ObjectLink *pLnk = pLst->First; pLnk; pLnk = pLnk->Next) if (pLnk->Obj->Status) if (Check(pLnk->Obj)) { // Grow the array, if neccessary if (iSize >= pArray->GetSize()) pArray->SetSize(iSize * 2); // Add object (*pArray)[iSize++] = C4VObj(pLnk->Obj); } } // Shrink array pArray->SetSize(iSize); // Recheck object status (may shrink array again) CheckObjectStatus(pArray); // Apply sorting if (pSort) pSort->SortObjects(pArray); return pArray; }
void C4ObjResort::Sort(C4ObjectLink *pFirst, C4ObjectLink *pLast) { #ifdef _DEBUG assert(Game.Objects.Sectors.CheckSort()); #endif // do a simple insertion-like sort C4ObjectLink *pCurr; // current link to analyse C4ObjectLink *pCurr2; // second (previous) link to analyse C4ObjectLink *pNewFirst; // next link to be first C4ObjectLink *pFirstBck = pFirst; // backup of first link // pre-build parameters C4AulParSet Pars; // loop until there's nothing left to sort while (pFirst != pLast) { // start from the very end of the list pCurr = pNewFirst = pLast; // loop the checks up to the first list item to check while (pCurr != pFirst) { // get second check item pCurr2 = pCurr->Prev; while (!pCurr2->Obj->Status) pCurr2 = pCurr2->Prev; // perform the check Pars[0].Set(C4VObj(pCurr->Obj)); Pars[1].Set(C4VObj(pCurr2->Obj)); if (OrderFunc->Exec(NULL, &Pars).getInt() < 0) { // so there's something to be reordered: swap the links // FIXME: Inform C4ObjectList about this reorder C4Object *pObj = pCurr->Obj; pCurr->Obj = pCurr2->Obj; pCurr2->Obj = pObj; // and readd to sector lists pCurr->Obj->Unsorted = pCurr2->Obj->Unsorted = TRUE; // grow list section to scan next pNewFirst = pCurr; } // advance in list pCurr = pCurr2; } // reduce area to be checked pFirst = pNewFirst; } #ifdef _DEBUG assert(Game.Objects.Sectors.CheckSort()); #endif // resort objects in sector lists for (pCurr = pFirstBck; pCurr != pLast->Next; pCurr = pCurr->Next) { C4Object *pObj = pCurr->Obj; if (pObj->Status && pObj->Unsorted) { pObj->Unsorted = FALSE; Game.Objects.UpdatePosResort(pObj); } } #ifdef _DEBUG assert(Game.Objects.Sectors.CheckSort()); #endif }
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; }
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)) { // 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; }
// return is to be freed by the caller C4ValueArray *C4FindObject::FindMany(const C4ObjectList &Objs) { // Trivial case if (IsImpossible()) return new C4ValueArray(); // Set up array C4ValueArray *pArray = new C4ValueArray(32); int32_t iSize = 0; // Search for (C4Object *obj : Objs) if (obj->Status) if (Check(obj)) { // Grow the array, if neccessary if (iSize >= pArray->GetSize()) pArray->SetSize(iSize * 2); // Add object (*pArray)[iSize++] = C4VObj(obj); } // Shrink array pArray->SetSize(iSize); // Recheck object status (may shrink array again) CheckObjectStatus(pArray); // Apply sorting if (pSort) pSort->SortObjects(pArray); return pArray; }
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)); }
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; }
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)); }
int32_t C4Object::AddObjectAndContentsToArray(C4ValueArray *target_array, int32_t index) { // add self, contents and child contents count recursively to value array. Return index after last added item. target_array->SetItem(index++, C4VObj(this)); for (C4Object *cobj : Contents) { index = cobj->AddObjectAndContentsToArray(target_array, index); } return index; }
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); }
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 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; }
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->pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(pEff->iNumber), C4VInt(C4FxCall_Temp))); } // done? if (pEff == pLastReaddEffect) break; } }
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(pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(iNumber), 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; } }
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); }
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; }
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: ; } }
void C4ObjResort::SortObject() { // safety if (pSortObj->Status != C4OS_NORMAL || pSortObj->Unsorted) return; // pre-build parameters C4AulParSet Pars; Pars[1].Set(C4VObj(pSortObj)); // first, check forward in list C4ObjectLink *pMoveLink = NULL; C4ObjectLink *pLnk = Game.Objects.GetLink(pSortObj); C4ObjectLink *pLnkBck = pLnk; C4Object *pObj2; int iResult; if (!pLnk) return; while (pLnk = pLnk->Next) { // get object pObj2 = pLnk->Obj; if (!pObj2->Status) continue; // does the category still match? if (!(pObj2->Category & pSortObj->Category)) break; // perform the check Pars[0].Set(C4VObj(pObj2)); iResult = OrderFunc->Exec(NULL, &Pars).getInt(); if (iResult > 0) break; if (iResult < 0) pMoveLink = pLnk; } // check if movement has to be done if (pMoveLink) { // move link directly after pMoveLink // FIXME: Inform C4ObjectList that this is a reorder, not a remove+insert // move out of current position Game.Objects.RemoveLink(pLnkBck); // put into new position Game.Objects.InsertLink(pLnkBck, pMoveLink); } else { // no movement yet: check backwards in list Pars[0].Set(C4VObj(pSortObj)); pLnk = pLnkBck; while (pLnk = pLnk->Prev) { // get object pObj2 = pLnk->Obj; if (!pObj2->Status) continue; // does the category still match? if (!(pObj2->Category & pSortObj->Category)) break; // perform the check Pars[1].Set(C4VObj(pObj2)); iResult = OrderFunc->Exec(NULL, &Pars).getInt(); if (iResult > 0) break; if (iResult < 0) pMoveLink = pLnk; } // no movement to be done? finish if (!pMoveLink) return; // move link directly before pMoveLink // move out of current position Game.Objects.RemoveLink(pLnkBck); // put into new position Game.Objects.InsertLinkBefore(pLnkBck, pMoveLink); } // object has been resorted: resort into area lists, too Game.Objects.UpdatePosResort(pSortObj); // done }