C4Object* C4Object::CreateContents(C4PropList * PropList) { C4Object *nobj; if (!(nobj=Game.CreateObject(PropList,this,Owner))) return nullptr; if (!nobj->Enter(this)) { nobj->AssignRemoval(); return nullptr; } return nobj; }
void C4EditCursor::Draw(C4TargetFacet &cgo) { ZoomDataStackItem zdsi(cgo.X, cgo.Y, cgo.Zoom); // Draw selection marks C4Object *cobj; C4ObjectLink *clnk; for (clnk=Selection.First; clnk && (cobj=clnk->Obj); clnk=clnk->Next) { // target pos (parallax) float offX, offY, newzoom; cobj->GetDrawPosition(cgo, offX, offY, newzoom); ZoomDataStackItem zdsi(cgo.X, cgo.Y, newzoom); FLOAT_RECT frame = { offX+cobj->Shape.x, offX+cobj->Shape.x + cobj->Shape.Wdt, offY+cobj->Shape.y, offY+cobj->Shape.y + cobj->Shape.Hgt }; DrawSelectMark(cgo, frame); // highlight selection if shift is pressed if (fShiftWasDown) { uint32_t dwOldMod = cobj->ColorMod; uint32_t dwOldBlitMode = cobj->BlitMode; cobj->ColorMod = 0xffffffff; cobj->BlitMode = C4GFXBLIT_CLRSFC_MOD2 | C4GFXBLIT_ADDITIVE; StdMeshInstance::FaceOrdering old_fo = StdSubMeshInstance::FO_Fixed; if(cobj->pMeshInstance) cobj->pMeshInstance->SetFaceOrdering(StdSubMeshInstance::FO_NearestToFarthest); cobj->Draw(cgo,-1); cobj->DrawTopFace(cgo, -1); if(cobj->pMeshInstance) cobj->pMeshInstance->SetFaceOrderingForClrModulation(cobj->ColorMod); cobj->ColorMod = dwOldMod; cobj->BlitMode = dwOldBlitMode; } } // Draw drag frame if (DragFrame) pDraw->DrawFrameDw(cgo.Surface, Min(X, X2) + cgo.X - cgo.TargetX, Min(Y, Y2) + cgo.Y - cgo.TargetY, Max(X, X2) + cgo.X - cgo.TargetX, Max(Y, Y2) + cgo.Y - cgo.TargetY, 0xffffffff); // Draw drag line if (DragLine) pDraw->DrawLineDw(cgo.Surface, X + cgo.X - cgo.TargetX, Y + cgo.Y - cgo.TargetY, X2 + cgo.X - cgo.TargetX, Y2 + cgo.Y - cgo.TargetY, 0xffffffff); // Draw drop target if (DropTarget) ::GraphicsResource.fctDropTarget.Draw(cgo.Surface, DropTarget->GetX() + cgo.X - cgo.TargetX - ::GraphicsResource.fctDropTarget.Wdt / 2, DropTarget->GetY() + DropTarget->Shape.y + cgo.Y - cgo.TargetY - ::GraphicsResource.fctDropTarget.Hgt); }
int C4ObjectList::ClearPointers(C4Object *pObj) { int rval=0; // Clear all primary list pointers while (Remove(pObj)) rval++; // Clear all sub pointers C4Object *cobj; C4ObjectLink *clnk; for (clnk=First; clnk && (cobj=clnk->Obj); clnk=clnk->Next) cobj->ClearPointers(pObj); return rval; }
void C4EditCursor::FrameSelection() { ClearSelection(); C4Object *cobj; C4ObjectLink *clnk; for (clnk=::Objects.First; clnk && (cobj=clnk->Obj); clnk=clnk->Next) if (cobj->Status) if (cobj->OCF & OCF_NotContained) { if (Inside(cobj->GetX(),Min(X,X2),Max(X,X2)) && Inside(cobj->GetY(),Min(Y,Y2),Max(Y,Y2))) AddToSelection(cobj); } OnSelectionChanged(); }
void Explosion(int32_t tx, int32_t ty, int32_t level, C4Object *inobj, int32_t iCausedBy, C4Object *pByObj, C4ID idEffect, const char *szEffect) { int32_t grade = BoundBy((level / 10) - 1, 1, 3); // Sound StdStrBuf sound = FormatString("Blast%c", '0' + grade); StartSoundEffect(sound.getData(), false, 100, pByObj); // Check blast containment C4Object *container = inobj; while (container && !container->Def->ContainBlast) container = container->Contained; // Uncontained blast effects if (!container) { // Incinerate landscape if (!Game.Landscape.Incinerate(tx, ty)) if (!Game.Landscape.Incinerate(tx, ty - 10)) if (!Game.Landscape.Incinerate(tx - 5, ty - 5)) Game.Landscape.Incinerate(tx + 5, ty - 5); // Create blast object or particle C4Object *pBlast; C4ParticleDef *pPrtDef = Game.Particles.pBlast; // particle override if (szEffect) { C4ParticleDef *pPrtDef2 = Game.Particles.GetDef(szEffect); if (pPrtDef2) pPrtDef = pPrtDef2; } else if (idEffect) pPrtDef = NULL; // create particle if (pPrtDef) { Game.Particles.Create(pPrtDef, (float)tx, (float)ty, 0.0f, 0.0f, (float)level, 0); if (SEqual2(pPrtDef->Name.getData(), "Blast")) Game.Particles.Cast(Game.Particles.pFSpark, level / 5 + 1, (float)tx, (float)ty, level, level / 2 + 1.0f, 0x00ef0000, level + 1.0f, 0xffff1010); } else if (pBlast = Game.CreateObjectConstruction( idEffect ? idEffect : C4Id("FXB1"), pByObj, iCausedBy, tx, ty + level, FullCon * level / 20)) pBlast->Call(PSF_Activate); } // Blast objects Game.BlastObjects(tx, ty, level, inobj, iCausedBy, pByObj); if (container != inobj) Game.BlastObjects(tx, ty, level, container, iCausedBy, pByObj); if (!container) { // Blast free landscape. After blasting objects so newly mined materials // don't get flinged Game.Landscape.BlastFree(tx, ty, level, grade, iCausedBy); } }
void C4EditCursor::UpdateDropTarget(DWORD dwKeyState) { C4Object *cobj; C4ObjectLink *clnk; DropTarget=NULL; if (dwKeyState & MK_CONTROL) if (Selection.GetObject()) for (clnk=::Objects.First; clnk && (cobj=clnk->Obj); clnk=clnk->Next) if (cobj->Status) if (!cobj->Contained) if (Inside<int32_t>(X-(cobj->GetX()+cobj->Shape.x),0,cobj->Shape.Wdt-1)) if (Inside<int32_t>(Y-(cobj->GetY()+cobj->Shape.y),0,cobj->Shape.Hgt-1)) if (!Selection.GetLink(cobj)) { DropTarget=cobj; break; } }
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 Smoke(int32_t tx, int32_t ty, int32_t level, DWORD dwClr) { if (Game.Particles.pSmoke) { Game.Particles.Create(Game.Particles.pSmoke, float(tx), float(ty) - level / 2, 0.0f, 0.0f, float(level), dwClr); return; } // User-defined smoke level int32_t SmokeLevel = GetSmokeLevel(); // Enough smoke out there already if (Game.Objects.ObjectCount(C4Id("FXS1")) >= SmokeLevel) return; // Create smoke level = BoundBy<int32_t>(level, 3, 32); C4Object *pObj; if (pObj = Game.CreateObjectConstruction(C4Id("FXS1"), NULL, NO_OWNER, tx, ty, FullCon * level / 32)) pObj->Call(PSF_Activate); }
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(iPlayerNumber); fFulfilled = !!pObj->Call(PSF_IsFulfilled, &pars); } GoalList.SetIDCount(idGoal, cnt, true); if (fFulfilled) FulfilledGoalList.SetIDCount(idGoal, 1, true); } }
BOOL Buy2Base(int32_t iPlr, C4Object *pBase, C4ID id, BOOL fShowErrors) { C4Object *pThing; // Validity if (!ValidPlr(iPlr)) return FALSE; if (!pBase || !ValidPlr(pBase->Base)) return FALSE; if (~Game.C4S.Game.Realism.BaseFunctionality & BASEFUNC_Buy) return FALSE; // Base owner hostile if (Hostile(iPlr,pBase->Base)) { if (!fShowErrors) return FALSE; StartSoundEffect("Error",false,100,pBase); sprintf(OSTR,LoadResStr("IDS_PLR_HOSTILE"),Game.Players.Get(pBase->Base)->GetName()); GameMsgPlayer(OSTR,iPlr); return FALSE; } // buy if (!(pThing=Game.Players.Get(pBase->Base)->Buy(id, fShowErrors, iPlr, pBase))) return FALSE; // Object enter target object pThing->Enter(pBase); // Success return TRUE; }
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; }
C4Object *C4GameObjects::AtObject(int ctx, int cty, DWORD &ocf, C4Object *exclude) { DWORD cocf; C4Object *cObj; C4ObjectLink *clnk; for (clnk = ObjectsAt(ctx, cty).First; clnk && (cObj = clnk->Obj); clnk = clnk->Next) if (!exclude || (cObj != exclude && exclude->pLayer == cObj->pLayer)) if (cObj->Status) { cocf = ocf | OCF_Exclusive; if (cObj->At(ctx, cty, cocf)) { // Search match if (cocf & ocf) { ocf = cocf; return cObj; } // EXCLUSIVE block else return NULL; } } return NULL; }
void C4SolidMask::Remove(bool fCauseInstability, bool fBackupAttachment) { // If put, restore background pixels from buffer // Not put if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; CheckConsistency(); // reput background pixels for (int ycnt = 0; ycnt < MaskPutRect.Hgt; ++ycnt) { BYTE *pPix = pSolidMaskMatBuff + (ycnt + MaskPutRect.ty) * MatBuffPitch + MaskPutRect.tx; for (int xcnt = 0; xcnt < MaskPutRect.Wdt; ++xcnt, ++pPix) // only if mask was used here if (*pPix != MCVehic) { // calc position in landscape int iTx = MaskPutRect.x + xcnt; int iTy = MaskPutRect.y + ycnt; // restore pixel here // The pPix-check ensures that only pixels that hads been overwritten by // this SolidMask are restored // Non-SolidMask-pixels should not happen here, because all relevant // landscape change routines should // temp remove SolidMasks before assert(_GBackPix(iTx, iTy) == MCVehic); _SBackPixIfMask(iTx, iTy, *pPix, MCVehic); // Instability if (fCauseInstability) Game.Landscape.CheckInstabilityRange(iTx, iTy); } } // Mask not put flag MaskPut = false; // update surrounding masks in that range C4TargetRect ClipRect; for (C4SolidMask *pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev) if (pSolid->MaskPut) if (pSolid->MaskPutRect.Overlap(MaskPutRect)) { // set clipping rect for all calls, since they may modify it ClipRect.Set(MaskPutRect.x, MaskPutRect.y, MaskPutRect.Wdt, MaskPutRect.Hgt, 0, 0); // doubled solidmask-pixels have just been removed in the clipped area! pSolid->MaskPut = false; // re-put the solidmask pSolid->Put(false, &ClipRect, false); } // backup attachment if desired: Backup old pos and all objects that attach to // or lie on the SolidMask if (fBackupAttachment) { MaskRemovalX = pForObject->x; MaskRemovalY = pForObject->y; iAttachingObjectsCount = 0; C4LArea SolidArea(&Game.Objects.Sectors, MaskPutRect.x - 1, MaskPutRect.y - 1, MaskPutRect.Wdt + 2, MaskPutRect.Hgt + 2); C4LSector *pSct; C4Object *pObj; for (C4ObjectList *pLst = SolidArea.FirstObjectShapes(&pSct); pLst; pLst = SolidArea.NextObjectShapes(pLst, &pSct)) for (C4ObjectLink *clnk = pLst->First; clnk; clnk = clnk->Next) if ((pObj = clnk->Obj) && pObj != pForObject && pObj->IsMoveableBySolidMask() && !pObj->Shape.CheckContact(pObj->x, pObj->y)) { // check for any contact to own SolidMask - attach-directions, bottom // - "stuck" (CNAT_Center) is ignored, because that causes problems // with things being stuck in basements :( int iVtx = 0; for (; iVtx < pObj->Shape.VtxNum; ++iVtx) if (pObj->Shape.GetVertexContact( iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->x, pObj->y, DensityProvider(pForObject, *this))) if (pObj->Shape.GetVertexContact( iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->x, pObj->y, DensityProvider(pForObject, *this))) break; if (iVtx == pObj->Shape.VtxNum) continue; // no contact // contact: Add object to list if (iAttachingObjectsCapacity == iAttachingObjectsCount) { iAttachingObjectsCapacity += 4; C4Object **ppNewAttachingObjects = new C4Object *[iAttachingObjectsCapacity]; if (iAttachingObjectsCount) memcpy(ppNewAttachingObjects, ppAttachingObjects, sizeof(C4Object *) * iAttachingObjectsCount); delete[] ppAttachingObjects; ppAttachingObjects = ppNewAttachingObjects; } ppAttachingObjects[iAttachingObjectsCount++] = pObj; } } CheckConsistency(); }
void C4SolidMask::Put(bool fCauseInstability, C4TargetRect *pClipRect, bool fRestoreAttachment) { // If not put, put mask to background, // storing background pixels in cSolidMask. // No mask if (!pSolidMask || !pSolidMaskMatBuff) { iAttachingObjectsCount = 0; return; } // Contained if (pForObject->Contained) { iAttachingObjectsCount = 0; return; } // Mask is put if (fCauseInstability) CheckConsistency(); bool RegularPut; if (!pClipRect) { // Regular Put: Update MaskPutRect and MaskPutRotation MaskPutRotation = pForObject->r; pClipRect = &MaskPutRect; RegularPut = true; } else { // Reput by C4SolidMask::Remove // Don't change MaskPutRotation or MaskPutRect // Intersect ClipRect with the MaskPutRect if (!pClipRect->ClipBy(MaskPutRect)) return; RegularPut = false; } // Lock mask surface int iPitch = pForObject->SolidMask.Wdt; int xcnt, ycnt, iTx, iTy; // Put mask pixels BYTE byPixel; // not rotated? if (!MaskPutRotation) { // calc put rect if (RegularPut) { int ox, oy; ox = pForObject->x + pForObject->Def->Shape.x + pForObject->SolidMask.tx; oy = pForObject->y + pForObject->Def->Shape.y + pForObject->SolidMask.ty; MaskPutRect.x = ox; if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.x = 0; } else MaskPutRect.tx = 0; MaskPutRect.y = oy; if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.y = 0; } else MaskPutRect.ty = 0; MaskPutRect.Wdt = Min<int32_t>(ox + pForObject->SolidMask.Wdt, GBackWdt) - MaskPutRect.x; MaskPutRect.Hgt = Min<int32_t>(oy + pForObject->SolidMask.Hgt, GBackHgt) - MaskPutRect.y; } // fill rect with mask for (ycnt = 0; ycnt < pClipRect->Hgt; ++ycnt) { BYTE *pPix = pSolidMask + (ycnt + pClipRect->ty) * pForObject->SolidMask.Wdt + pClipRect->tx; for (xcnt = 0; xcnt < pClipRect->Wdt; ++xcnt, ++pPix) { if (*pPix) { // solid mask present here // calc position in landscape iTx = pClipRect->x + xcnt; iTy = pClipRect->y + ycnt; // is background mat to be stored? always do this in the given rect if (!MaskPut) { // get background pixel byPixel = GBackPix(iTx, iTy); // store it. If MCVehic, also store in initial put, but won't be // used in restore // do not overwrite current value in re-put issued by // SolidMask-remover if (byPixel != MCVehic || RegularPut) pSolidMaskMatBuff[(ycnt + pClipRect->ty) * MatBuffPitch + xcnt + pClipRect->tx] = byPixel; } // and set mask _SBackPix(iTx, iTy, MCVehic); } else // no SolidMask: mark buffer as unused here if (!MaskPut) pSolidMaskMatBuff[(ycnt + pClipRect->ty) * MatBuffPitch + xcnt + pClipRect->tx] = MCVehic; } } } else { // calc matrix for given rotation FIXED Ma1 = Cos(itofix(-MaskPutRotation)), Ma2 = -Sin(itofix(-MaskPutRotation)), Mb1 = Sin(itofix(-MaskPutRotation)), Mb2 = Cos(itofix(-MaskPutRotation)); // get upper-left corner of landscape copy rect int centerx = pForObject->Def->Shape.x + pForObject->SolidMask.tx + pForObject->SolidMask.Wdt / 2; int centery = pForObject->Def->Shape.y + pForObject->SolidMask.ty + pForObject->SolidMask.Hgt / 2; int xstart = pForObject->x + fixtoi(Ma1 * itofix(centerx) - Ma2 * itofix(centery)) - MatBuffPitch / 2; int ystart = pForObject->y + fixtoi(-Mb1 * itofix(centerx) + Mb2 * itofix(centery)) - MatBuffPitch / 2; // store put rect if (RegularPut) { MaskPutRect.x = xstart; if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.Wdt = MaskPutRect.x; MaskPutRect.x = 0; } else { MaskPutRect.tx = 0; MaskPutRect.Wdt = 0; } MaskPutRect.y = ystart; if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.Hgt = MaskPutRect.y; MaskPutRect.y = 0; } else { MaskPutRect.ty = 0; MaskPutRect.Hgt = 0; } MaskPutRect.Wdt = Min<int32_t>(xstart + MatBuffPitch, GBackWdt) - MaskPutRect.x; MaskPutRect.Hgt = Min<int32_t>(ystart + MatBuffPitch, GBackHgt) - MaskPutRect.y; } // go through clipping rect const FIXED y0 = itofix(pClipRect->ty - MatBuffPitch / 2); const FIXED x0 = itofix(pClipRect->tx - MatBuffPitch / 2); iTy = pClipRect->y; int w = pForObject->SolidMask.Wdt; int h = pForObject->SolidMask.Hgt; FIXED ya = y0 * Ma2; FIXED yb = y0 * Mb2; for (ycnt = 0; ycnt < pClipRect->Hgt; ycnt++) { iTx = pClipRect->x; int i = (ycnt + pClipRect->ty) * MatBuffPitch + pClipRect->tx; FIXED xa = x0 * Ma1; FIXED xb = x0 * Mb1; for (xcnt = 0; xcnt < pClipRect->Wdt; xcnt++) { // calc position in solidmask buffer int iMx = fixtoi(xa + ya) + w / 2; int iMy = fixtoi(xb + yb) + h / 2; // in bounds? and solidmask? if (iMx >= 0 && iMy >= 0 && iMx < w && iMy < h && pSolidMask[iMy * iPitch + iMx]) { // is background mat to be stored? if (!MaskPut) { // get background pixel byPixel = _GBackPix(iTx, iTy); // store it. If MCVehic, also store in initial put, but won't be // used in restore // do not overwrite current value in re-put issued by // SolidMask-remover if (byPixel != MCVehic || RegularPut) pSolidMaskMatBuff[i + xcnt] = byPixel; } // set mask pix _SBackPix(iTx, iTy, MCVehic); } else if (!MaskPut) // mark pix as unused in buf pSolidMaskMatBuff[i + xcnt] = MCVehic; xa += Ma1; xb += Mb1; ++iTx; } ya += Ma2; yb += Mb2; ++iTy; } } // Store mask put status MaskPut = TRUE; // restore attached object positions if moved if (fRestoreAttachment && iAttachingObjectsCount) { int32_t dx = pForObject->x - MaskRemovalX; int32_t dy = pForObject->y - MaskRemovalY; if (dx | dy) for (int i = 0; i < iAttachingObjectsCount; ++i) { C4Object *pObj = ppAttachingObjects[i]; if (pObj->IsMoveableBySolidMask()) if (!pObj->Shape.ContactCheck(pObj->x + dx, pObj->y + dy)) if (pObj->iLastAttachMovementFrame != Game.FrameCounter) { pObj->iLastAttachMovementFrame = Game.FrameCounter; pObj->MovePosition(dx, dy); } } iAttachingObjectsCount = 0; } if (fCauseInstability) CheckConsistency(); }
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; }
bool C4EditCursor::DoContextMenu(DWORD dwKeyState) { bool fObjectSelected = !!Selection.ObjectCount(); #ifdef USE_WIN32_WINDOWS POINT point; GetCursorPos(&point); HMENU hContext = GetSubMenu(hMenu,0); SetMenuItemEnable( hContext, IDM_VIEWPORT_DELETE, fObjectSelected && Console.Editing); SetMenuItemEnable( hContext, IDM_VIEWPORT_DUPLICATE, fObjectSelected && Console.Editing); SetMenuItemEnable( hContext, IDM_VIEWPORT_CONTENTS, fObjectSelected && Selection.GetObject()->Contents.ObjectCount() && Console.Editing); SetMenuItemText(hContext,IDM_VIEWPORT_DELETE,LoadResStr("IDS_MNU_DELETE")); SetMenuItemText(hContext,IDM_VIEWPORT_DUPLICATE,LoadResStr("IDS_MNU_DUPLICATE")); SetMenuItemText(hContext,IDM_VIEWPORT_CONTENTS,LoadResStr("IDS_MNU_CONTENTS")); ObjselectDelItems(); C4FindObjectAtPoint pFO(X,Y); C4ValueArray * atcursor; atcursor = pFO.FindMany(::Objects, ::Objects.Sectors); // needs freeing (single object ptr) int itemcount = atcursor->GetSize(); if(itemcount > 0) { const int maxitems = 25; // Maximum displayed objects. if you raise it, also change note with IDM_VPORTDYN_FIRST in resource.h if(itemcount > maxitems) itemcount = maxitems+1; itemsObjselect.resize(itemcount+1); // +1 for a separator itemsObjselect[0].ItemId = IDM_VPORTDYN_FIRST; itemsObjselect[0].Object = NULL; AppendMenu(hContext, MF_SEPARATOR, IDM_VPORTDYN_FIRST, NULL); int i = 1; for(std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin() + 1; it != itemsObjselect.end(); ++it, ++i) { C4Object * obj = (*atcursor)[i-1].getObj(); assert(obj); it->ItemId = IDM_VPORTDYN_FIRST+i; it->Object = obj; AppendMenu(hContext, MF_STRING, it->ItemId, FormatString("%s #%i (%i/%i)", obj->GetName(), obj->Number, obj->GetX(), obj->GetY()).GetWideChar()); } if(atcursor->GetSize() > maxitems) { AppendMenu(hContext, MF_GRAYED, IDM_VPORTDYN_FIRST+maxitems+1, L"..."); itemsObjselect[maxitems+1].ItemId = IDM_VPORTDYN_FIRST+maxitems+1; itemsObjselect[maxitems+1].Object = NULL; } } delete atcursor; int32_t iItem = TrackPopupMenu( hContext, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NONOTIFY, point.x,point.y, 0, Console.hWindow, NULL); switch (iItem) { case IDM_VIEWPORT_DELETE: Delete(); break; case IDM_VIEWPORT_DUPLICATE: Duplicate(); break; case IDM_VIEWPORT_CONTENTS: GrabContents(); break; case 0: break; default: for(std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin() + 1; it != itemsObjselect.end(); ++it) if(it->ItemId == iItem) { DoContextObjsel(it->Object, (dwKeyState & MK_SHIFT) == 0); break; } break; } ObjselectDelItems(); #else #ifdef WITH_DEVELOPER_MODE gtk_widget_set_sensitive(itemDelete, fObjectSelected && Console.Editing); gtk_widget_set_sensitive(itemDuplicate, fObjectSelected && Console.Editing); gtk_widget_set_sensitive(itemGrabContents, fObjectSelected && Selection.GetObject()->Contents.ObjectCount() && Console.Editing); ObjselectDelItems(); C4FindObjectAtPoint pFO(X,Y); C4ValueArray * atcursor; atcursor = pFO.FindMany(::Objects, ::Objects.Sectors); // needs freeing int itemcount = atcursor->GetSize(); if(itemcount > 0) { itemsObjselect.resize(itemcount+1); // +1 for a separator itemsObjselect[0].MenuItem = gtk_separator_menu_item_new(); itemsObjselect[0].EditCursor = this; gtk_menu_shell_append(GTK_MENU_SHELL(menuContext), itemsObjselect[0].MenuItem); int i = 0; for(std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin() + 1; it != itemsObjselect.end(); ++it, ++i) { it->EditCursor = this; C4Object * obj = (*atcursor)[i].getObj(); assert(obj); it->Object = obj; GtkWidget * wdg = gtk_menu_item_new_with_label(FormatString("%s #%i (%i/%i)", obj->GetName(), obj->Number, obj->GetX(), obj->GetY()).getData()); it->MenuItem = wdg; gtk_menu_shell_append(GTK_MENU_SHELL(menuContext), wdg); g_signal_connect(G_OBJECT(wdg), "activate", G_CALLBACK(OnObjselect), &*it); } } delete atcursor; gtk_widget_show_all(menuContext); gtk_menu_popup(GTK_MENU(menuContext), NULL, NULL, NULL, NULL, 3, 0); #endif #endif return true; }
bool C4ObjectMenu::DoRefillInternal(bool &rfRefilled) { // Variables C4FacetSurface fctSymbol; C4Object *pObj; char szCaption[256+1],szCommand[256+1],szCommand2[256+1]; int32_t iCount; C4Def *pDef; C4IDList ListItems; C4Object *pTarget; C4Facet fctTarget; // Refill switch (Identification) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case C4MN_Activate: // Clear items ClearItems(); // Refill target if (!(pTarget=RefillObject)) return false; { // Add target contents items C4ObjectListIterator iter(pTarget->Contents); while ((pObj = iter.GetNext(&iCount))) { pDef = pObj->Def; if (pDef->NoGet) continue; // Prefer fully constructed objects if (~pObj->OCF & OCF_FullCon) { // easy way: only if first concat check matches // this doesn't catch all possibilities, but that will rarely matter C4Object *pObj2=pTarget->Contents.Find(pDef, ANY_OWNER, OCF_FullCon); if (pObj2) if (pObj2->CanConcatPictureWith(pObj)) pObj = pObj2; } // Caption sprintf(szCaption,LoadResStr("IDS_MENU_ACTIVATE"),(const char *) pObj->GetName()); // Picture fctSymbol.Set(fctSymbol.Surface, 0,0,C4SymbolSize,C4SymbolSize); pObj->Picture2Facet(fctSymbol); // Commands sprintf(szCommand,"SetCommand(\"Activate\",Object(%d))&&ExecuteCommand()",pObj->Number); sprintf(szCommand2,"SetCommand(\"Activate\",nil,%d,0,Object(%d),%s)&&ExecuteCommand()",pTarget->Contents.ObjectCount(pDef->id),pTarget->Number,pDef->id.ToString()); // Add menu item Add(szCaption,fctSymbol,szCommand,iCount,pObj,"",pDef->id,szCommand2,true,pObj->GetValue(pTarget, NO_OWNER)); // facet taken over (arrg!) fctSymbol.Default(); } } break; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case C4MN_Get: case C4MN_Contents: // Clear items ClearItems(); // Refill target if (!(pTarget = RefillObject)) return false; { // Add target contents items C4ObjectListIterator iter(pTarget->Contents); while ((pObj = iter.GetNext(&iCount))) { pDef = pObj->Def; if (pDef->NoGet) continue; // Prefer fully constructed objects if (~pObj->OCF & OCF_FullCon) { // easy way: only if first concat check matches // this doesn't catch all possibilities, but that will rarely matter C4Object *pObj2 = pTarget->Contents.Find(pDef, ANY_OWNER, OCF_FullCon); if (pObj2) if (pObj2->CanConcatPictureWith(pObj)) pObj = pObj2; } // Determine whether to get or activate bool fGet = true; if (!(pObj->OCF & OCF_Carryable)) fGet = false; // not a carryable item if (Identification == C4MN_Contents) { if (Object && !!Object->Call(PSF_RejectCollection, &C4AulParSet(pObj->Def, pObj))) fGet = false; // collection rejected } if (!(pTarget->OCF & OCF_Entrance)) fGet = true; // target object has no entrance: cannot activate - force get // Caption sprintf(szCaption, LoadResStr(fGet ? "IDS_MENU_GET" : "IDS_MENU_ACTIVATE"), (const char *)pObj->GetName()); // Picture fctSymbol.Set(fctSymbol.Surface, 0, 0, C4SymbolSize, C4SymbolSize); pObj->Picture2Facet(fctSymbol); // Primary command: get/activate single object sprintf(szCommand, "SetCommand(\"%s\", Object(%d)) && ExecuteCommand()", fGet ? "Get" : "Activate", pObj->Number); // Secondary command: get/activate all objects of the chosen type szCommand2[0] = 0; int32_t iAllCount; if ((iAllCount = pTarget->Contents.ObjectCount(pDef->id)) > 1) sprintf(szCommand2, "SetCommand(\"%s\", nil, %d,0, Object(%d), %s) && ExecuteCommand()", fGet ? "Get" : "Activate", iAllCount, pTarget->Number, pDef->id.ToString()); // Add menu item (with object) Add(szCaption, fctSymbol, szCommand, iCount, pObj, "", pDef->id, szCommand2); fctSymbol.Default(); } } break; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - default: // Not an internal menu return true; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } // Successfull internal refill rfRefilled = true; return true; }
BOOL C4PropertyDlg::Update() { if (!Active) return FALSE; StdStrBuf Output; idSelectedDef = C4ID_None; // Compose info text by selected object(s) switch (Selection.ObjectCount()) { // No selection case 0: Output = LoadResStr("IDS_CNS_NOOBJECT"); break; // One selected object case 1: { C4Object *cobj = Selection.GetObject(); // Type Output.AppendFormat(LoadResStr("IDS_CNS_TYPE"), cobj->GetName(), C4IdText(cobj->Def->id)); // Owner if (ValidPlr(cobj->Owner)) { Output.Append(LineFeed); Output.AppendFormat(LoadResStr("IDS_CNS_OWNER"), Game.Players.Get(cobj->Owner)->GetName()); } // Contents if (cobj->Contents.ObjectCount()) { Output.Append(LineFeed); Output.Append(LoadResStr("IDS_CNS_CONTENTS")); Output.Append(static_cast<const StdStrBuf &>( cobj->Contents.GetNameList(Game.Defs))); } // Action if (cobj->Action.Act != ActIdle) { Output.Append(LineFeed); Output.Append(LoadResStr("IDS_CNS_ACTION")); Output.Append(cobj->Def->ActMap[cobj->Action.Act].Name); } // Locals int cnt; bool fFirstLocal = true; for (cnt = 0; cnt < cobj->Local.GetSize(); cnt++) if (!!cobj->Local[cnt]) { // Header if (fFirstLocal) { Output.Append(LineFeed); Output.Append(LoadResStr("IDS_CNS_LOCALS")); fFirstLocal = false; } Output.Append(LineFeed); // Append id Output.AppendFormat(" Local(%d) = ", cnt); // write value Output.Append( static_cast<const StdStrBuf &>(cobj->Local[cnt].GetDataString())); } // Locals (named) for (cnt = 0; cnt < cobj->LocalNamed.GetAnzItems(); cnt++) { // Header if (fFirstLocal) { Output.Append(LineFeed); Output.Append(LoadResStr("IDS_CNS_LOCALS")); fFirstLocal = false; } Output.Append(LineFeed); // Append name Output.AppendFormat(" %s = ", cobj->LocalNamed.pNames->pNames[cnt]); // write value Output.Append(static_cast<const StdStrBuf &>( cobj->LocalNamed.pData[cnt].GetDataString())); } // Effects for (C4Effect *pEffect = cobj->pEffects; pEffect; pEffect = pEffect->pNext) { // Header if (pEffect == cobj->pEffects) { Output.Append(LineFeed); Output.Append(LoadResStr("IDS_CNS_EFFECTS")); } Output.Append(LineFeed); // Effect name Output.AppendFormat(" %s: Interval %d", pEffect->Name, pEffect->iIntervall); } // Store selected def idSelectedDef = cobj->id; break; } // Multiple selected objects default: Output.Format(LoadResStr("IDS_CNS_MULTIPLEOBJECTS"), Selection.ObjectCount()); break; } // Update info edit control #ifdef WITH_DEVELOPER_MODE GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); gtk_text_buffer_set_text( buffer, C4Language::IconvUtf8(Output.getData()).getData(), -1); #endif return TRUE; }
void C4ObjectList::CloseMenus() { C4Object *cobj; C4ObjectLink *clnk; for (clnk=First; clnk && (cobj=clnk->Obj); clnk=clnk->Next) cobj->CloseMenu(true); }
int C4GameObjects::Load(C4Group &hGroup, bool fKeepInactive) { Clear(!fKeepInactive); // Load data component StdStrBuf Source; if (!hGroup.LoadEntryString(C4CFN_ScenarioObjects, Source)) return 0; // Compile StdStrBuf Name = hGroup.GetFullName() + DirSep C4CFN_ScenarioObjects; if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(*this, false), Source, Name.getData())) return 0; // Process objects C4ObjectLink *cLnk = nullptr; C4Object *pObj = nullptr; bool fObjectNumberCollision = false; int32_t iMaxObjectNumber = 0; for (cLnk = Last; cLnk; cLnk = cLnk->Prev) { C4Object *pObj = cLnk->Obj; // check object number collision with inactive list if (fKeepInactive) { for (C4ObjectLink *clnk = InactiveObjects.First; clnk; clnk = clnk->Next) if (clnk->Obj->Number == pObj->Number) fObjectNumberCollision = true; } // keep track of numbers iMaxObjectNumber = Max<long>(iMaxObjectNumber, pObj->Number); // add to list of backobjects if (pObj->Category & C4D_Background) Game.BackObjects.Add(pObj, C4ObjectList::stMain, this); // add to list of foreobjects if (pObj->Category & C4D_Foreground) Game.ForeObjects.Add(pObj, C4ObjectList::stMain, this); // Unterminate end } // Denumerate pointers // if object numbers collideded, numbers will be adjusted afterwards // so fake inactive object list empty meanwhile C4ObjectLink *pInFirst = nullptr; if (fObjectNumberCollision) { pInFirst = InactiveObjects.First; InactiveObjects.First = NULL; } // denumerate pointers Denumerate(); // update object enumeration index now, because calls like UpdateTransferZone // might create objects Game.ObjectEnumerationIndex = Max(Game.ObjectEnumerationIndex, iMaxObjectNumber); // end faking and adjust object numbers if (fObjectNumberCollision) { InactiveObjects.First = pInFirst; // simply renumber all inactive objects for (cLnk = InactiveObjects.First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) pObj->Number = ++Game.ObjectEnumerationIndex; } // special checks: // -contained/contents-consistency // -StaticBack-objects zero speed for (cLnk = First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) { // staticback must not have speed if (pObj->Category & C4D_StaticBack) { pObj->xdir = pObj->ydir = 0; } // contained must be in contents list if (pObj->Contained) if (!pObj->Contained->Contents.GetLink(pObj)) { DebugLogF( "Error in Objects.txt: Container of #%d is #%d, but not found in " "contents list!", pObj->Number, pObj->Contained->Number); pObj->Contained->Contents.Add(pObj, C4ObjectList::stContents); } // all contents must have contained set; otherwise, remove them! C4Object *pObj2; for (C4ObjectLink *cLnkCont = pObj->Contents.First; cLnkCont; cLnkCont = cLnkCont->Next) { // check double links if (pObj->Contents.GetLink(cLnkCont->Obj) != cLnkCont) { DebugLogF("Error in Objects.txt: Double containment of #%d by #%d!", cLnkCont->Obj->Number, pObj->Number); // this remove-call will only remove the previous (dobuled) link, so // cLnkCont should be save pObj->Contents.Remove(cLnkCont->Obj); // contents checked already continue; } // check contents/contained-relation if ((pObj2 = cLnkCont->Obj)->Status) if (pObj2->Contained != pObj) { DebugLogF( "Error in Objects.txt: Object #%d not in container #%d as " "referenced!", pObj2->Number, pObj->Number); pObj2->Contained = pObj; } } } // sort out inactive objects C4ObjectLink *cLnkNext; for (cLnk = First; cLnk; cLnk = cLnkNext) { cLnkNext = cLnk->Next; if (cLnk->Obj->Status == C4OS_INACTIVE) { if (cLnk->Prev) cLnk->Prev->Next = cLnkNext; else First = cLnkNext; if (cLnkNext) cLnkNext->Prev = cLnk->Prev; else Last = cLnk->Prev; if (cLnk->Prev = InactiveObjects.Last) InactiveObjects.Last->Next = cLnk; else InactiveObjects.First = cLnk; InactiveObjects.Last = cLnk; cLnk->Next = NULL; Mass -= pObj->Mass; } } { C4DebugRecOff DBGRECOFF; // - script callbacks that would kill // DebugRec-sync for runtime start // update graphics UpdateGraphics(false); // Update faces UpdateFaces(false); // Update ocf SetOCF(); } // make sure list is sorted by category - after sorting out inactives, because // inactives aren't sorted into the main list FixObjectOrder(); // Sectors.Dump(); // misc updates for (cLnk = First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) { // add to plrview pObj->PlrFoWActualize(); // update flipdir (for old objects.txt with no flipdir defined) // assigns Action.DrawDir as well pObj->UpdateFlipDir(); } // Done return ObjectCount(); }