Example #1
0
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);
}
Example #3
0
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();
}
Example #5
0
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; }

}
Example #7
0
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;
	}
Example #8
0
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);
	}
}
Example #10
0
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;
	}
Example #11
0
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;
}
Example #12
0
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;
}
Example #13
0
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();
}
Example #14
0
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();
}
Example #15
0
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;
}
Example #17
0
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;
}
Example #18
0
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;
}
Example #19
0
void C4ObjectList::CloseMenus()
{
	C4Object *cobj; C4ObjectLink *clnk;
	for (clnk=First; clnk && (cobj=clnk->Obj); clnk=clnk->Next)
		cobj->CloseMenu(true);
}
Example #20
0
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();
}