OpenClonk
C4Object.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* That which fills the world with life */
19 
20 #include "C4Include.h"
22 #include "object/C4Object.h"
23 
24 #include "control/C4Record.h"
25 #include "game/C4Application.h"
26 #include "game/C4GraphicsSystem.h"
27 #include "game/C4Physics.h"
28 #include "game/C4Viewport.h"
29 #include "graphics/C4Draw.h"
31 #include "gui/C4GameMessage.h"
33 #include "landscape/C4PXS.h"
34 #include "landscape/C4Particles.h"
35 #include "landscape/C4SolidMask.h"
36 #include "landscape/fow/C4FoW.h"
37 #include "lib/C4Random.h"
38 #include "object/C4Command.h"
39 #include "object/C4Def.h"
40 #include "object/C4DefList.h"
41 #include "object/C4GameObjects.h"
42 #include "object/C4MeshAnimation.h"
44 #include "object/C4ObjectCom.h"
45 #include "object/C4ObjectInfo.h"
46 #include "object/C4ObjectMenu.h"
47 #include "platform/C4SoundSystem.h"
48 #include "player/C4Player.h"
49 #include "player/C4PlayerList.h"
50 #include "player/C4RankSystem.h"
51 #include "script/C4AulExec.h"
52 #include "script/C4Effect.h"
53 
54 static void DrawVertex(C4Facet &cgo, float tx, float ty, int32_t col, int32_t contact)
55 {
56  if (Inside<int32_t>(tx,cgo.X,cgo.X+cgo.Wdt) && Inside<int32_t>(ty,cgo.Y,cgo.Y+cgo.Hgt))
57  {
58  pDraw->DrawLineDw(cgo.Surface, tx - 1, ty, tx + 1, ty, col, 0.5f);
59  pDraw->DrawLineDw(cgo.Surface, tx, ty - 1, tx, ty + 1, col, 0.5f);
60  if (contact) pDraw->DrawFrameDw(cgo.Surface,tx-1.5,ty-1.5,tx+1.5,ty+1.5,C4RGB(0xff, 0xff, 0xff));
61  }
62 }
63 
64 void C4Action::SetBridgeData(int32_t iBridgeTime, bool fMoveClonk, bool fWall, int32_t iBridgeMaterial)
65 {
66  // validity
67  iBridgeMaterial = std::min(iBridgeMaterial, ::MaterialMap.Num-1);
68  if (iBridgeMaterial < 0) iBridgeMaterial = 0xff;
69  iBridgeTime = Clamp<int32_t>(iBridgeTime, 0, 0xffff);
70  // mask in this->Data
71  Data = (uint32_t(iBridgeTime) << 16) + (uint32_t(fMoveClonk) << 8) + (uint32_t(fWall) << 9) + iBridgeMaterial;
72 }
73 
74 void C4Action::GetBridgeData(int32_t &riBridgeTime, bool &rfMoveClonk, bool &rfWall, int32_t &riBridgeMaterial)
75 {
76  // mask from this->Data
77  uint32_t uiData = Data;
78  riBridgeTime = (uint32_t(uiData) >> 16);
79  rfMoveClonk = !!(uiData & 0x100);
80  rfWall = !!(uiData & 0x200);
81  riBridgeMaterial = (uiData & 0xff);
82  if (riBridgeMaterial == 0xff) riBridgeMaterial = -1;
83 }
84 
86 {
87  FrontParticles = BackParticles = nullptr;
88  Default();
89 }
90 
92 {
93  id=C4ID::None;
94  nInfo.Clear();
95  RemovalDelay=0;
96  Owner=NO_OWNER;
97  Controller=NO_OWNER;
98  LastEnergyLossCausePlayer=NO_OWNER;
99  Category=0;
100  Con=0;
101  Mass=OwnMass=0;
102  Damage=0;
103  Energy=0;
104  Alive=false;
105  Breath=0;
106  InMat=MNone;
107  Color=0;
108  lightRange=0;
109  lightFadeoutRange=0;
110  lightColor=0xffffffff;
111  fix_x=fix_y=fix_r=0;
112  xdir=ydir=rdir=0;
113  Mobile=false;
114  Unsorted=false;
115  Initializing=false;
116  OnFire=false;
117  InLiquid=false;
118  EntranceStatus=false;
119  Audible=AudiblePan=0;
120  AudiblePlayer = NO_OWNER;
121  t_contact=0;
122  OCF=0;
123  Action.Default();
124  Shape.Default();
125  fOwnVertices=false;
126  Contents.Default();
127  SolidMask.Default();
128  HalfVehicleSolidMask=false;
129  PictureRect.Default();
130  Def=nullptr;
131  Info=nullptr;
132  Command=nullptr;
133  Contained=nullptr;
134  TopFace.Default();
135  Menu=nullptr;
136  MaterialContents=nullptr;
137  Marker=0;
138  ColorMod=0xffffffff;
139  BlitMode=0;
140  CrewDisabled=false;
141  Layer=nullptr;
142  pSolidMaskData=nullptr;
143  pGraphics=nullptr;
144  pMeshInstance=nullptr;
145  pDrawTransform=nullptr;
146  pEffects=nullptr;
147  pGfxOverlay=nullptr;
148  iLastAttachMovementFrame=-1;
149 
150  ClearParticleLists();
151 }
152 
153 bool C4Object::Init(C4PropList *pDef, C4Object *pCreator,
154  int32_t iOwner, C4ObjectInfo *pInfo,
155  int32_t nx, int32_t ny, int32_t nr,
156  C4Real nxdir, C4Real nydir, C4Real nrdir, int32_t iController)
157 {
159  // currently initializing
160  Initializing=true;
161 
162  // Def & basics
163  Owner=iOwner;
164  if (iController > NO_OWNER) Controller = iController; else Controller=iOwner;
165  LastEnergyLossCausePlayer=NO_OWNER;
166  Info=pInfo;
167  Def=pDef->GetDef(); assert(Def);
168  SetProperty(P_Prototype, C4VPropList(pDef));
169  id=Def->id;
170  if (Info) SetName(pInfo->Name);
171  Category=Def->Category;
172  Plane = Def->GetPlane(); assert(Plane);
173  Def->Count++;
174  if (pCreator) Layer=pCreator->Layer;
175 
176  // graphics
177  pGraphics = &Def->Graphics;
178  if (pGraphics->Type == C4DefGraphics::TYPE_Mesh)
179  {
180  pMeshInstance = new StdMeshInstance(*pGraphics->Mesh, Def->GrowthType ? 1.0f : static_cast<float>(Con)/static_cast<float>(FullCon));
181  pMeshInstance->SetFaceOrderingForClrModulation(ColorMod);
182  }
183  else
184  {
185  pMeshInstance = nullptr;
186  }
187  BlitMode = Def->BlitMode;
188 
189  // Position
190  if (!Def->Rotateable) { nr=0; nrdir=0; }
191  fix_x=itofix(nx);
192  fix_y=itofix(ny);
193  fix_r=itofix(nr);
194  xdir=nxdir; ydir=nydir; rdir=nrdir;
195 
196  // Initial mobility
197  if (!!xdir || !!ydir || !!rdir)
198  Mobile=true;
199 
200  // Mass
201  Mass=std::max<int32_t>(Def->Mass*Con/FullCon,1);
202 
203  // Life, energy, breath
204  if (Category & C4D_Living) Alive=true;
205  if (Alive) Energy=GetPropertyInt(P_MaxEnergy);
206  Breath=GetPropertyInt(P_MaxBreath);
207 
208  // Color
209  if (Def->ColorByOwner)
210  {
211  if (ValidPlr(Owner))
212  Color=::Players.Get(Owner)->ColorDw;
213  else
214  Color=0xff; // no-owner color: blue
215  }
216 
217  // Shape & face
218  Shape=Def->Shape;
219  SolidMask=Def->SolidMask;
220  CheckSolidMaskRect();
221  UpdateGraphics(false);
222  UpdateFace(true);
223 
224  // Initial audibility
225  Audible=::Viewports.GetAudibility(GetX(), GetY(), &AudiblePan, 0, &AudiblePlayer);
226 
227  // Initial OCF
228  SetOCF();
229 
230  // finished initializing
231  Initializing=false;
232 
233  return true;
234 }
235 
237 {
238  Clear();
239 
240 #if defined(_DEBUG)
241  // debug: mustn't be listed in any list now
243 #endif
244 }
245 
247 {
248  if (FrontParticles != nullptr)
249  Particles.ReleaseParticleList(FrontParticles);
250  if (BackParticles != nullptr)
251  Particles.ReleaseParticleList(BackParticles);
252  FrontParticles = BackParticles = nullptr;
253 }
254 
255 void C4Object::AssignRemoval(bool fExitContents)
256 {
257  // check status
258  if (!Status) return;
259  if (Config.General.DebugRec)
260  {
261  C4RCCreateObj rc;
262  memset(&rc, '\0', sizeof(rc));
263  rc.oei=Number;
264  if (Def && Def->GetName()) strncpy(rc.id, Def->GetName(), 32+1);
265  rc.x=GetX(); rc.y=GetY(); rc.ownr=Owner;
266  AddDbgRec(RCT_DsObj, &rc, sizeof(rc));
267  }
268  // Destruction call in container
269  if (Contained)
270  {
271  C4AulParSet pars(this);
272  Contained->Call(PSF_ContentsDestruction, &pars);
273  if (!Status) return;
274  }
275  // Destruction call
277  // Destruction-callback might have deleted the object already
278  if (!Status) return;
279  // remove all effects (extinguishes as well)
280  if (pEffects)
281  {
282  pEffects->ClearAll(C4FxCall_RemoveClear);
283  // Effect-callback might actually have deleted the object already
284  if (!Status) return;
285  }
286  // remove particles
287  ClearParticleLists();
288  // Action idle
289  SetAction(nullptr);
290  // Object system operation
291  if (Status == C4OS_INACTIVE)
292  {
293  // object was inactive: activate first, then delete
295  Status = C4OS_NORMAL;
296  ::Objects.Add(this);
297  }
298  Status=0;
299  // count decrease
300  Def->Count--;
301 
302  // get container for next actions
303  C4Object *pCont = Contained;
304  // remove or exit contents
305  for (C4Object *cobj : Contents)
306  {
307  if (fExitContents)
308  {
309  // move objects to parent container or exit them completely
310  bool fRejectCollect;
311  if (!pCont || !cobj->Enter(pCont, true, false, &fRejectCollect))
312  cobj->Exit(GetX(), GetY());
313  }
314  else
315  {
316  Contents.Remove(cobj);
317  cobj->Contained = nullptr;
318  cobj->AssignRemoval();
319  }
320  }
321  // remove this object from container *after* its contents have been removed!
322  if (pCont)
323  {
324  pCont->Contents.Remove(this);
325  pCont->UpdateMass();
326  pCont->SetOCF();
327  Contained=nullptr;
328  }
329  // Object info
330  if (Info) Info->Retire();
331  Info = nullptr;
332  // Object system operation
333  ClearRefs();
334  Game.ClearPointers(this);
335  ClearCommands();
336  if (pSolidMaskData)
337  {
338  delete pSolidMaskData;
339  pSolidMaskData = nullptr;
340  }
341  SolidMask.Wdt = 0;
342  RemovalDelay=2;
343 }
344 
345 void C4Object::UpdateShape(bool bUpdateVertices)
346 {
347 
348  // Line shape independent
349  if (Def->Line) return;
350 
351  // Copy shape from def
352  Shape.CopyFrom(Def->Shape, bUpdateVertices, !!fOwnVertices);
353 
354  // Construction zoom
355  if (Con!=FullCon)
356  {
357  if (Def->GrowthType)
358  Shape.Stretch(Con, bUpdateVertices);
359  else
360  Shape.Jolt(Con, bUpdateVertices);
361  }
362 
363  // Rotation
364  if (Def->Rotateable)
365  if (fix_r != Fix0)
366  Shape.Rotate(fix_r, bUpdateVertices);
367 
368  // covered area changed? to be on the save side, update pos
369  UpdatePos();
370 }
371 
373 {
374  // get new area covered
375  // do *NOT* do this while initializing, because object cannot be sorted by main list
376  if (!Initializing && Status == C4OS_NORMAL)
377  ::Objects.UpdatePos(this);
378 }
379 
380 void C4Object::UpdateFace(bool bUpdateShape, bool fTemp)
381 {
382 
383  // Update shape - NOT for temp call, because temnp calls are done in drawing routine
384  // must not change sync relevant data here (although the shape and pos *should* be updated at that time anyway,
385  // because a runtime join would desync otherwise)
386  if (!fTemp) { if (bUpdateShape) UpdateShape(); else UpdatePos(); }
387 
388  // SolidMask
389  if (!fTemp) UpdateSolidMask(false);
390 
391  // Null defaults
392  TopFace.Default();
393 
394  // newgfx: TopFace only
395  if (Con>=FullCon || Def->GrowthType)
396  if (!Def->Rotateable || (fix_r == Fix0))
397  if (Def->TopFace.Wdt>0) // Fullcon & no rotation
398  TopFace.Set(GetGraphics()->GetBitmap(Color),
399  Def->TopFace.x,Def->TopFace.y,
400  Def->TopFace.Wdt,Def->TopFace.Hgt);
401 
402  // Active face
403  UpdateActionFace();
404 }
405 
406 void C4Object::UpdateGraphics(bool fGraphicsChanged, bool fTemp)
407 {
408  // check color
409  if (!fTemp) if (!pGraphics->IsColorByOwner()) Color=0;
410  // new grafics: update face
411  if (fGraphicsChanged)
412  {
413  // Keep mesh instance if it uses the same underlying mesh
414  if(!pMeshInstance || pGraphics->Type != C4DefGraphics::TYPE_Mesh ||
415  &pMeshInstance->GetMesh() != pGraphics->Mesh)
416  {
417  // If this mesh is attached somewhere, detach it before deletion
418  if(pMeshInstance && pMeshInstance->GetAttachParent() != nullptr)
419  {
420  // TODO: If the new mesh has a bone with the same name, we could try updating...
421  StdMeshInstance::AttachedMesh* attach_parent = pMeshInstance->GetAttachParent();
422  attach_parent->Parent->DetachMesh(attach_parent->Number);
423  }
424 
425  delete pMeshInstance;
426 
427  if (pGraphics->Type == C4DefGraphics::TYPE_Mesh)
428  {
429  pMeshInstance = new StdMeshInstance(*pGraphics->Mesh, Def->GrowthType ? 1.0f : static_cast<float>(Con)/static_cast<float>(FullCon));
430  pMeshInstance->SetFaceOrderingForClrModulation(ColorMod);
431  }
432  else
433  {
434  pMeshInstance = nullptr;
435  }
436  }
437 
438  // update face - this also puts any SolidMask
439  UpdateFace(false);
440  }
441 }
442 
444 {
445  int32_t iFlipDir;
446  // We're active
447  C4PropList* pActionDef = GetAction();
448  if (pActionDef)
449  // Get flipdir value from action
450  if ((iFlipDir = pActionDef->GetPropertyInt(P_FlipDir)))
451  // Action dir is in flipdir range
452  if (Action.Dir >= iFlipDir)
453  {
454  // Calculate flipped drawing dir (from the flipdir direction going backwards)
455  Action.DrawDir = (iFlipDir - 1 - (Action.Dir - iFlipDir));
456  // Set draw transform, creating one if necessary
457  if (pDrawTransform)
458  pDrawTransform->SetFlipDir(-1);
459  else
460  pDrawTransform = new C4DrawTransform(-1);
461  // Done setting flipdir
462  return;
463  }
464  // No flipdir necessary
465  Action.DrawDir = Action.Dir;
466  // Draw transform present?
467  if (pDrawTransform)
468  {
469  // reset flip dir
470  pDrawTransform->SetFlipDir(1);
471  // if it's identity now, remove the matrix
472  if (pDrawTransform->IsIdentity())
473  {
474  delete pDrawTransform;
475  pDrawTransform=nullptr;
476  }
477  }
478 }
479 
480 void C4Object::DrawFaceImpl(C4TargetFacet &cgo, bool action, float fx, float fy, float fwdt, float fhgt, float tx, float ty, float twdt, float thgt, C4DrawTransform* transform) const
481 {
482  C4Surface* sfc;
483  switch (GetGraphics()->Type)
484  {
486  // no graphics.
487  break;
489  sfc = action ? Action.Facet.Surface : GetGraphics()->GetBitmap(Color);
490 
491  pDraw->Blit(sfc,
492  fx, fy, fwdt, fhgt,
493  cgo.Surface, tx, ty, twdt, thgt,
494  true, transform);
495  break;
497  C4Value value;
498  GetProperty(P_MeshTransformation, &value);
499  StdMeshMatrix matrix;
500  if (!C4ValueToMatrix(value, &matrix))
501  matrix = StdMeshMatrix::Identity();
502 
503  if (fix_r != Fix0)
504  {
505  // Rotation should happen around the mesh center after application of any mesh transformation
506  // So translate back by the transformed mesh center before rotation
507  auto mesh_center = pMeshInstance->GetMesh().GetBoundingBox().GetCenter();
508  mesh_center = matrix * mesh_center;
509  matrix = StdMeshMatrix::Translate(-mesh_center.x, -mesh_center.y, -mesh_center.z) * matrix;
510  matrix = StdMeshMatrix::Rotate(fixtof(fix_r) * (M_PI / 180.0f), 0.0f, 0.0f, 1.0f) * matrix;
511  matrix = StdMeshMatrix::Translate(mesh_center.x, mesh_center.y, mesh_center.z) * matrix;
512  }
513 
514  if(twdt != fwdt || thgt != fhgt)
515  {
516  // Also scale Z so that the mesh is not totally distorted and
517  // so that normals halfway keep pointing into sensible directions.
518  // We don't have a better guess so use the geometric mean for Z scale.
519  matrix = StdMeshMatrix::Scale(twdt/fwdt,thgt/fhgt,std::sqrt(twdt*thgt/(fwdt*fhgt))) * matrix;
520  }
521 
522  pDraw->SetMeshTransform(&matrix);
523 
524  pDraw->RenderMesh(*pMeshInstance, cgo.Surface, tx, ty, twdt, thgt, Color, transform);
525  pDraw->SetMeshTransform(nullptr);
526  break;
527  }
528 }
529 
530 void C4Object::DrawFace(C4TargetFacet &cgo, float offX, float offY, int32_t iPhaseX, int32_t iPhaseY) const
531 {
532  const auto swdt = float(Def->Shape.Wdt);
533  const auto shgt = float(Def->Shape.Hgt);
534  // Grow Type Display
535  auto fx = float(swdt * iPhaseX);
536  auto fy = float(shgt * iPhaseY);
537  auto fwdt = float(swdt);
538  auto fhgt = float(shgt);
539 
540  float stretch_factor = static_cast<float>(Con) / FullCon;
541  float tx = offX + Def->Shape.GetX() * stretch_factor;
542  float ty = offY + Def->Shape.GetY() * stretch_factor;
543  float twdt = swdt * stretch_factor;
544  float thgt = shgt * stretch_factor;
545 
546  // Construction Type Display
547  if (!Def->GrowthType)
548  {
549  tx = offX + Def->Shape.GetX();
550  twdt = swdt;
551 
552  fy += fhgt - thgt;
553  fhgt = thgt;
554  }
555 
556  C4DrawTransform transform;
557  bool transform_active = false;
558  if (pDrawTransform)
559  {
560  transform.SetTransformAt(*pDrawTransform, offX, offY);
561  transform_active = true;
562  }
563 
564  // Meshes aren't rotated via DrawTransform to ensure lighting is applied correctly.
565  if (GetGraphics()->Type != C4DefGraphics::TYPE_Mesh && Def->Rotateable && fix_r != Fix0)
566  {
567  if (pDrawTransform)
568  transform.Rotate(fixtof(fix_r), offX, offY);
569  else
570  transform.SetRotate(fixtof(fix_r), offX, offY);
571  transform_active = true;
572  }
573 
574  DrawFaceImpl(cgo, false, fx, fy, fwdt, fhgt, tx, ty, twdt, thgt, transform_active ? &transform : nullptr);
575 }
576 
577 void C4Object::DrawActionFace(C4TargetFacet &cgo, float offX, float offY) const
578 {
579  // This should not be called for meshes since Facet has no meaning
580  // for them. Only use DrawFace() with meshes!
581  assert(GetGraphics()->Type == C4DefGraphics::TYPE_Bitmap);
582  C4PropList* pActionDef = GetAction();
583 
584  // Regular action facet
585  const auto swdt = float(Action.Facet.Wdt);
586  const auto shgt = float(Action.Facet.Hgt);
587  int32_t iPhase = Action.Phase;
588  if (pActionDef->GetPropertyInt(P_Reverse)) iPhase = pActionDef->GetPropertyInt(P_Length) - 1 - Action.Phase;
589 
590  // Grow Type Display
591  auto fx = float(Action.Facet.X + swdt * iPhase);
592  auto fy = float(Action.Facet.Y + shgt * Action.DrawDir);
593  auto fwdt = float(swdt);
594  auto fhgt = float(shgt);
595 
596  // draw stretched towards shape center with transform
597  float stretch_factor = static_cast<float>(Con) / FullCon;
598  float tx = (Def->Shape.GetX() + Action.FacetX) * stretch_factor + offX;
599  float ty = (Def->Shape.GetY() + Action.FacetY) * stretch_factor + offY;
600  float twdt = swdt * stretch_factor;
601  float thgt = shgt * stretch_factor;
602 
603  // Construction Type Display
604  if (!Def->GrowthType)
605  {
606  // FIXME
607  if (Con != FullCon)
608  {
609  // incomplete constructions do not show actions
610  DrawFace(cgo, offX, offY);
611  return;
612  }
613  tx = Def->Shape.GetX() + Action.FacetX + offX;
614  twdt = swdt;
615  float offset_from_top = shgt * std::max(FullCon - Con, 0) / FullCon;
616  fy += offset_from_top;
617  fhgt -= offset_from_top;
618  }
619 
620  C4DrawTransform transform;
621  bool transform_active = false;
622  if (pDrawTransform)
623  {
624  transform.SetTransformAt(*pDrawTransform, offX, offY);
625  transform_active = true;
626  }
627 
628  // Meshes aren't rotated via DrawTransform to ensure lighting is applied correctly.
629  if (GetGraphics()->Type != C4DefGraphics::TYPE_Mesh && Def->Rotateable && fix_r != Fix0)
630  {
631  if (pDrawTransform)
632  transform.Rotate(fixtof(fix_r), offX, offY);
633  else
634  transform.SetRotate(fixtof(fix_r), offX, offY);
635  transform_active = true;
636  }
637 
638  DrawFaceImpl(cgo, true, fx, fy, fwdt, fhgt, tx, ty, twdt, thgt, transform_active ? &transform : nullptr);
639 }
640 
642 {
643  Mass=std::max<int32_t>((Def->Mass+OwnMass)*Con/FullCon,1);
644  if (!Def->NoMassFromContents) Mass+=Contents.Mass;
645  if (Contained)
646  {
647  Contained->Contents.MassCount();
648  Contained->UpdateMass();
649  }
650 }
651 
652 void C4Object::UpdateInMat()
653 {
654  // get new mat
655  int32_t newmat;
656  if (Contained)
657  newmat = Contained->Def->ClosedContainer ? MNone : Contained->InMat;
658  else
659  newmat = GBackMat(GetX(), GetY());
660 
661  // mat changed?
662  if (newmat != InMat)
663  {
664  Call(PSF_OnMaterialChanged,&C4AulParSet(newmat,InMat));
665  InMat = newmat;
666  }
667 }
668 
670 {
671  C4PropList* pActionDef = GetAction();
672  uint32_t dwOCFOld = OCF;
673  // Update the object character flag according to the object's current situation
674  C4Real cspeed=GetSpeed();
675 #ifdef _DEBUG
676  if (Contained && !C4PropListNumbered::CheckPropList(Contained))
677  { LogF("Warning: contained in wild object %p!", static_cast<void*>(Contained)); }
678  else if (Contained && !Contained->Status)
679  { LogF("Warning: contained in deleted object (#%d) (%s)!", Contained->Number, Contained->GetName()); }
680 #endif
681  // OCF_Normal: The OCF is never zero
682  OCF=OCF_Normal;
683  // OCF_Construct: Can be built outside
684  if (Def->Constructable && (Con<FullCon)
685  && (fix_r==Fix0) && !OnFire)
686  OCF|=OCF_Construct;
687  // OCF_Grab: Can be pushed
688  if (GetPropertyInt(P_Touchable))
689  OCF|=OCF_Grab;
690  // OCF_Carryable: Can be picked up
691  if (GetPropertyInt(P_Collectible))
692  OCF|=OCF_Carryable;
693  // OCF_OnFire: Is burning
694  if (OnFire)
695  OCF|=OCF_OnFire;
696  // OCF_Inflammable: Is not burning and is inflammable
697  if (!OnFire && GetPropertyInt(P_ContactIncinerate) > 0)
698  OCF|=OCF_Inflammable;
699  // OCF_FullCon: Is fully completed/grown
700  if (Con>=FullCon)
701  OCF|=OCF_FullCon;
702  // OCF_Rotate: Can be rotated
703  if (Def->Rotateable)
704  // Don't rotate minimum (invisible) construction sites
705  if (Con>100)
706  OCF|=OCF_Rotate;
707  // OCF_Exclusive: No action through this, no construction in front of this
708  if (Def->Exclusive)
709  OCF|=OCF_Exclusive;
710  // OCF_Entrance: Can currently be entered/activated
711  if ((Def->Entrance.Wdt>0) && (Def->Entrance.Hgt>0))
712  if ((OCF & OCF_FullCon) && ((Def->RotatedEntrance == 1) || (GetR() <= Def->RotatedEntrance)))
713  OCF|=OCF_Entrance;
714  // HitSpeeds
715  if (cspeed>=HitSpeed1) OCF|=OCF_HitSpeed1;
716  if (cspeed>=HitSpeed2) OCF|=OCF_HitSpeed2;
717  if (cspeed>=HitSpeed3) OCF|=OCF_HitSpeed3;
718  if (cspeed>=HitSpeed4) OCF|=OCF_HitSpeed4;
719  // OCF_Collection
720  if ((OCF & OCF_FullCon) || Def->IncompleteActivity)
721  if ((Def->Collection.Wdt>0) && (Def->Collection.Hgt>0))
722  if (!pActionDef || (!pActionDef->GetPropertyInt(P_ObjectDisabled)))
723  OCF|=OCF_Collection;
724  // OCF_Alive
725  if (Alive) OCF|=OCF_Alive;
726  // OCF_CrewMember
727  if (Def->CrewMember)
728  if (Alive)
729  OCF|=OCF_CrewMember;
730  // OCF_NotContained
731  if (!Contained)
732  OCF|=OCF_NotContained;
733  // OCF_InLiquid
734  if (InLiquid)
735  if (!Contained)
736  OCF|=OCF_InLiquid;
737  // OCF_InSolid
738  if (!Contained)
739  if (GBackSolid(GetX(), GetY()))
740  OCF|=OCF_InSolid;
741  // OCF_InFree
742  if (!Contained)
743  if (!GBackSemiSolid(GetX(), GetY()-1))
744  OCF|=OCF_InFree;
745  // OCF_Available
746  if (!Contained || (Contained->Def->GrabPutGet & C4D_Grab_Get) || (Contained->OCF & OCF_Entrance))
747  if (!GBackSemiSolid(GetX(), GetY()-1) || (!GBackSolid(GetX(), GetY()-1) && !GBackSemiSolid(GetX(), GetY()-8)))
748  OCF|=OCF_Available;
749  // OCF_Container
750  if ((Def->GrabPutGet & C4D_Grab_Put) || (Def->GrabPutGet & C4D_Grab_Get) || (OCF & OCF_Entrance))
751  OCF|=OCF_Container;
753  {
754  C4RCOCF rc = { dwOCFOld, OCF, false };
755  AddDbgRec(RCT_OCF, &rc, sizeof(rc));
756  }
757 }
758 
759 
761 {
762  C4PropList* pActionDef = GetAction();
763  uint32_t dwOCFOld = OCF;
764  // Update the object character flag according to the object's current situation
765  C4Real cspeed=GetSpeed();
766 #ifdef _DEBUG
767  if (Contained && !C4PropListNumbered::CheckPropList(Contained))
768  { LogF("Warning: contained in wild object %p!", static_cast<void*>(Contained)); }
769  else if (Contained && !Contained->Status)
770  { LogF("Warning: contained in deleted object %p (%s)!", static_cast<void*>(Contained), Contained->GetName()); }
771 #endif
772  // Keep the bits that only have to be updated with SetOCF (def, category, con, alive, onfire)
775  // OCF_inflammable: can catch fire and is not currently burning.
776  if (!OnFire && GetPropertyInt(P_ContactIncinerate) > 0)
777  OCF |= OCF_Inflammable;
778  // OCF_Carryable: Can be picked up
779  if (GetPropertyInt(P_Collectible))
780  OCF|=OCF_Carryable;
781  // OCF_Grab: Can be grabbed.
782  if (GetPropertyInt(P_Touchable))
783  OCF |= OCF_Grab;
784  // OCF_Construct: Can be built outside
785  if (Def->Constructable && (Con<FullCon)
786  && (fix_r == Fix0) && !OnFire)
787  OCF|=OCF_Construct;
788  // OCF_Entrance: Can currently be entered/activated
789  if ((Def->Entrance.Wdt>0) && (Def->Entrance.Hgt>0))
790  if ((OCF & OCF_FullCon) && ((Def->RotatedEntrance == 1) || (GetR() <= Def->RotatedEntrance)))
791  OCF|=OCF_Entrance;
792  // HitSpeeds
793  if (cspeed>=HitSpeed1) OCF|=OCF_HitSpeed1;
794  if (cspeed>=HitSpeed2) OCF|=OCF_HitSpeed2;
795  if (cspeed>=HitSpeed3) OCF|=OCF_HitSpeed3;
796  if (cspeed>=HitSpeed4) OCF|=OCF_HitSpeed4;
797  // OCF_Collection
798  if ((OCF & OCF_FullCon) || Def->IncompleteActivity)
799  if ((Def->Collection.Wdt>0) && (Def->Collection.Hgt>0))
800  if (!pActionDef || (!pActionDef->GetPropertyInt(P_ObjectDisabled)))
801  OCF|=OCF_Collection;
802  // OCF_NotContained
803  if (!Contained)
804  OCF|=OCF_NotContained;
805  // OCF_InLiquid
806  if (InLiquid)
807  if (!Contained)
808  OCF|=OCF_InLiquid;
809  // OCF_InSolid
810  if (!Contained)
811  if (GBackSolid(GetX(), GetY()))
812  OCF|=OCF_InSolid;
813  // OCF_InFree
814  if (!Contained)
815  if (!GBackSemiSolid(GetX(), GetY()-1))
816  OCF|=OCF_InFree;
817  // OCF_Available
818  if (!Contained || (Contained->Def->GrabPutGet & C4D_Grab_Get) || (Contained->OCF & OCF_Entrance))
819  if (!GBackSemiSolid(GetX(), GetY()-1) || (!GBackSolid(GetX(), GetY()-1) && !GBackSemiSolid(GetX(), GetY()-8)))
820  OCF|=OCF_Available;
821  // OCF_Container
822  if ((Def->GrabPutGet & C4D_Grab_Put) || (Def->GrabPutGet & C4D_Grab_Get) || (OCF & OCF_Entrance))
823  OCF|=OCF_Container;
825  {
826  C4RCOCF rc = { dwOCFOld, OCF, true };
827  AddDbgRec(RCT_OCF, &rc, sizeof(rc));
828  }
829 #ifdef _DEBUG
831  uint32_t updateOCF = OCF;
832  SetOCF();
833  assert (updateOCF == OCF);
835 #endif
836 }
837 
838 
840 {
841  // Breathing
842  if (!::Game.iTick5)
843  if (Alive && !Def->NoBreath)
844  {
845  // Supply check
846  bool Breathe=false;
847  // Forcefields are breathable.
848  if (GBackMat(GetX(), GetY()+Shape.GetY()/2)==MVehic)
849  { Breathe=true; }
850  else if (GetPropertyInt(P_BreatheWater))
851  { if (GBackMat(GetX(), GetY())==MWater) Breathe=true; }
852  else
853  { if (!GBackSemiSolid(GetX(), GetY()+Shape.GetY()/2)) Breathe=true; }
854  if (Contained) Breathe=true;
855  // No supply
856  if (!Breathe)
857  {
858  // Reduce breath, then energy, bubble
859  if (Breath > 0) DoBreath(-5);
860  else DoEnergy(-1,false,C4FxCall_EngAsphyxiation, NO_OWNER);
861  }
862  // Supply
863  else
864  {
865  // Take breath
866  int32_t takebreath = GetPropertyInt(P_MaxBreath) - Breath;
867  if (takebreath > 0) DoBreath(takebreath);
868  }
869  }
870 
871  // Corrosion energy loss
872  if (!::Game.iTick10)
873  if (Alive)
874  if (InMat!=MNone)
875  if (::MaterialMap.Map[InMat].Corrosive)
876  if (!GetPropertyInt(P_CorrosionResist))
877  DoEnergy(-::MaterialMap.Map[InMat].Corrosive/15,false,C4FxCall_EngCorrosion, NO_OWNER);
878 
879  // InMat incineration
880  if (!::Game.iTick10)
881  if (InMat!=MNone)
882  if (::MaterialMap.Map[InMat].Incendiary)
883  if (GetPropertyInt(P_ContactIncinerate) > 0 || GetPropertyBool(P_MaterialIncinerate) > 0)
884  {
886  }
887 
888  // birthday
889  if (!::Game.iTick255)
890  if (Alive)
891  if (Info)
892  {
893  int32_t iPlayingTime = Info->TotalPlayingTime + (Game.Time - Info->InActionTime);
894 
895  int32_t iNewAge = iPlayingTime / 3600 / 5;
896 
897  if (Info->Age != iNewAge && Info->Age)
898  {
899  // message
900  GameMsgObject(FormatString(LoadResStr("IDS_OBJ_BIRTHDAY"),GetName (), Info->TotalPlayingTime / 3600 / 5).getData(),this);
901  StartSoundEffect("UI::Trumpet",false,100,this);
902  }
903 
904  Info->Age = iNewAge;
905 
906 
907  }
908 
909  return true;
910 }
911 
913 {
914  if (Config.General.DebugRec)
915  {
916  // record debug
917  C4RCExecObj rc;
918  rc.Number=Number;
919  rc.fx=fix_x;
920  rc.fy=fix_y;
921  rc.fr=fix_r;
922  AddDbgRec(RCT_ExecObj, &rc, sizeof(rc));
923  }
924  // OCF
925  UpdateOCF();
926  // Command
927  ExecuteCommand();
928  // Action
929  // need not check status, because dead objects have lost their action
930  ExecAction();
931  // commands and actions are likely to have removed the object, and movement
932  // *must not* be executed for dead objects (SolidMask-errors)
933  if (!Status) return;
934  // Movement
935  ExecMovement();
936  if (!Status) return;
937  // effects
938  if (pEffects)
939  {
940  C4Effect::Execute(&pEffects);
941  if (!Status) return;
942  }
943  // Life
944  ExecLife();
945  // Animation. If the mesh is attached, then don't execute animation here but let the parent object do it to make sure it is only executed once a frame.
946  if (pMeshInstance && !pMeshInstance->GetAttachParent())
947  pMeshInstance->ExecuteAnimation(1.0f/37.0f /* play smoothly at 37 FPS */);
948  // Menu
949  if (Menu) Menu->Execute();
950 }
951 
952 bool C4Object::At(int32_t ctx, int32_t cty) const
953 {
954  if (Status) if (!Contained) if (Def)
955  if (Inside<int32_t>(cty - (GetY() + Shape.GetY() - addtop()), 0, Shape.Hgt - 1 + addtop()))
956  if (Inside<int32_t>(ctx - (GetX() + Shape.GetX()), 0, Shape.Wdt - 1))
957  return true;
958  return false;
959 }
960 
961 bool C4Object::At(int32_t ctx, int32_t cty, DWORD &ocf) const
962 {
963  if (Status) if (!Contained) if (Def)
964  if (OCF & ocf)
965  if (Inside<int32_t>(cty - (GetY() + Shape.GetY() - addtop()), 0, Shape.Hgt - 1 + addtop()))
966  if (Inside<int32_t>(ctx - (GetX() + Shape.GetX()), 0, Shape.Wdt - 1))
967  {
968  // Set ocf return value
969  GetOCFForPos(ctx, cty, ocf);
970  return true;
971  }
972  return false;
973 }
974 
975 void C4Object::GetOCFForPos(int32_t ctx, int32_t cty, DWORD &ocf) const
976 {
977  DWORD rocf=OCF;
978  // Verify entrance area OCF return
979  if (rocf & OCF_Entrance)
980  if (!Inside<int32_t>(cty - (GetY() + Def->Entrance.y), 0, Def->Entrance.Hgt - 1)
981  || !Inside<int32_t>(ctx - (GetX() + Def->Entrance.x), 0, Def->Entrance.Wdt - 1))
982  rocf &= (~OCF_Entrance);
983  // Verify collection area OCF return
984  if (rocf & OCF_Collection)
985  if (!Inside<int32_t>(cty - (GetY() + Def->Collection.y), 0, Def->Collection.Hgt - 1)
986  || !Inside<int32_t>(ctx - (GetX() + Def->Collection.x), 0, Def->Collection.Wdt - 1))
987  rocf &= (~OCF_Collection);
988  ocf=rocf;
989 }
990 
991 void C4Object::AssignDeath(bool fForced)
992 {
993  C4Object *thing;
994  // Alive objects only
995  if (!Alive) return;
996  // clear all effects
997  // do not delete effects afterwards, because they might have denied removal
998  // set alive-flag before, so objects know what's up
999  // and prevent recursive death-calls this way
1000  // get death causing player before doing effect calls, because those might meddle around with the flags
1001  int32_t iDeathCausingPlayer = LastEnergyLossCausePlayer;
1002  Alive=false;
1003  if (pEffects) pEffects->ClearAll(C4FxCall_RemoveDeath);
1004  // if the object is alive again, abort here if the kill is not forced
1005  if (Alive && !fForced) return;
1006  // Action
1007  SetActionByName("Dead");
1008  // Values
1009  Alive=false;
1010  ClearCommands();
1011  C4ObjectInfo * pInfo = Info;
1012  if (Info)
1013  {
1014  Info->HasDied=true;
1015  ++Info->DeathCount;
1016  Info->Retire();
1017  }
1018  // Remove from crew/cursor/view
1019  C4Player *pPlr = ::Players.Get(Owner);
1020  if (pPlr) pPlr->ClearPointers(this, true);
1021  // Remove from light sources
1022  SetLightRange(0,0);
1023  // Engine script call
1024  C4AulParSet pars(iDeathCausingPlayer);
1025  Call(PSF_Death, &pars);
1026  // Lose contents
1027  while ((thing=Contents.GetObject())) thing->Exit(thing->GetX(),thing->GetY());
1028  // Update OCF. Done here because previously it would have been done in the next frame
1029  // Whats worse: Having the OCF change because of some unrelated script-call like
1030  // SetCategory, or slightly breaking compatibility?
1031  SetOCF();
1032  // Engine broadcast: relaunch player (in CR, this was called from clonk script.
1033  // Now, it is done for every crew member)
1034  if(pPlr)
1035  if(!pPlr->Crew.ObjectCount())
1037  &C4AulParSet(Owner, iDeathCausingPlayer, Status ? this : nullptr));
1038  if (pInfo)
1039  pInfo->HasDied = false;
1040 }
1041 
1043 {
1044  // Get new definition
1045  C4Def *pDef=C4Id2Def(idNew);
1046  if (!pDef) return false;
1047  // Containment storage
1048  C4Object *pContainer=Contained;
1049  // Exit container (no Ejection/Departure)
1050  if (Contained) Exit(0,0,0,Fix0,Fix0,Fix0,false);
1051  // Pre change resets
1052  SetAction(nullptr);
1053  ResetProperty(&Strings.P[P_Action]); // Enforce ActIdle because SetAction may have failed due to NoOtherAction
1054  SetDir(0); // will drop any outdated flipdir
1055  if (pSolidMaskData) { delete pSolidMaskData; pSolidMaskData=nullptr; }
1056  Def->Count--;
1057  // Def change
1058  Def=pDef;
1059  SetProperty(P_Prototype, C4VPropList(pDef));
1060  id=pDef->id;
1061  Def->Count++;
1062  // new def: Needs to be resorted
1063  Unsorted=true;
1064  // graphics change
1065  pGraphics = &pDef->Graphics;
1066  // blit mode adjustment
1067  if (!(BlitMode & C4GFXBLIT_CUSTOM)) BlitMode = Def->BlitMode;
1068  // an object may have newly become an ColorByOwner-object
1069  // if it had been ColorByOwner, but is not now, this will be caught in UpdateGraphics()
1070  if (!Color && ValidPlr(Owner))
1071  Color=::Players.Get(Owner)->ColorDw;
1072  if (!Def->Rotateable) { fix_r=rdir=Fix0; }
1073  // Reset solid mask
1074  SolidMask=Def->SolidMask;
1075  HalfVehicleSolidMask=false;
1076  // Post change updates
1077  UpdateGraphics(true);
1078  UpdateMass();
1079  UpdateFace(true);
1080  SetOCF();
1081  // Any effect callbacks to this object might need to reinitialize their target functions
1082  // This is ugly, because every effect there is must be updated...
1087  for (C4Object *obj : Objects)
1088  if (obj->pEffects) obj->pEffects->OnObjectChangedDef(this);
1089  // Containment (no Entrance)
1090  if (pContainer) Enter(pContainer,false);
1091  // Done
1092  return true;
1093 }
1094 
1095 void C4Object::DoDamage(int32_t iChange, int32_t iCausedBy, int32_t iCause)
1096 {
1097  // non-living: ask effects first
1098  if (pEffects && !Alive)
1099  {
1100  pEffects->DoDamage(iChange, iCause, iCausedBy);
1101  if (!iChange) return;
1102  }
1103  // Change value
1104  Damage = std::max<int32_t>( Damage+iChange, 0 );
1105  // Engine script call
1106  Call(PSF_Damage,&C4AulParSet(iChange, iCause, iCausedBy));
1107 }
1108 
1109 void C4Object::DoEnergy(int32_t iChange, bool fExact, int32_t iCause, int32_t iCausedByPlr)
1110 {
1111  if (!fExact)
1112  {
1113  // Clamp range of change to prevent integer overflow errors
1114  // Do not clamp directly to (0...MaxEnergy)-current_energy, because
1115  // the change value calculated here may be reduced by effect callbacks
1116  int32_t scale = C4MaxPhysical / 100; // iChange 100% = Physical 100000
1117  iChange = Clamp<int32_t>(iChange, std::numeric_limits<int32_t>::min()/scale, std::numeric_limits<int32_t>::max()/scale)*scale;
1118  }
1119  // Was zero?
1120  bool fWasZero=(Energy==0);
1121  // Mark last damage causing player to trace kills
1122  if (iChange < 0) UpdatLastEnergyLossCause(iCausedByPlr);
1123  // Living things: ask effects for change first
1124  if (pEffects && Alive)
1125  pEffects->DoDamage(iChange, iCause, iCausedByPlr);
1126  // Do change
1127  iChange = Clamp<int32_t>(iChange, -Energy, GetPropertyInt(P_MaxEnergy) - Energy);
1128  Energy += iChange;
1129  // call to object
1130  Call(PSF_EnergyChange,&C4AulParSet(iChange, iCause, iCausedByPlr));
1131  // Alive and energy reduced to zero: death
1132  if (Alive) if (Energy==0) if (!fWasZero) AssignDeath(false);
1133 }
1134 
1135 void C4Object::UpdatLastEnergyLossCause(int32_t iNewCausePlr)
1136 {
1137  // Mark last damage causing player to trace kills
1138  // do not regard self-administered damage if there was a previous damage causing player, because that would steal kills
1139  // if people tumble themselves via stop-stop-(left/right)-throw while falling into teh abyss
1140  if (iNewCausePlr != Controller || LastEnergyLossCausePlayer < 0)
1141  {
1142  LastEnergyLossCausePlayer = iNewCausePlr;
1143  }
1144 }
1145 
1146 void C4Object::DoBreath(int32_t iChange)
1147 {
1148  // Do change
1149  iChange = Clamp<int32_t>(iChange, -Breath, GetPropertyInt(P_MaxBreath) - Breath);
1150  Breath += iChange;
1151  // call to object
1152  Call(PSF_BreathChange,&C4AulParSet(iChange));
1153 }
1154 
1155 void C4Object::DoCon(int32_t iChange, bool grow_from_center)
1156 {
1157  C4Real strgt_con_b = fix_y + Shape.GetBottom();
1158  bool fWasFull = (Con>=FullCon);
1159  int32_t old_con = Con;
1160 
1161  // Change con
1162  if (Def->Oversize)
1163  Con=std::max<int32_t>(Con+iChange,0);
1164  else
1165  Con=Clamp<int32_t>(Con+iChange,0,FullCon);
1166 
1167  // Update OCF
1168  SetOCF();
1169 
1170  // Mass
1171  UpdateMass();
1172 
1173  // shape and position
1174  UpdateShape();
1175  // make the bottom-most vertex stay in place
1176  if (!grow_from_center)
1177  {
1178  fix_y = strgt_con_b - Shape.GetBottom();
1179  }
1180  // Face (except for the shape)
1181  UpdateFace(false);
1182 
1183  // Do a callback on completion change.
1184  if (iChange != 0)
1185  Call(PSF_OnCompletionChange, &C4AulParSet(old_con, Con));
1186 
1187  // Unfullcon
1188  if (fWasFull && (Con<FullCon))
1189  {
1190  // Lose contents
1191  if (!Def->IncompleteActivity)
1192  {
1193  C4Object *cobj;
1194  while ((cobj=Contents.GetObject()))
1195  if (Contained) cobj->Enter(Contained);
1196  else cobj->Exit(cobj->GetX(),cobj->GetY());
1197  SetAction(nullptr);
1198  }
1199  }
1200 
1201  // Completion
1202  if (!fWasFull && (Con>=FullCon))
1204 
1205  // Con Zero Removal
1206  if (Con<=0)
1207  AssignRemoval();
1208  // Mesh Graphics Update
1209  else if(pMeshInstance)
1210  pMeshInstance->SetCompletion(Def->GrowthType ? 1.0f : static_cast<float>(Con)/static_cast<float>(FullCon));
1211 }
1212 
1213 void C4Object::DoExperience(int32_t change)
1214 {
1215  const int32_t MaxExperience = 100000000;
1216 
1217  if (!Info) return;
1218 
1219  Info->Experience=Clamp<int32_t>(Info->Experience+change,0,MaxExperience);
1220 
1221  // Promotion check
1222  if (Info->Experience<MaxExperience)
1223  if (Info->Experience>=::DefaultRanks.Experience(Info->Rank+1))
1224  Promote(Info->Rank+1, false, false);
1225 }
1226 
1227 bool C4Object::Exit(int32_t iX, int32_t iY, int32_t iR, C4Real iXDir, C4Real iYDir, C4Real iRDir, bool fCalls)
1228 {
1229  // 1. Exit the current container.
1230  // 2. Update Contents of container object and set Contained to nullptr.
1231  // 3. Set offset position/motion if desired.
1232  // 4. Call Ejection for container and Departure for object.
1233 
1234  // Not contained
1235  C4Object *pContainer=Contained;
1236  if (!pContainer) return false;
1237  // Remove object from container
1238  pContainer->Contents.Remove(this);
1239  pContainer->UpdateMass();
1240  pContainer->SetOCF();
1241  // No container
1242  Contained=nullptr;
1243  // Position/motion
1244  fix_x=itofix(iX); fix_y=itofix(iY);
1245  fix_r=itofix(iR);
1246  BoundsCheck(fix_x, fix_y);
1247  xdir=iXDir; ydir=iYDir; rdir=iRDir;
1248  // Misc updates
1249  Mobile=true;
1250  InLiquid=false;
1251  CloseMenu(true);
1252  UpdateFace(true);
1253  SetOCF();
1254  // Object list callback (before script callbacks, because script callbacks may enter again)
1255  ObjectListChangeListener.OnObjectContainerChanged(this, pContainer, nullptr);
1256  // Engine calls
1257  if (fCalls) pContainer->Call(PSF_Ejection,&C4AulParSet(this));
1258  if (fCalls) Call(PSF_Departure,&C4AulParSet(pContainer));
1259  // Success (if the obj wasn't "re-entered" by script)
1260  return !Contained;
1261 }
1262 
1263 bool C4Object::Enter(C4Object *pTarget, bool fCalls, bool fCopyMotion, bool *pfRejectCollect)
1264 {
1265  // 0. Query entrance and collection
1266  // 1. Exit if contained.
1267  // 2. Set new container.
1268  // 3. Update Contents and mass of the new container.
1269  // 4. Call collection for container
1270  // 5. Call entrance for object.
1271 
1272  // No valid target or target is self
1273  if (!pTarget || (pTarget==this)) return false;
1274  // check if entrance is allowed
1275  if (!! Call(PSF_RejectEntrance, &C4AulParSet(pTarget))) return false;
1276  // check if we end up in an endless container-recursion
1277  for (C4Object *pCnt=pTarget->Contained; pCnt; pCnt=pCnt->Contained)
1278  if (pCnt==this) return false;
1279  // Check RejectCollect, if desired
1280  if (pfRejectCollect)
1281  {
1282  if (!!pTarget->Call(PSF_RejectCollection,&C4AulParSet(Def, this)))
1283  {
1284  *pfRejectCollect = true;
1285  return false;
1286  }
1287  *pfRejectCollect = false;
1288  }
1289  // Exit if contained
1290  if (Contained) if (!Exit(GetX(),GetY())) return false;
1291  if (Contained || !Status || !pTarget->Status) return false;
1292  // Failsafe updates
1293  if (Menu)
1294  {
1295  CloseMenu(true);
1296  // CloseMenu might do bad stuff
1297  if (Contained || !Status || !pTarget->Status) return false;
1298  }
1299  SetOCF();
1300  // Set container
1301  Contained=pTarget;
1302  // Enter
1303  if (!Contained->Contents.Add(this, C4ObjectList::stContents))
1304  {
1305  Contained=nullptr;
1306  return false;
1307  }
1308  // Assume that the new container controls this object, if it cannot control itself (i.e.: Alive)
1309  // So it can be traced back who caused the damage, if a projectile hits its target
1310  if (!Alive)
1311  Controller = pTarget->Controller;
1312  // Misc updates
1313  // motion must be copied immediately, so the position will be correct when OCF is set, and
1314  // OCF_Available will be set for newly bought items, even if 50/50 is solid in the landscape
1315  // however, the motion must be preserved sometimes to keep flags like OCF_HitSpeed upon collection
1316  if (fCopyMotion)
1317  {
1318  // remove any solidmask before copying the motion...
1319  UpdateSolidMask(false);
1320  CopyMotion(Contained);
1321  }
1322  SetOCF();
1323  UpdateFace(true);
1324  // Update container
1325  Contained->UpdateMass();
1326  Contained->SetOCF();
1327  // Object list callback (before script callbacks, because script callbacks may exit again)
1328  ObjectListChangeListener.OnObjectContainerChanged(this, nullptr, Contained);
1329  // Collection call
1330  if (fCalls) pTarget->Call(PSF_Collection2,&C4AulParSet(this));
1331  if (!Contained || !Contained->Status || !pTarget->Status) return true;
1332  // Entrance call
1333  if (fCalls) Call(PSF_Entrance,&C4AulParSet(Contained));
1334  if (!Contained || !Contained->Status || !pTarget->Status) return true;
1335  // Success
1336  return true;
1337 }
1338 
1339 void C4Object::Fling(C4Real txdir, C4Real tydir, bool fAddSpeed)
1340 {
1341  if (fAddSpeed) { txdir+=xdir/2; tydir+=ydir/2; }
1342  if (!ObjectActionTumble(this,(txdir<0),txdir,tydir))
1343  if (!ObjectActionJump(this,txdir,tydir,false))
1344  {
1345  xdir=txdir; ydir=tydir;
1346  Mobile=true;
1347  Action.t_attach&=~CNAT_Bottom;
1348  }
1349 }
1350 
1351 bool C4Object::ActivateEntrance(int32_t by_plr, C4Object *by_obj)
1352 {
1353 
1354  // Try entrance activation
1355  if (OCF & OCF_Entrance)
1356  if (!! Call(PSF_ActivateEntrance,&C4AulParSet(by_obj)))
1357  return true;
1358  // Failure
1359  return false;
1360 }
1361 
1362 bool C4Object::Push(C4Real txdir, C4Real dforce, bool fStraighten)
1363 {
1364  // Valid check
1365  if (!Status || !Def || Contained || !(OCF & OCF_Grab)) return false;
1366  // Grabbing okay, no pushing
1367  if (GetPropertyInt(P_Touchable)==2) return true;
1368  // Mobilization check (pre-mobilization zero)
1369  if (!Mobile)
1370  { xdir=ydir=Fix0; }
1371  // General pushing force vs. object mass
1372  dforce=dforce*100/Mass;
1373  // Set dir
1374  if (xdir<0) SetDir(DIR_Left);
1375  if (xdir>0) SetDir(DIR_Right);
1376  // Work towards txdir
1377  if (Abs(xdir-txdir)<=dforce) // Close-enough-set
1378  { xdir=txdir; }
1379  else // Work towards
1380  {
1381  if (xdir<txdir) xdir+=dforce;
1382  if (xdir>txdir) xdir-=dforce;
1383  }
1384  // Straighten
1385  if (fStraighten)
1386  {
1387  if (Inside<int32_t>(GetR(),-StableRange,+StableRange))
1388  {
1389  rdir=0; // cheap way out
1390  }
1391  else
1392  {
1393  if (fix_r > Fix0) { if (rdir>-RotateAccel) rdir-=dforce; }
1394  else { if (rdir<+RotateAccel) rdir+=dforce; }
1395  }
1396  }
1397 
1398  // Mobilization check
1399  if (!!xdir || !!ydir || !!rdir) Mobile=true;
1400 
1401  // Stuck check
1402  if (!::Game.iTick35) if (txdir) if (!Def->NoHorizontalMove)
1403  if (ContactCheck(GetX(), GetY())) // Resets t_contact
1404  {
1405  GameMsgObjectError(FormatString(LoadResStr("IDS_OBJ_STUCK"),GetName()).getData(),this);
1406  Call(PSF_Stuck);
1407  }
1408 
1409  return true;
1410 }
1411 
1412 bool C4Object::Lift(C4Real tydir, C4Real dforce)
1413 {
1414  // Valid check
1415  if (!Status || !Def || Contained) return false;
1416  // Mobilization check
1417  if (!Mobile)
1418  { xdir=ydir=Fix0; Mobile=true; }
1419  // General pushing force vs. object mass
1420  dforce=dforce*100/Mass;
1421  // If close enough, set tydir
1422  if (Abs(tydir-ydir)<=Abs(dforce))
1423  ydir=tydir;
1424  else // Work towards tydir
1425  {
1426  if (ydir<tydir) ydir+=dforce;
1427  if (ydir>tydir) ydir-=dforce;
1428  }
1429  // Stuck check
1430  if (tydir != -GravAccel)
1431  if (ContactCheck(GetX(), GetY())) // Resets t_contact
1432  {
1433  GameMsgObjectError(FormatString(LoadResStr("IDS_OBJ_STUCK"),GetName()).getData(),this);
1434  Call(PSF_Stuck);
1435  }
1436  return true;
1437 }
1438 
1440 {
1441  C4Object *nobj;
1442  if (!(nobj=Game.CreateObject(PropList,this,Owner))) return nullptr;
1443  if (!nobj->Enter(this)) { nobj->AssignRemoval(); return nullptr; }
1444  return nobj;
1445 }
1446 
1448 {
1449  int32_t cnt,cnt2;
1450  for (cnt=0; idlist.GetID(cnt); cnt++)
1451  for (cnt2=0; cnt2<idlist.GetCount(cnt); cnt2++)
1452  if (!CreateContents(C4Id2Def(idlist.GetID(cnt))))
1453  return false;
1454  return true;
1455 }
1456 
1457 static void DrawMenuSymbol(int32_t iMenu, C4Facet &cgo, int32_t iOwner)
1458 {
1459  C4Facet ccgo;
1460 
1461  DWORD dwColor=0;
1462  if (ValidPlr(iOwner)) dwColor=::Players.Get(iOwner)->ColorDw;
1463 
1464  switch (iMenu)
1465  {
1466  case C4MN_Buy:
1467  ::GraphicsResource.fctFlagClr.DrawClr(ccgo = cgo.GetFraction(75, 75), true, dwColor);
1469  ::GraphicsResource.fctArrow.Draw(ccgo = cgo.GetFraction(70, 70, C4FCT_Right, C4FCT_Center), false, 0);
1470  break;
1471  case C4MN_Sell:
1472  ::GraphicsResource.fctFlagClr.DrawClr(ccgo = cgo.GetFraction(75, 75), true, dwColor);
1474  ::GraphicsResource.fctArrow.Draw(ccgo = cgo.GetFraction(70, 70, C4FCT_Right, C4FCT_Center), false, 1);
1475  break;
1476  }
1477 }
1478 
1479 bool C4Object::ActivateMenu(int32_t iMenu, int32_t iMenuSelect,
1480  int32_t iMenuData, int32_t iMenuPosition,
1481  C4Object *pTarget)
1482 {
1483  // Variables
1484  C4FacetSurface fctSymbol;
1485  C4IDList ListItems;
1486  // Close any other menu
1487  if (Menu && Menu->IsActive()) if (!Menu->TryClose(true, false)) return false;
1488  // Create menu
1489  if (!Menu) Menu = new C4ObjectMenu; else Menu->ClearItems();
1490  // Open menu
1491  switch (iMenu)
1492  {
1493  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1494  case C4MN_Activate:
1495  // No target specified: use own container as target
1496  if (!pTarget) if (!(pTarget=Contained)) break;
1497  // Opening contents menu blocked by RejectContents
1498  if (!!pTarget->Call(PSF_RejectContents)) return false;
1499  // Create symbol
1500  fctSymbol.Create(C4SymbolSize,C4SymbolSize);
1501  pTarget->Def->Draw(fctSymbol,false,pTarget->Color,pTarget);
1502  // Init
1503  Menu->Init(fctSymbol,FormatString(LoadResStr("IDS_OBJ_EMPTY"),pTarget->GetName()).getData(),this,C4MN_Extra_None,0,iMenu);
1504  Menu->SetPermanent(true);
1505  Menu->SetRefillObject(pTarget);
1506  // Success
1507  return true;
1508  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1509  case C4MN_Buy:
1510  // No target specified: container is base
1511  if (!pTarget) if (!(pTarget=Contained)) break;
1512  // Create symbol
1513  fctSymbol.Create(C4SymbolSize,C4SymbolSize);
1514  DrawMenuSymbol(C4MN_Buy, fctSymbol, pTarget->Owner);
1515  // Init menu
1516  Menu->Init(fctSymbol,LoadResStr("IDS_PLR_NOBUY"),this,C4MN_Extra_Value,0,iMenu);
1517  Menu->SetPermanent(true);
1518  Menu->SetRefillObject(pTarget);
1519  // Success
1520  return true;
1521  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1522  case C4MN_Sell:
1523  // No target specified: container is base
1524  if (!pTarget) if (!(pTarget=Contained)) break;
1525  // Create symbol & init
1526  fctSymbol.Create(C4SymbolSize,C4SymbolSize);
1527  DrawMenuSymbol(C4MN_Sell, fctSymbol, pTarget->Owner);
1528  Menu->Init(fctSymbol,FormatString(LoadResStr("IDS_OBJ_EMPTY"),pTarget->GetName()).getData(),this,C4MN_Extra_Value,0,iMenu);
1529  Menu->SetPermanent(true);
1530  Menu->SetRefillObject(pTarget);
1531  // Success
1532  return true;
1533  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1534  case C4MN_Get:
1535  case C4MN_Contents:
1536  // No target specified
1537  if (!pTarget) break;
1538  // Opening contents menu blocked by RejectContents
1539  if (!!pTarget->Call(PSF_RejectContents)) return false;
1540  // Create symbol & init
1541  fctSymbol.Create(C4SymbolSize,C4SymbolSize);
1542  pTarget->Def->Draw(fctSymbol,false,pTarget->Color,pTarget);
1543  Menu->Init(fctSymbol,FormatString(LoadResStr("IDS_OBJ_EMPTY"),pTarget->GetName()).getData(),this,C4MN_Extra_None,0,iMenu);
1544  Menu->SetPermanent(true);
1545  Menu->SetRefillObject(pTarget);
1546  // Success
1547  return true;
1548  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1549  case C4MN_Info:
1550  // Target by parameter
1551  if (!pTarget) break;
1552  // Create symbol & init menu
1553  fctSymbol.Create(C4SymbolSize, C4SymbolSize); GfxR->fctOKCancel.Draw(fctSymbol,true,0,1);
1554  Menu->Init(fctSymbol, pTarget->GetName(), this, C4MN_Extra_None, 0, iMenu, C4MN_Style_Info);
1555  Menu->SetPermanent(true);
1556  Menu->SetAlignment(C4MN_Align_Free);
1557  C4Viewport *pViewport = ::Viewports.GetViewport(Controller); // Hackhackhack!!!
1558  if (pViewport) Menu->SetLocation((pTarget->GetX() + pTarget->Shape.GetX() + pTarget->Shape.Wdt + 10 - pViewport->GetViewX()) * pViewport->GetZoom(),
1559  (pTarget->GetY() + pTarget->Shape.GetY() - pViewport->GetViewY()) * pViewport->GetZoom());
1560  // Add info item
1561  fctSymbol.Create(C4PictureSize, C4PictureSize); pTarget->Def->Draw(fctSymbol, false, pTarget->Color, pTarget);
1562  Menu->Add(pTarget->GetName(), fctSymbol, "", C4MN_Item_NoCount, nullptr, pTarget->GetInfoString().getData());
1563  fctSymbol.Default();
1564  // Success
1565  return true;
1566  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1567  }
1568  // Invalid menu identification
1569  CloseMenu(true);
1570  return false;
1571 }
1572 
1573 bool C4Object::CloseMenu(bool fForce)
1574 {
1575  if (Menu)
1576  {
1577  if (Menu->IsActive()) if (!Menu->TryClose(fForce, false)) return false;
1578  if (!Menu->IsCloseQuerying()) { delete Menu; Menu=nullptr; } // protect menu deletion from recursive menu operation calls
1579  }
1580  return true;
1581 }
1582 
1583 BYTE C4Object::GetArea(int32_t &aX, int32_t &aY, int32_t &aWdt, int32_t &aHgt) const
1584 {
1585  if (!Status || !Def) return 0;
1586  aX = GetX() + Shape.GetX(); aY = GetY() + Shape.GetY();
1587  aWdt=Shape.Wdt; aHgt=Shape.Hgt;
1588  return 1;
1589 }
1590 
1591 BYTE C4Object::GetEntranceArea(int32_t &aX, int32_t &aY, int32_t &aWdt, int32_t &aHgt) const
1592 {
1593  if (!Status || !Def) return 0;
1594  // Return actual entrance
1595  if (OCF & OCF_Entrance)
1596  {
1597  aX=GetX() + Def->Entrance.x;
1598  aY=GetY() + Def->Entrance.y;
1599  aWdt=Def->Entrance.Wdt;
1600  aHgt=Def->Entrance.Hgt;
1601  }
1602  // Return object center
1603  else
1604  {
1605  aX=GetX(); aY=GetY();
1606  aWdt=0; aHgt=0;
1607  }
1608  // Done
1609  return 1;
1610 }
1611 
1613 {
1614  rxdir=rydir=0;
1615  if (!Status || !Def) return 0;
1616  rxdir=xdir; rydir=ydir;
1617  return 1;
1618 }
1619 
1621 {
1622  C4Real cobjspd=Fix0;
1623  if (xdir<0) cobjspd-=xdir; else cobjspd+=xdir;
1624  if (ydir<0) cobjspd-=ydir; else cobjspd+=ydir;
1625  return cobjspd;
1626 }
1627 
1629 {
1630  StdStrBuf Output;
1631  // Type
1632  Output.AppendFormat(LoadResStr("IDS_CNS_TYPE"),GetName(),Def->id.ToString());
1633  // Owner
1634  if (ValidPlr(Owner))
1635  {
1636  Output.Append(LineFeed);
1637  Output.AppendFormat(LoadResStr("IDS_CNS_OWNER"),::Players.Get(Owner)->GetName());
1638  }
1639  // Contents
1640  if (Contents.ObjectCount())
1641  {
1642  Output.Append(LineFeed);
1643  Output.Append(LoadResStr("IDS_CNS_CONTENTS"));
1644  Output.Append(Contents.GetNameList(::Definitions));
1645  }
1646  // Action
1647  if (GetAction())
1648  {
1649  Output.Append(LineFeed);
1650  Output.Append(LoadResStr("IDS_CNS_ACTION"));
1651  Output.Append(GetAction()->GetName());
1652  }
1653  // Properties
1654  Output.Append(LineFeed);
1655  Output.Append(LoadResStr("IDS_CNS_PROPERTIES"));
1656  Output.Append(LineFeed " ");
1657  AppendDataString(&Output, LineFeed " ");
1658  // Effects
1659  if (pEffects)
1660  {
1661  Output.Append(LineFeed);
1662  Output.Append(LoadResStr("IDS_CNS_EFFECTS"));
1663  Output.Append(": ");
1664  }
1665  for (C4Effect *pEffect = pEffects; pEffect; pEffect = pEffect->pNext)
1666  {
1667  Output.Append(LineFeed);
1668  // Effect name
1669  Output.AppendFormat(" %s: Priority %d, Interval %d", pEffect->GetName(), pEffect->iPriority, pEffect->iInterval);
1670  }
1671 
1672  StdStrBuf Output2;
1673  C4ValueNumbers numbers;
1674  DecompileToBuf_Log<StdCompilerINIWrite>(mkNamingAdapt(mkInsertAdapt(mkParAdapt(*this, &numbers),
1675  mkNamingAdapt(numbers, "Values"), false),
1676  "Object"), &Output2, "C4Object::GetDataString");
1677  Output.Append(LineFeed);
1678  Output.Append(Output2);
1679  return Output;
1680 }
1681 
1682 void C4Object::SetName(const char * NewName)
1683 {
1684  if (!NewName && Info)
1685  C4PropList::SetName(Info->Name);
1686  else
1687  C4PropList::SetName(NewName);
1688 }
1689 
1690 int32_t C4Object::GetValue(C4Object *pInBase, int32_t iForPlayer)
1691 {
1692  C4Value r = Call(PSF_CalcValue, &C4AulParSet(pInBase, iForPlayer));
1693  int32_t iValue;
1694  if (r != C4VNull)
1695  iValue = r.getInt();
1696  else
1697  {
1698  // get value of def
1699  // Caution: Do not pass pInBase here, because the def base value is to be queried
1700  // - and not the value if you had to buy the object in this particular base
1701  iValue = Def->GetValue(nullptr, iForPlayer);
1702  }
1703  // Con percentage
1704  iValue = iValue * Con / FullCon;
1705  // do any adjustments based on where the item is bought
1706  if (pInBase)
1707  {
1708  r = pInBase->Call(PSF_CalcSellValue, &C4AulParSet(this, iValue));
1709  if (r != C4VNull)
1710  iValue = r.getInt();
1711  }
1712  return iValue;
1713 }
1714 
1715 bool C4Object::Promote(int32_t torank, bool exception, bool fForceRankName)
1716 {
1717  if (!Info) return false;
1718  // get rank system
1719  C4Def *pUseDef = C4Id2Def(Info->id);
1720  C4RankSystem *pRankSys;
1721  if (pUseDef && pUseDef->pRankNames)
1722  pRankSys = pUseDef->pRankNames;
1723  else
1724  pRankSys = &::DefaultRanks;
1725  // always promote info
1726  Info->Promote(torank,*pRankSys, fForceRankName);
1727  // silent update?
1728  if (!pRankSys->GetRankName(torank,false)) return false;
1729  GameMsgObject(FormatString(LoadResStr("IDS_OBJ_PROMOTION"),GetName (),Info->sRankName.getData()).getData(),this);
1730 
1731  // call to object
1733 
1734  StartSoundEffect("UI::Trumpet",false,100,this);
1735  return true;
1736 }
1737 
1739 {
1740  // mesh attachments and animation nodes
1741  if(pMeshInstance) pMeshInstance->ClearPointers(pObj);
1742  // effects
1743  if (pEffects) pEffects->ClearPointers(pObj);
1744  // contents/contained: although normally not necessery because it's done in AssignRemoval and StatusDeactivate,
1745  // it is also required during game destruction (because ClearPointers might do script callbacks)
1746  // Perform silent exit to avoid additional callbacks
1747  if (Contained == pObj)
1748  {
1749  Contained->Contents.Remove(this);
1750  Contained = nullptr;
1751  }
1752  Contents.Remove(pObj);
1753  // Action targets
1754  if (Action.Target==pObj) Action.Target=nullptr;
1755  if (Action.Target2==pObj) Action.Target2=nullptr;
1756  // Commands
1757  C4Command *cCom;
1758  for (cCom=Command; cCom; cCom=cCom->Next)
1759  cCom->ClearPointers(pObj);
1760  // Menu
1761  if (Menu) Menu->ClearPointers(pObj);
1762  // Layer
1763  if (Layer==pObj) Layer=nullptr;
1764  // gfx overlays
1765  if (pGfxOverlay)
1766  {
1767  C4GraphicsOverlay *pNextGfxOvrl = pGfxOverlay, *pGfxOvrl;
1768  while ((pGfxOvrl = pNextGfxOvrl))
1769  {
1770  pNextGfxOvrl = pGfxOvrl->GetNext();
1771  if (pGfxOvrl->GetOverlayObject() == pObj)
1772  // overlay relying on deleted object: Delete!
1773  RemoveGraphicsOverlay(pGfxOvrl->GetID());
1774  }
1775  }
1776 }
1777 
1778 bool C4Object::SetPhase(int32_t iPhase)
1779 {
1780  C4PropList* pActionDef = GetAction();
1781  if (!pActionDef) return false;
1782  const int32_t length = pActionDef->GetPropertyInt(P_Length);
1783  Action.Phase=Clamp<int32_t>(iPhase,0,length);
1784  Action.PhaseDelay = 0;
1785  return true;
1786 }
1787 
1788 void C4Object::Draw(C4TargetFacet &cgo, int32_t iByPlayer, DrawMode eDrawMode, float offX, float offY)
1789 {
1790 #ifndef USE_CONSOLE
1791  C4Facet ccgo;
1792 
1793  // Status
1794  if (!Status || !Def) return;
1795 
1796  // visible?
1797  if (!IsVisible(iByPlayer, !!eDrawMode)) return;
1798 
1799  // Set up custom uniforms.
1800  auto uniform_popper = pDraw->scriptUniform.Push(this);
1801 
1802  // Line
1803  if (Def->Line) { DrawLine(cgo, iByPlayer); return; }
1804 
1805  // background particles (bounds not checked)
1806  if (BackParticles) BackParticles->Draw(cgo, this);
1807 
1808  // Object output position
1809  float newzoom = cgo.Zoom;
1810  if (eDrawMode!=ODM_Overlay)
1811  {
1812  if (!GetDrawPosition(cgo, offX, offY, newzoom)) return;
1813  }
1814  ZoomDataStackItem zdsi(newzoom);
1815 
1816  bool fYStretchObject=false;
1817  C4PropList* pActionDef = GetAction();
1818  if (pActionDef)
1819  if (pActionDef->GetPropertyInt(P_FacetTargetStretch))
1820  fYStretchObject=true;
1821 
1822  // Set audibility
1823  if (!eDrawMode) SetAudibilityAt(cgo, GetX(), GetY(), iByPlayer);
1824 
1825  // Output boundary
1826  if (!fYStretchObject && !eDrawMode && !(Category & C4D_Parallax))
1827  {
1828  // For actions with a custom facet set, check against that action facet. Otherwise (or with oversize objects), just check against shape.
1829  if (pActionDef && fix_r == Fix0 && !pActionDef->GetPropertyInt(P_FacetBase) && Con <= FullCon && Action.Facet.Wdt)
1830  {
1831  // active
1832  if ( !Inside<float>(offX+Shape.GetX()+Action.FacetX,cgo.X-Action.Facet.Wdt,cgo.X+cgo.Wdt)
1833  || (!Inside<float>(offY+Shape.GetY()+Action.FacetY,cgo.Y-Action.Facet.Hgt,cgo.Y+cgo.Hgt)) )
1834  {
1835  if (FrontParticles && !Contained) FrontParticles->Draw(cgo, this);
1836  return;
1837  }
1838  }
1839  else
1840  // idle
1841  if ( !Inside<float>(offX+Shape.GetX(),cgo.X-Shape.Wdt,cgo.X+cgo.Wdt)
1842  || (!Inside<float>(offY+Shape.GetY(),cgo.Y-Shape.Hgt,cgo.Y+cgo.Hgt)) )
1843  {
1844  if (FrontParticles && !Contained) FrontParticles->Draw(cgo, this);
1845  return;
1846  }
1847  }
1848 
1849  // ensure correct color is set
1850  if (GetGraphics()->Type == C4DefGraphics::TYPE_Bitmap)
1851  if (GetGraphics()->Bmp.BitmapClr) GetGraphics()->Bmp.BitmapClr->SetClr(Color);
1852 
1853  // Debug Display //////////////////////////////////////////////////////////////////////
1854  if (::GraphicsSystem.ShowCommand && !eDrawMode)
1855  {
1856  C4Command *pCom;
1857  int32_t ccx=GetX(),ccy=GetY();
1858  float offX1, offY1, offX2, offY2, newzoom;
1859  char szCommand[200];
1860  StdStrBuf Cmds;
1861  int32_t iMoveTos=0;
1862  for (pCom=Command; pCom; pCom=pCom->Next)
1863  {
1864  switch (pCom->Command)
1865  {
1866  case C4CMD_MoveTo:
1867  // Angle
1868  int32_t iAngle; iAngle=Angle(ccx,ccy,pCom->Tx._getInt(),pCom->Ty); while (iAngle>180) iAngle-=360;
1869  // Path
1870  if(GetDrawPosition(cgo, ccx, ccy, cgo.Zoom, offX1, offY1, newzoom) &&
1871  GetDrawPosition(cgo, pCom->Tx._getInt(), pCom->Ty, cgo.Zoom, offX2, offY2, newzoom))
1872  {
1873  ZoomDataStackItem zdsi(newzoom);
1874  pDraw->DrawLineDw(cgo.Surface,offX1,offY1,offX2,offY2,C4RGB(0xca,0,0));
1875  pDraw->DrawFrameDw(cgo.Surface,offX2-1,offY2-1,offX2+1,offY2+1,C4RGB(0xca,0,0));
1876  }
1877 
1878  ccx=pCom->Tx._getInt(); ccy=pCom->Ty;
1879  // Message
1880  iMoveTos++; szCommand[0]=0;
1881  break;
1882  case C4CMD_Put:
1883  sprintf(szCommand,"%s %s to %s",CommandName(pCom->Command),pCom->Target2 ? pCom->Target2->GetName() : pCom->Data ? pCom->Data.GetDataString().getData() : "Content",pCom->Target ? pCom->Target->GetName() : "");
1884  break;
1885  case C4CMD_Buy: case C4CMD_Sell:
1886  sprintf(szCommand,"%s %s at %s",CommandName(pCom->Command),pCom->Data.GetDataString().getData(),pCom->Target ? pCom->Target->GetName() : "closest base");
1887  break;
1888  case C4CMD_Acquire:
1889  sprintf(szCommand,"%s %s",CommandName(pCom->Command),pCom->Data.GetDataString().getData());
1890  break;
1891  case C4CMD_Call:
1892  sprintf(szCommand,"%s %s in %s",CommandName(pCom->Command),pCom->Text->GetCStr(),pCom->Target ? pCom->Target->GetName() : "(null)");
1893  break;
1894  case C4CMD_None:
1895  szCommand[0]=0;
1896  break;
1897  case C4CMD_Transfer:
1898  // Path
1899  if(GetDrawPosition(cgo, ccx, ccy, cgo.Zoom, offX1, offY1, newzoom) &&
1900  GetDrawPosition(cgo, pCom->Tx._getInt(), pCom->Ty, cgo.Zoom, offX2, offY2, newzoom))
1901  {
1902  ZoomDataStackItem zdsi(newzoom);
1903  pDraw->DrawLineDw(cgo.Surface,offX1,offY1,offX2,offY2,C4RGB(0,0xca,0));
1904  pDraw->DrawFrameDw(cgo.Surface,offX2-1,offY2-1,offX2+1,offY2+1,C4RGB(0,0xca,0));
1905  }
1906 
1907  ccx=pCom->Tx._getInt(); ccy=pCom->Ty;
1908  // Message
1909  sprintf(szCommand,"%s %s",CommandName(pCom->Command),pCom->Target ? pCom->Target->GetName() : "");
1910  break;
1911  default:
1912  sprintf(szCommand,"%s %s",CommandName(pCom->Command),pCom->Target ? pCom->Target->GetName() : "");
1913  break;
1914  }
1915  // Compose command stack message
1916  if (szCommand[0])
1917  {
1918  // End MoveTo stack first
1919  if (iMoveTos) { Cmds.AppendChar('|'); Cmds.AppendFormat("%dx MoveTo",iMoveTos); iMoveTos=0; }
1920  // Current message
1921  Cmds.AppendChar('|');
1922  if (pCom->Finished) Cmds.Append("<i>");
1923  Cmds.Append(szCommand);
1924  if (pCom->Finished) Cmds.Append("</i>");
1925  }
1926  }
1927  // Open MoveTo stack
1928  if (iMoveTos) { Cmds.AppendChar('|'); Cmds.AppendFormat("%dx MoveTo",iMoveTos); iMoveTos=0; }
1929  // Draw message
1930  int32_t cmwdt,cmhgt; ::GraphicsResource.FontRegular.GetTextExtent(Cmds.getData(),cmwdt,cmhgt,true);
1931  pDraw->TextOut(Cmds.getData(), ::GraphicsResource.FontRegular, 1.0, cgo.Surface,offX,offY+Shape.GetY()-10-cmhgt,C4Draw::DEFAULT_MESSAGE_COLOR,ACenter);
1932  }
1933  // Debug Display ///////////////////////////////////////////////////////////////////////////////
1934 
1935  // Don't draw (show solidmask)
1936  if (::GraphicsSystem.Show8BitSurface != 0)
1937  if (SolidMask.Wdt)
1938  {
1939  // DrawSolidMask(cgo); - no need to draw it, because the 8bit-surface will be shown
1940  return;
1941  }
1942 
1943  // Contained check
1944  if (Contained && !eDrawMode) return;
1945 
1946  // Visibility inside FoW
1947  const C4FoWRegion* pOldFoW = pDraw->GetFoW();
1948  if(pOldFoW && (Category & C4D_IgnoreFoW))
1949  pDraw->SetFoW(nullptr);
1950 
1951  // color modulation (including construction sign...)
1952  if (ColorMod != 0xffffffff || BlitMode) if (!eDrawMode) PrepareDrawing();
1953 
1954  // Not active or rotated: BaseFace only
1955  if (!pActionDef)
1956  {
1957  DrawFace(cgo, offX, offY);
1958  }
1959 
1960  // Active
1961  else
1962  {
1963  // FacetBase
1964  if (pActionDef->GetPropertyInt(P_FacetBase) || GetGraphics()->Type != C4DefGraphics::TYPE_Bitmap)
1965  DrawFace(cgo, offX, offY, 0, Action.DrawDir);
1966 
1967  // Special: stretched action facet
1968  if (Action.Facet.Surface && pActionDef->GetPropertyInt(P_FacetTargetStretch))
1969  {
1970  if (Action.Target)
1971  pDraw->Blit(Action.Facet.Surface,
1972  float(Action.Facet.X),float(Action.Facet.Y),float(Action.Facet.Wdt),float(Action.Facet.Hgt),
1973  cgo.Surface,
1974  offX + Shape.GetX() + Action.FacetX, offY + Shape.GetY() + Action.FacetY,Action.Facet.Wdt,
1975  (fixtof(Action.Target->fix_y) + Action.Target->Shape.GetY()) - (fixtof(fix_y) + Shape.GetY() + Action.FacetY),
1976  true);
1977  }
1978  else if (Action.Facet.Surface)
1979  DrawActionFace(cgo, offX, offY);
1980  }
1981 
1982  // end of color modulation
1983  if (ColorMod != 0xffffffff || BlitMode) if (!eDrawMode) FinishedDrawing();
1984 
1985  // draw overlays - after blit mode changes, because overlay gfx set their own
1986  if (pGfxOverlay) if (eDrawMode!=ODM_BaseOnly)
1987  for (C4GraphicsOverlay *pGfxOvrl = pGfxOverlay; pGfxOvrl; pGfxOvrl = pGfxOvrl->GetNext())
1988  if (!pGfxOvrl->IsPicture())
1989  pGfxOvrl->Draw(cgo, this, iByPlayer);
1990 
1991  // local particles in front of the object
1992  if (eDrawMode!=ODM_BaseOnly)
1993  {
1994  if (FrontParticles)
1995  FrontParticles->Draw(cgo, this);
1996  }
1997 
1998  // Debug Display ////////////////////////////////////////////////////////////////////////
1999  if (::GraphicsSystem.ShowVertices) if (eDrawMode!=ODM_BaseOnly)
2000  {
2001  int32_t cnt;
2002  if (Shape.VtxNum>1)
2003  for (cnt=0; cnt<Shape.VtxNum; cnt++)
2004  {
2005  DrawVertex(cgo,
2006  offX+Shape.VtxX[cnt],
2007  offY+Shape.VtxY[cnt],
2008  (Shape.VtxCNAT[cnt] & CNAT_NoCollision) ? C4RGB(0, 0, 0xff) : (Mobile ? C4RGB(0xff, 0, 0) : C4RGB(0xef, 0xef, 0)),
2009  Shape.VtxContactCNAT[cnt]);
2010  }
2011  }
2012 
2013  if (::GraphicsSystem.ShowEntrance) if (eDrawMode!=ODM_BaseOnly)
2014  {
2015  if (OCF & OCF_Entrance)
2016  pDraw->DrawFrameDw(cgo.Surface,offX+Def->Entrance.x,
2017  offY+Def->Entrance.y,
2018  offX+Def->Entrance.x+Def->Entrance.Wdt-1,
2019  offY+Def->Entrance.y+Def->Entrance.Hgt-1,
2020  C4RGB(0, 0, 0xff));
2021  if (OCF & OCF_Collection)
2022  pDraw->DrawFrameDw(cgo.Surface,offX+Def->Collection.x,
2023  offY+Def->Collection.y,
2024  offX+Def->Collection.x+Def->Collection.Wdt-1,
2025  offY+Def->Collection.y+Def->Collection.Hgt-1,
2026  C4RGB(0xca, 0, 0));
2027  }
2028 
2029  if (::GraphicsSystem.ShowAction) if (eDrawMode!=ODM_BaseOnly)
2030  {
2031  if (pActionDef)
2032  {
2033  StdStrBuf str;
2034  str.Format("%s (%d)",pActionDef->GetName(),Action.Phase);
2035  int32_t cmwdt,cmhgt; ::GraphicsResource.FontRegular.GetTextExtent(str.getData(),cmwdt,cmhgt,true);
2037  1.0, cgo.Surface, offX, offY + Shape.GetY() - cmhgt,
2038  InLiquid ? 0xfa0000FF : C4Draw::DEFAULT_MESSAGE_COLOR, ACenter);
2039  }
2040  }
2041  // Debug Display ///////////////////////////////////////////////////////////////////////
2042 
2043  // Restore visibility inside FoW
2044  if (pOldFoW) pDraw->SetFoW(pOldFoW);
2045 #endif
2046 }
2047 
2048 void C4Object::DrawTopFace(C4TargetFacet &cgo, int32_t iByPlayer, DrawMode eDrawMode, float offX, float offY)
2049 {
2050 #ifndef USE_CONSOLE
2051  // Status
2052  if (!Status || !Def) return;
2053  // visible?
2054  if (!IsVisible(iByPlayer, eDrawMode==ODM_Overlay)) return;
2055  // target pos (parallax)
2056  float newzoom = cgo.Zoom;
2057  if (eDrawMode!=ODM_Overlay) GetDrawPosition(cgo, offX, offY, newzoom);
2058  ZoomDataStackItem zdsi(newzoom);
2059  // TopFace
2060  if (!(TopFace.Surface || (OCF & OCF_Construct))) return;
2061  // Output bounds check
2062  if (!Inside<float>(offX, cgo.X - Shape.Wdt, cgo.X + cgo.Wdt)
2063  || !Inside<float>(offY, cgo.Y - Shape.Hgt, cgo.Y + cgo.Hgt))
2064  return;
2065  // Don't draw (show solidmask)
2066  if (::GraphicsSystem.Show8BitSurface != 0 && SolidMask.Wdt) return;
2067  // Contained
2068  if (Contained) if (eDrawMode!=ODM_Overlay) return;
2069  // Construction sign
2070  if (OCF & OCF_Construct && fix_r == Fix0)
2071  if (eDrawMode!=ODM_BaseOnly)
2072  {
2074  pDraw->Blit(fctConSign.Surface,
2075  fctConSign.X, fctConSign.Y,
2076  fctConSign.Wdt, fctConSign.Hgt,
2077  cgo.Surface,
2078  offX + Shape.GetX(), offY + Shape.GetY() + Shape.Hgt - fctConSign.Hgt,
2079  fctConSign.Wdt, fctConSign.Hgt, true);
2080  }
2081  if(TopFace.Surface)
2082  {
2083  // FacetTopFace: Override TopFace.GetX()/GetY()
2084  C4PropList* pActionDef = GetAction();
2085  if (pActionDef && pActionDef->GetPropertyInt(P_FacetTopFace))
2086  {
2087  int32_t iPhase = Action.Phase;
2088  if (pActionDef->GetPropertyInt(P_Reverse)) iPhase = pActionDef->GetPropertyInt(P_Length) - 1 - Action.Phase;
2089  TopFace.X = pActionDef->GetPropertyInt(P_X) + Def->TopFace.x + pActionDef->GetPropertyInt(P_Wdt) * iPhase;
2090  TopFace.Y = pActionDef->GetPropertyInt(P_Y) + Def->TopFace.y + pActionDef->GetPropertyInt(P_Hgt) * Action.DrawDir;
2091  }
2092  // ensure correct color is set
2093  if (GetGraphics()->Bmp.BitmapClr) GetGraphics()->Bmp.BitmapClr->SetClr(Color);
2094  // color modulation
2095  if (!eDrawMode) PrepareDrawing();
2096  // Draw top face bitmap
2097  if (Con!=FullCon && Def->GrowthType)
2098  // stretched
2099  pDraw->Blit(TopFace.Surface,
2100  TopFace.X, TopFace.Y, TopFace.Wdt, TopFace.Hgt,
2101  cgo.Surface,
2102  offX + Shape.GetX() + float(Def->TopFace.tx * Con) / FullCon, offY + Shape.GetY() + float(Def->TopFace.ty * Con) / FullCon,
2103  float(TopFace.Wdt * Con) / FullCon, float(TopFace.Hgt * Con) / FullCon,
2104  true, pDrawTransform ? &C4DrawTransform(*pDrawTransform, offX, offY) : nullptr);
2105  else
2106  // normal
2107  pDraw->Blit(TopFace.Surface,
2108  TopFace.X,TopFace.Y,
2109  TopFace.Wdt,TopFace.Hgt,
2110  cgo.Surface,
2111  offX + Shape.GetX() + Def->TopFace.tx, offY + Shape.GetY() + Def->TopFace.ty,
2112  TopFace.Wdt, TopFace.Hgt,
2113  true, pDrawTransform ? &C4DrawTransform(*pDrawTransform, offX, offY) : nullptr);
2114  }
2115  // end of color modulation
2116  if (!eDrawMode) FinishedDrawing();
2117 #endif
2118 }
2119 
2120 void C4Object::DrawLine(C4TargetFacet &cgo, int32_t at_player)
2121 {
2122  // Nothing to draw if the object has less than two vertices
2123  if (Shape.VtxNum < 2)
2124  return;
2125 #ifndef USE_CONSOLE
2126  // Audibility
2127  SetAudibilityAt(cgo, Shape.VtxX[0], Shape.VtxY[0], at_player);
2128  SetAudibilityAt(cgo, Shape.VtxX[Shape.VtxNum - 1], Shape.VtxY[Shape.VtxNum - 1], at_player);
2129  // additive mode?
2130  PrepareDrawing();
2131  // Draw line segments
2132  C4Value colorsV; GetProperty(P_LineColors, &colorsV);
2133  C4ValueArray *colors = colorsV.getArray();
2134  // TODO: Edge color (color1) is currently ignored.
2135  int32_t color0 = 0xFFFF00FF;// , color1 = 0xFFFF00FF; // use bright colors so author notices
2136  if (colors)
2137  {
2138  color0 = colors->GetItem(0).getInt();
2139  }
2140 
2141  std::vector<C4BltVertex> vertices;
2142  vertices.resize( (Shape.VtxNum - 1) * 2);
2143  for (int32_t vtx=0; vtx+1<Shape.VtxNum; vtx++)
2144  {
2145  DwTo4UB(color0, vertices[2*vtx].color);
2146  DwTo4UB(color0, vertices[2*vtx+1].color);
2147 
2148  vertices[2*vtx].ftx = Shape.VtxX[vtx] + cgo.X - cgo.TargetX;
2149  vertices[2*vtx].fty = Shape.VtxY[vtx] + cgo.Y - cgo.TargetY;
2150  vertices[2*vtx+1].ftx = Shape.VtxX[vtx+1] + cgo.X - cgo.TargetX;
2151  vertices[2*vtx+1].fty = Shape.VtxY[vtx+1] + cgo.Y - cgo.TargetY;
2152  }
2153 
2154  pDraw->PerformMultiLines(cgo.Surface, &vertices[0], vertices.size(), 1.0f, nullptr);
2155 
2156  // reset blit mode
2157  FinishedDrawing();
2158 #endif
2159 }
2160 
2162 {
2163  bool deserializing = pComp->isDeserializer();
2164  if (deserializing)
2165  Clear();
2166 
2167  // Compile ID, search definition
2168  pComp->Value(mkNamingAdapt(id, "id", C4ID::None ));
2169  if (deserializing)
2170  {
2171  Def = ::Definitions.ID2Def(id);
2172  if (!Def)
2173  { pComp->excNotFound(LoadResStr("IDS_PRC_UNDEFINEDOBJECT"),id.ToString()); return; }
2174  }
2175 
2176  pComp->Value(mkNamingAdapt( mkParAdapt(static_cast<C4PropListNumbered&>(*this), numbers), "Properties"));
2177  pComp->Value(mkNamingAdapt( Status, "Status", 1 ));
2178  if (Info) nInfo = Info->Name; else nInfo.Clear();
2179  pComp->Value(mkNamingAdapt( toC4CStrBuf(nInfo), "Info", "" ));
2180  pComp->Value(mkNamingAdapt( Owner, "Owner", NO_OWNER ));
2181  pComp->Value(mkNamingAdapt( Controller, "Controller", NO_OWNER ));
2182  pComp->Value(mkNamingAdapt( LastEnergyLossCausePlayer, "LastEngLossPlr", NO_OWNER ));
2183  pComp->Value(mkNamingAdapt( Category, "Category", 0 ));
2184  pComp->Value(mkNamingAdapt( Plane, "Plane", 0 ));
2185 
2186  pComp->Value(mkNamingAdapt( iLastAttachMovementFrame, "LastSolidAtchFrame", -1 ));
2187  pComp->Value(mkNamingAdapt( Con, "Size", 0 ));
2188  pComp->Value(mkNamingAdapt( OwnMass, "OwnMass", 0 ));
2189  pComp->Value(mkNamingAdapt( Mass, "Mass", 0 ));
2190  pComp->Value(mkNamingAdapt( Damage, "Damage", 0 ));
2191  pComp->Value(mkNamingAdapt( Energy, "Energy", 0 ));
2192  pComp->Value(mkNamingAdapt( Alive, "Alive", false ));
2193  pComp->Value(mkNamingAdapt( Breath, "Breath", 0 ));
2194  pComp->Value(mkNamingAdapt( Color, "Color", 0u ));
2195  pComp->Value(mkNamingAdapt( fix_x, "X", Fix0 ));
2196  pComp->Value(mkNamingAdapt( fix_y, "Y", Fix0 ));
2197  pComp->Value(mkNamingAdapt( fix_r, "R", Fix0 ));
2198  pComp->Value(mkNamingAdapt( xdir, "XDir", 0 ));
2199  pComp->Value(mkNamingAdapt( ydir, "YDir", 0 ));
2200  pComp->Value(mkNamingAdapt( rdir, "RDir", 0 ));
2201  pComp->Value(mkParAdapt(Shape, &Def->Shape));
2202  pComp->Value(mkNamingAdapt( fOwnVertices, "OwnVertices", false ));
2203  pComp->Value(mkNamingAdapt( SolidMask, "SolidMask", Def->SolidMask ));
2204  pComp->Value(mkNamingAdapt( HalfVehicleSolidMask, "HalfVehicleSolidMask", false ));
2205  pComp->Value(mkNamingAdapt( PictureRect, "Picture" ));
2206  pComp->Value(mkNamingAdapt( Mobile, "Mobile", false ));
2207  pComp->Value(mkNamingAdapt( OnFire, "OnFire", false ));
2208  pComp->Value(mkNamingAdapt( InLiquid, "InLiquid", false ));
2209  pComp->Value(mkNamingAdapt( EntranceStatus, "EntranceStatus", false ));
2210  pComp->Value(mkNamingAdapt( OCF, "OCF", 0u ));
2211  pComp->Value(Action);
2212  pComp->Value(mkNamingAdapt( Contained, "Contained", C4ObjectPtr::Null ));
2213  pComp->Value(mkNamingAdapt( Action.Target, "ActionTarget1", C4ObjectPtr::Null ));
2214  pComp->Value(mkNamingAdapt( Action.Target2, "ActionTarget2", C4ObjectPtr::Null ));
2215  pComp->Value(mkNamingAdapt( mkParAdapt(Contents, numbers), "Contents" ));
2216  pComp->Value(mkNamingAdapt( lightRange, "LightRange", 0 ));
2217  pComp->Value(mkNamingAdapt( lightFadeoutRange, "LightFadeoutRange", 0 ));
2218  pComp->Value(mkNamingAdapt( lightColor, "lightColor", 0xffffffffu ));
2219  pComp->Value(mkNamingAdapt( ColorMod, "ColorMod", 0xffffffffu ));
2220  pComp->Value(mkNamingAdapt( BlitMode, "BlitMode", 0u ));
2221  pComp->Value(mkNamingAdapt( CrewDisabled, "CrewDisabled", false ));
2222  pComp->Value(mkNamingAdapt( Layer, "Layer", C4ObjectPtr::Null ));
2223  pComp->Value(mkNamingAdapt( C4DefGraphicsAdapt(pGraphics), "Graphics", &Def->Graphics ));
2224  pComp->Value(mkNamingPtrAdapt( pDrawTransform, "DrawTransform" ));
2225  pComp->Value(mkParAdapt(mkNamingPtrAdapt( pEffects, "Effects" ), this, numbers));
2226  pComp->Value(mkNamingAdapt( C4GraphicsOverlayListAdapt(pGfxOverlay),"GfxOverlay", (C4GraphicsOverlay *)nullptr));
2227 
2228  // Serialize mesh instance if we have a mesh graphics
2229  if(pGraphics->Type == C4DefGraphics::TYPE_Mesh)
2230  {
2231  if(pComp->isDeserializer())
2232  {
2233  assert(!pMeshInstance);
2234  pMeshInstance = new StdMeshInstance(*pGraphics->Mesh, Def->GrowthType ? 1.0f : static_cast<float>(Con)/static_cast<float>(FullCon));
2235  }
2236 
2237  pComp->Value(mkNamingAdapt(mkParAdapt(*pMeshInstance, C4MeshDenumeratorFactory), "Mesh"));
2238 
2239  // Does not work because unanimated meshes without attached meshes
2240  // do not even write a [Mesh] header so this does not create a mesh instance in that case
2241 /* pComp->Value(mkNamingContextPtrAdapt( pMeshInstance, *pGraphics->Mesh, "Mesh"));
2242  if(!pMeshInstance)
2243  pComp->excCorrupt("Mesh graphics without mesh instance");*/
2244  }
2245 
2246  // TODO: Animations / attached meshes
2247 
2248  // Commands
2249  if (pComp->FollowName("Commands"))
2250  {
2251  if (deserializing)
2252  {
2253  C4Command *pCmd = nullptr;
2254  for (int i = 1; ; i++)
2255  {
2256  // Every command has its own naming environment
2257  StdStrBuf Naming = FormatString("Command%d", i);
2258  pComp->Value(mkParAdapt(mkNamingPtrAdapt(pCmd ? pCmd->Next : Command, Naming.getData()), numbers));
2259  // Last command?
2260  pCmd = (pCmd ? pCmd->Next : Command);
2261  if (!pCmd)
2262  break;
2263  pCmd->cObj = this;
2264  }
2265  }
2266  else
2267  {
2268  C4Command *pCmd = Command;
2269  for (int i = 1; pCmd; i++, pCmd = pCmd->Next)
2270  {
2271  StdStrBuf Naming = FormatString("Command%d", i);
2272  pComp->Value(mkNamingAdapt(mkParAdapt(*pCmd, numbers), Naming.getData()));
2273  }
2274  }
2275  }
2276 
2277  // Compiling? Do initialization.
2278  if (deserializing)
2279  {
2280  // add to def count
2281  Def->Count++;
2282 
2283 
2284  // Set action (override running data)
2285  /* FIXME
2286  int32_t iTime=Action.Time;
2287  int32_t iPhase=Action.Phase;
2288  int32_t iPhaseDelay=Action.PhaseDelay;
2289  if (SetActionByName(Action.pActionDef->GetName(),0,0,false))
2290  {
2291  Action.Time=iTime;
2292  Action.Phase=iPhase; // No checking for valid phase
2293  Action.PhaseDelay=iPhaseDelay;
2294  }*/
2295 
2296  if (pMeshInstance)
2297  {
2298  // Set Action animation by slot 0
2299  Action.Animation = pMeshInstance->GetRootAnimationForSlot(0);
2300  pMeshInstance->SetFaceOrderingForClrModulation(ColorMod);
2301  }
2302 
2303  // blit mode not assigned? use definition default then
2304  if (!BlitMode) BlitMode = Def->BlitMode;
2305 
2306  // object needs to be resorted? May happen if there's unsorted objects in savegame
2307  if (Unsorted) Game.fResortAnyObject = true;
2308  }
2309 
2310 }
2311 
2313 {
2314  C4PropList::Denumerate(numbers);
2315  // Standard enumerated pointers
2316  Contained.DenumeratePointers();
2317  Action.Target.DenumeratePointers();
2318  Action.Target2.DenumeratePointers();
2319  Layer.DenumeratePointers();
2320 
2321  // Post-compile object list
2322  Contents.DenumeratePointers();
2323 
2324  // Commands
2325  for (C4Command *pCom=Command; pCom; pCom=pCom->Next)
2326  pCom->Denumerate(numbers);
2327 
2328  // effects
2329  if (pEffects) pEffects->Denumerate(numbers);
2330 
2331  // gfx overlays
2332  if (pGfxOverlay)
2333  for (C4GraphicsOverlay *pGfxOvrl = pGfxOverlay; pGfxOvrl; pGfxOvrl = pGfxOvrl->GetNext())
2334  pGfxOvrl->DenumeratePointers();
2335 
2336  // mesh instance
2337  if (pMeshInstance) pMeshInstance->DenumeratePointers();
2338 }
2339 
2340 void C4Object::DrawPicture(C4Facet &cgo, bool fSelected, C4DrawTransform* transform)
2341 {
2342  // Draw def picture with object color
2343  Def->Draw(cgo,fSelected,Color,this,0,0,transform);
2344 }
2345 
2347 {
2348  // set picture rect to facet
2349  C4Rect fctPicRect = PictureRect;
2350  if (!fctPicRect.Wdt) fctPicRect = Def->PictureRect;
2351  C4Facet fctPicture;
2352  fctPicture.Set(GetGraphics()->GetBitmap(Color),fctPicRect.x,fctPicRect.y,fctPicRect.Wdt,fctPicRect.Hgt);
2353 
2354  // use direct facet w/o own data if possible
2355  if (ColorMod == 0xffffffff && BlitMode == C4GFXBLIT_NORMAL && !pGfxOverlay)
2356  {
2357  cgo.Set(fctPicture);
2358  return;
2359  }
2360 
2361  // otherwise, draw to picture facet
2362  if (!cgo.Create(cgo.Wdt, cgo.Hgt)) return;
2363 
2364  // specific object color?
2365  PrepareDrawing();
2366 
2367  // draw picture itself
2368  fctPicture.Draw(cgo,true);
2369 
2370  // draw overlays
2371  if (pGfxOverlay)
2372  for (C4GraphicsOverlay *pGfxOvrl = pGfxOverlay; pGfxOvrl; pGfxOvrl = pGfxOvrl->GetNext())
2373  if (pGfxOvrl->IsPicture())
2374  pGfxOvrl->DrawPicture(cgo, this, nullptr);
2375 
2376  // done; reset drawing states
2377  FinishedDrawing();
2378 }
2379 
2381 {
2382  // Check owner and controller
2383  if (!ValidPlr(Owner)) Owner=NO_OWNER;
2384  if (!ValidPlr(Controller)) Controller=NO_OWNER;
2385  // Color is not reset any more, because many scripts change colors to non-owner-colors these days
2386  // Additionally, player colors are now guarantueed to remain the same in savegame resumes
2387  return true;
2388 }
2389 
2391 {
2392  if (Info || !ValidPlr(Owner)) return false;
2393  // In crew list?
2394  C4Player *pPlr = ::Players.Get(Owner);
2395  if (pPlr->Crew.GetLink(this))
2396  {
2397  // Register with player
2398  if (!::Players.Get(Owner)->MakeCrewMember(this, true, false))
2399  pPlr->Crew.Remove(this);
2400  return true;
2401  }
2402  // Info set, but not in crew list, so
2403  // a) The savegame is old-style (without crew list)
2404  // or b) The clonk is dead
2405  // or c) The clonk belongs to a script player that's restored without Game.txt
2406  else if (nInfo.getLength())
2407  {
2408  if (!::Players.Get(Owner)->MakeCrewMember(this, true, false))
2409  return false;
2410  // Dead and gone (info flags, remove from crew/cursor)
2411  if (!Alive)
2412  {
2413  if (ValidPlr(Owner)) ::Players.Get(Owner)->ClearPointers(this, true);
2414  }
2415  return true;
2416  }
2417  return false;
2418 }
2419 
2421 {
2422  if (!lightRange && !lightFadeoutRange) return true;
2423 
2424  UpdateLight();
2425  return true;
2426 }
2427 
2429 {
2430  if (Info==pInfo)
2431  {
2432  Info=nullptr;
2433  }
2434 }
2435 
2437 {
2438  ClearParticleLists();
2439 
2440  if (pEffects) { delete pEffects; pEffects=nullptr; }
2441  if (pSolidMaskData) { delete pSolidMaskData; pSolidMaskData=nullptr; }
2442  if (Menu) delete Menu; Menu=nullptr;
2443  if (MaterialContents) delete MaterialContents; MaterialContents=nullptr;
2444  // clear commands!
2445  C4Command *pCom, *pNext;
2446  for (pCom=Command; pCom; pCom=pNext)
2447  {
2448  pNext=pCom->Next; delete pCom; pCom=pNext;
2449  }
2450  if (pDrawTransform) { delete pDrawTransform; pDrawTransform=nullptr; }
2451  if (pGfxOverlay) { delete pGfxOverlay; pGfxOverlay=nullptr; }
2452  if (pMeshInstance) { delete pMeshInstance; pMeshInstance = nullptr; }
2453 }
2454 
2455 bool C4Object::MenuCommand(const char *szCommand)
2456 {
2457  // Native script execution
2458  if (!Def || !Status) return false;
2459  return !! ::AulExec.DirectExec(this, szCommand, "MenuCommand");
2460 }
2461 
2462 void C4Object::SetSolidMask(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iTX, int32_t iTY)
2463 {
2464  // remove old
2465  if (pSolidMaskData) { delete pSolidMaskData; pSolidMaskData=nullptr; }
2466  // set new data
2467  SolidMask.Set(iX,iY,iWdt,iHgt,iTX,iTY);
2468  // re-put if valid
2469  if (CheckSolidMaskRect()) UpdateSolidMask(false);
2470 }
2471 
2473 {
2474  if (!pSolidMaskData) return;
2475  HalfVehicleSolidMask = set;
2476  pSolidMaskData->SetHalfVehicle(set);
2477 }
2478 
2480 {
2481  // Ensure SolidMask rect lies within bounds of SolidMask bitmap in definition
2482  CSurface8 *sfcGraphics = Def->pSolidMask;
2483  if (!sfcGraphics)
2484  {
2485  // no graphics to set solid in
2486  SolidMask.Set(0,0,0,0,0,0);
2487  return false;
2488  }
2489  SolidMask.Set(std::max<int32_t>(SolidMask.x,0), std::max<int32_t>(SolidMask.y,0),
2490  std::min<int32_t>(SolidMask.Wdt,sfcGraphics->Wdt-SolidMask.x), std::min<int32_t>(SolidMask.Hgt, sfcGraphics->Hgt-SolidMask.y),
2491  SolidMask.tx, SolidMask.ty);
2492  if (SolidMask.Hgt<=0) SolidMask.Wdt=0;
2493  return SolidMask.Wdt>0;
2494 }
2495 
2497 {
2498  // Misc. no-save safeties
2499  Action.t_attach = CNAT_None;
2500  InMat = MNone;
2501  t_contact = 0;
2502  // Update OCF
2503  SetOCF();
2504  // Menu
2505  CloseMenu(true);
2506  // Material contents
2507  if (MaterialContents) delete MaterialContents; MaterialContents=nullptr;
2508  // reset speed of staticback-objects
2509  if (Category & C4D_StaticBack)
2510  {
2511  xdir = ydir = 0;
2512  }
2513 }
2514 
2516 {
2517  // Status
2518  if (!Status) return;
2519  // No select marks in film playback
2520  if (Game.C4S.Head.Film && Game.C4S.Head.Replay) return;
2521  // target pos (parallax)
2522  float offX, offY, newzoom;
2523  GetDrawPosition(cgo, offX, offY, newzoom);
2524  // Output boundary
2525  if (!Inside<float>(offX, cgo.X, cgo.X + cgo.Wdt)
2526  || !Inside<float>(offY, cgo.Y, cgo.Y + cgo.Hgt)) return;
2527  // Draw select marks
2528  float cox = offX + Shape.GetX() - cgo.X + cgo.X - 2;
2529  float coy = offY + Shape.GetY() - cgo.Y + cgo.Y - 2;
2530  GfxR->fctSelectMark.Draw(cgo.Surface,cox,coy,0);
2531  GfxR->fctSelectMark.Draw(cgo.Surface,cox+Shape.Wdt,coy,1);
2532  GfxR->fctSelectMark.Draw(cgo.Surface,cox,coy+Shape.Hgt,2);
2533  GfxR->fctSelectMark.Draw(cgo.Surface,cox+Shape.Wdt,coy+Shape.Hgt,3);
2534 }
2535 
2537 {
2538  C4Command *pNext;
2539  while (Command)
2540  {
2541  pNext=Command->Next;
2542  if (!Command->iExec)
2543  delete Command;
2544  else
2545  Command->iExec = 2;
2546  Command=pNext;
2547  }
2548 }
2549 
2551 {
2552  C4Command *pCom,*pNext;
2553  for (pCom=Command; pCom; pCom=pNext)
2554  {
2555  // Last one to clear
2556  if (pCom==pUntil) pNext=nullptr;
2557  // Next one to clear after this
2558  else pNext=pCom->Next;
2559  Command=pCom->Next;
2560  if (!pCom->iExec)
2561  delete pCom;
2562  else
2563  pCom->iExec = 2;
2564  }
2565 }
2566 
2567 bool C4Object::AddCommand(int32_t iCommand, C4Object *pTarget, C4Value iTx, int32_t iTy,
2568  int32_t iUpdateInterval, C4Object *pTarget2,
2569  bool fInitEvaluation, C4Value iData, bool fAppend,
2570  int32_t iRetries, C4String *szText, int32_t iBaseMode)
2571 {
2572  // Command stack size safety
2573  const int32_t MaxCommandStack = 35;
2574  C4Command *pCom,*pLast; int32_t iCommands;
2575  for (pCom=Command,iCommands=0; pCom; pCom=pCom->Next,iCommands++) {}
2576  if (iCommands>=MaxCommandStack) return false;
2577  // Valid command safety
2578  if (!Inside(iCommand,C4CMD_First,C4CMD_Last)) return false;
2579  // Allocate and set new command
2580  if (!(pCom=new C4Command)) return false;
2581  pCom->Set(iCommand,this,pTarget,iTx,iTy,pTarget2,iData,
2582  iUpdateInterval,!fInitEvaluation,iRetries,szText,iBaseMode);
2583  // Append to bottom of stack
2584  if (fAppend)
2585  {
2586  for (pLast=Command; pLast && pLast->Next; pLast=pLast->Next) {}
2587  if (pLast) pLast->Next=pCom;
2588  else Command=pCom;
2589  }
2590  // Add to top of command stack
2591  else
2592  {
2593  pCom->Next=Command;
2594  Command=pCom;
2595  }
2596  // Success
2597  return true;
2598 }
2599 
2600 void C4Object::SetCommand(int32_t iCommand, C4Object *pTarget, C4Value iTx, int32_t iTy,
2601  C4Object *pTarget2, bool fControl, C4Value iData,
2602  int32_t iRetries, C4String *szText)
2603 {
2604  // Clear stack
2605  ClearCommands();
2606  // Close menu
2607  if (fControl)
2608  if (!CloseMenu(false)) return;
2609  // Script overload
2610  if (fControl)
2612  pTarget,
2613  iTx,
2614  iTy,
2615  pTarget2,
2616  iData)))
2617  return;
2618  // Inside vehicle control overload
2619  if (Contained)
2620  if (Contained->Def->VehicleControl & C4D_VehicleControl_Inside)
2621  {
2622  Contained->Controller=Controller;
2623  if (!!Contained->Call(PSF_ControlCommand,&C4AulParSet(CommandName(iCommand),
2624  pTarget,
2625  iTx,
2626  iTy,
2627  pTarget2,
2628  iData,
2629  this)))
2630  return;
2631  }
2632  // Outside vehicle control overload
2633  if (GetProcedure()==DFA_PUSH)
2634  if (Action.Target) if (Action.Target->Def->VehicleControl & C4D_VehicleControl_Outside)
2635  {
2636  Action.Target->Controller=Controller;
2637  if (!!Action.Target->Call(PSF_ControlCommand,&C4AulParSet(CommandName(iCommand),
2638  pTarget,
2639  iTx,
2640  iTy,
2641  pTarget2,
2642  iData)))
2643  return;
2644  }
2645  // Add new command
2646  AddCommand(iCommand,pTarget,iTx,iTy,0,pTarget2,true,iData,false,iRetries,szText,C4CMD_Mode_Base);
2647 }
2648 
2649 C4Command *C4Object::FindCommand(int32_t iCommandType) const
2650 {
2651  // seek all commands
2652  for (C4Command *pCom = Command; pCom; pCom=pCom->Next)
2653  if (pCom->Command == iCommandType) return pCom;
2654  // nothing found
2655  return nullptr;
2656 }
2657 
2659 {
2660  // Execute first command
2661  if (Command) Command->Execute();
2662  // Command finished: engine call
2663  if (Command && Command->Finished)
2664  Call(PSF_ControlCommandFinished,&C4AulParSet(CommandName(Command->Command), Command->Target, Command->Tx, Command->Ty, Command->Target2, Command->Data));
2665  // Clear finished commands
2666  while (Command && Command->Finished) ClearCommand(Command);
2667  // Done
2668  return true;
2669 }
2670 
2672 {
2673  // Flag resort
2674  Unsorted=true;
2675  Game.fResortAnyObject = true;
2676  // Must not immediately resort - link change/removal would crash Game::ExecObjects
2677 }
2678 
2680 {
2681  C4Value value;
2682  GetProperty(P_Action, &value);
2683  return value.getPropList();
2684 }
2685 
2686 bool C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2, int32_t iCalls, bool fForce)
2687 {
2688  C4Value vLastAction;
2689  GetProperty(P_Action, &vLastAction);
2690  C4PropList * LastAction = vLastAction.getPropList();
2691  int32_t iLastPhase=Action.Phase;
2692  C4Object *pLastTarget = Action.Target;
2693  C4Object *pLastTarget2 = Action.Target2;
2694  // No other action
2695  if (LastAction)
2696  if (LastAction->GetPropertyInt(P_NoOtherAction) && !fForce)
2697  if (Act != LastAction)
2698  return false;
2699  // Set animation on instance. Abort if the mesh does not have
2700  // such an animation.
2701  if (pMeshInstance)
2702  {
2703  if (Action.Animation) pMeshInstance->StopAnimation(Action.Animation);
2704  Action.Animation = nullptr;
2705 
2706  C4String* Animation = Act ? Act->GetPropertyStr(P_Animation) : nullptr;
2707  if (Animation)
2708  {
2709  // note that weight is ignored
2710  Action.Animation = pMeshInstance->PlayAnimation(Animation->GetData(), 0, nullptr, new C4ValueProviderAction(this), new C4ValueProviderConst(itofix(1)), true);
2711  }
2712  }
2713  // Stop previous act sound
2714  if (LastAction)
2715  if (Act != LastAction)
2716  if (LastAction->GetPropertyStr(P_Sound))
2717  StopSoundEffect(LastAction->GetPropertyStr(P_Sound)->GetCStr(),this);
2718  // Unfullcon objects no action
2719  if (Con<FullCon)
2720  if (!Def->IncompleteActivity)
2721  Act = nullptr;
2722  // Reset action time on change
2723  if (Act!=LastAction)
2724  {
2725  Action.Time=0;
2726  // reset action data and targets if procedure is changed
2727  if ((Act ? Act->GetPropertyP(P_Procedure) : -1)
2728  != (LastAction ? LastAction->GetPropertyP(P_Procedure) : -1))
2729  {
2730  Action.Data = 0;
2731  Action.Target = nullptr;
2732  Action.Target2 = nullptr;
2733  }
2734  }
2735  // Set new action
2736  SetProperty(P_Action, C4VPropList(Act));
2737  Action.Phase=Action.PhaseDelay=0;
2738  // Set target if specified
2739  if (pTarget) Action.Target=pTarget;
2740  if (pTarget2) Action.Target2=pTarget2;
2741  // Set Action Facet
2742  UpdateActionFace();
2743  // update flipdir
2744  if ((LastAction ? LastAction->GetPropertyInt(P_FlipDir) : 0)
2745  != (Act ? Act->GetPropertyInt(P_FlipDir) : 0)) UpdateFlipDir();
2746  // Start act sound
2747  if (Act)
2748  if (Act != LastAction)
2749  if (Act->GetPropertyStr(P_Sound))
2750  StartSoundEffect(Act->GetPropertyStr(P_Sound)->GetCStr(),+1,100,this);
2751  // Reset OCF
2752  SetOCF();
2753  // issue calls
2754  // Execute EndCall for last action
2755  if (iCalls & SAC_EndCall && !fForce)
2756  if (LastAction)
2757  {
2758  if (LastAction->GetPropertyStr(P_EndCall))
2759  {
2760  C4Def *pOldDef = Def;
2761  Call(LastAction->GetPropertyStr(P_EndCall)->GetCStr());
2762  // abort exeution if def changed
2763  if (Def != pOldDef || !Status) return true;
2764  }
2765  }
2766  // Execute AbortCall for last action
2767  if (iCalls & SAC_AbortCall && !fForce)
2768  if (LastAction)
2769  {
2770  if (LastAction->GetPropertyStr(P_AbortCall))
2771  {
2772  C4Def *pOldDef = Def;
2773  if (pLastTarget && !pLastTarget->Status) pLastTarget = nullptr;
2774  if (pLastTarget2 && !pLastTarget2->Status) pLastTarget2 = nullptr;
2775  Call(LastAction->GetPropertyStr(P_AbortCall)->GetCStr(), &C4AulParSet(iLastPhase, pLastTarget, pLastTarget2));
2776  // abort exeution if def changed
2777  if (Def != pOldDef || !Status) return true;
2778  }
2779  }
2780  // Execute StartCall for new action
2781  if (iCalls & SAC_StartCall)
2782  if (Act)
2783  {
2784  if (Act->GetPropertyStr(P_StartCall))
2785  {
2786  C4Def *pOldDef = Def;
2788  // abort exeution if def changed
2789  if (Def != pOldDef || !Status) return true;
2790  }
2791  }
2792 
2793  C4Def *pOldDef = Def;
2794  Call(PSF_OnActionChanged, &C4AulParSet(LastAction ? LastAction->GetName() : "Idle"));
2795  if (Def != pOldDef || !Status) return true;
2796 
2797  return true;
2798 }
2799 
2801 {
2802  // Default: no action face
2803  Action.Facet.Default();
2804  // Active: get action facet from action definition
2805  C4PropList* pActionDef = GetAction();
2806  if (pActionDef)
2807  {
2808  if (pActionDef->GetPropertyInt(P_Wdt)>0)
2809  {
2810  Action.Facet.Set(GetGraphics()->GetBitmap(Color),
2811  pActionDef->GetPropertyInt(P_X),pActionDef->GetPropertyInt(P_Y),
2812  pActionDef->GetPropertyInt(P_Wdt),pActionDef->GetPropertyInt(P_Hgt));
2813  Action.FacetX=pActionDef->GetPropertyInt(P_OffX);
2814  Action.FacetY=pActionDef->GetPropertyInt(P_OffY);
2815  }
2816  }
2817 }
2818 
2820  C4Object *pTarget, C4Object *pTarget2,
2821  int32_t iCalls, bool fForce)
2822 {
2823  assert(ActName);
2824  // If we get the null string or ActIdle by name, set ActIdle
2825  if (!ActName || ActName == &Strings.P[P_Idle])
2826  return SetAction(nullptr,nullptr,nullptr,iCalls,fForce);
2827  C4Value ActMap; GetProperty(P_ActMap, &ActMap);
2828  if (!ActMap.getPropList()) return false;
2829  C4Value Action; ActMap.getPropList()->GetPropertyByS(ActName, &Action);
2830  if (!Action.getPropList()) return false;
2831  return SetAction(Action.getPropList(),pTarget,pTarget2,iCalls,fForce);
2832 }
2833 
2834 bool C4Object::SetActionByName(const char * szActName,
2835  C4Object *pTarget, C4Object *pTarget2,
2836  int32_t iCalls, bool fForce)
2837 {
2838  C4String * ActName = Strings.RegString(szActName);
2839  ActName->IncRef();
2840  bool r = SetActionByName(ActName, pTarget, pTarget2, iCalls, fForce);
2841  ActName->DecRef();
2842  return r;
2843 }
2844 
2845 void C4Object::SetDir(int32_t iDir)
2846 {
2847  // Not active
2848  C4PropList* pActionDef = GetAction();
2849  if (!pActionDef) return;
2850  // Invalid direction
2851  if (!Inside<int32_t>(iDir,0,pActionDef->GetPropertyInt(P_Directions)-1)) return;
2852  // Execute turn action
2853  if (iDir != Action.Dir)
2854  if (pActionDef->GetPropertyStr(P_TurnAction))
2855  { SetActionByName(pActionDef->GetPropertyStr(P_TurnAction)); }
2856  // Set dir
2857  Action.Dir=iDir;
2858  // update by flipdir?
2859  if (pActionDef->GetPropertyInt(P_FlipDir))
2860  UpdateFlipDir();
2861  else
2862  Action.DrawDir=iDir;
2863 }
2864 
2865 int32_t C4Object::GetProcedure() const
2866 {
2867  C4PropList* pActionDef = GetAction();
2868  if (!pActionDef) return -1;
2869  return pActionDef->GetPropertyP(P_Procedure);
2870 }
2871 
2872 void GrabLost(C4Object *cObj, C4Object *prev_target)
2873 {
2874  // Grab lost script call on target (quite hacky stuff...)
2875  if (prev_target && prev_target->Status) prev_target->Call(PSF_GrabLost);
2876  // Clear commands down to first PushTo (if any) in command stack
2877  for (C4Command *pCom=cObj->Command; pCom; pCom=pCom->Next)
2878  if (pCom->Next && pCom->Next->Command==C4CMD_PushTo)
2879  {
2880  cObj->ClearCommand(pCom);
2881  break;
2882  }
2883 }
2884 
2885 static void DoGravity(C4Object *cobj);
2886 
2888 {
2889  // Active objects
2890  if (GetAction())
2891  {
2892  int32_t iProcedure = GetProcedure();
2893  C4Object *prev_target = Action.Target;
2894  // Scaling upwards: corner scale
2895  if (iProcedure == DFA_SCALE && Action.ComDir != COMD_Stop && ComDirLike(Action.ComDir, COMD_Up))
2896  if (ObjectActionCornerScale(this)) return;
2897  if (iProcedure == DFA_SCALE && Action.ComDir == COMD_Left && Action.Dir == DIR_Left)
2898  if (ObjectActionCornerScale(this)) return;
2899  if (iProcedure == DFA_SCALE && Action.ComDir == COMD_Right && Action.Dir == DIR_Right)
2900  if (ObjectActionCornerScale(this)) return;
2901  // Scaling and stopped: fall off to side (avoid zuppel)
2902  if ((iProcedure == DFA_SCALE) && (Action.ComDir == COMD_Stop))
2903  {
2904  if (Action.Dir == DIR_Left)
2905  { if (ObjectActionJump(this,itofix(1),Fix0,false)) return; }
2906  else
2907  { if (ObjectActionJump(this,itofix(-1),Fix0,false)) return; }
2908  }
2909  // Pushing: grab loss
2910  if (iProcedure==DFA_PUSH) GrabLost(this, prev_target);
2911  // Else jump
2912  ObjectActionJump(this,xdir,ydir,false);
2913  }
2914  // Inactive objects, simple mobile natural gravity
2915  else
2916  {
2917  DoGravity(this);
2918  Mobile=true;
2919  }
2920 }
2921 
2923 {
2924  // Take certain action on contact. Evaluate t_contact-CNAT and Procedure.
2925 
2926  // Determine Procedure
2927  C4PropList* pActionDef = GetAction();
2928  if (!pActionDef) return;
2929  int32_t iProcedure=pActionDef->GetPropertyP(P_Procedure);
2930  int32_t fDisabled=pActionDef->GetPropertyInt(P_ObjectDisabled);
2931 
2932  //------------------------------- Hit Bottom ---------------------------------------------
2933  if (t_contact & CNAT_Bottom)
2934  switch (iProcedure)
2935  {
2936  case DFA_FLIGHT:
2937  if (ydir < 0) return;
2938  // Jump: FlatHit / HardHit / Walk
2939  if ((OCF & OCF_HitSpeed4) || fDisabled)
2940  if (ObjectActionFlat(this,Action.Dir)) return;
2941  if (OCF & OCF_HitSpeed3)
2942  if (ObjectActionKneel(this)) return;
2943  ObjectActionWalk(this);
2944  ydir = 0;
2945  return;
2946  case DFA_SCALE:
2947  // Scale down: stand
2948  if (ComDirLike(Action.ComDir, COMD_Down))
2949  {
2950  ObjectActionStand(this);
2951  return;
2952  }
2953  break;
2954  case DFA_DIG:
2955  // no special action
2956  break;
2957  case DFA_SWIM:
2958  // Try corner scale out
2959  if (!GBackSemiSolid(GetX(),GetY()-1+Def->Float*Con/FullCon-1))
2960  if (ObjectActionCornerScale(this)) return;
2961  break;
2962  }
2963 
2964  //------------------------------- Hit Ceiling -----------------------------------------
2965  if (t_contact & CNAT_Top)
2966  switch (iProcedure)
2967  {
2968  case DFA_WALK:
2969  // Walk: Stop
2970  ObjectActionStand(this); return;
2971  case DFA_SCALE:
2972  // Scale: Try hangle, else stop if going upward
2973  if (ComDirLike(Action.ComDir, COMD_Up))
2974  {
2975  if (ObjectActionHangle(this))
2976  {
2977  SetDir(Action.Dir == DIR_Left ? DIR_Right : DIR_Left);
2978  return;
2979  }
2980  Action.ComDir=COMD_Stop;
2981  }
2982  break;
2983  case DFA_FLIGHT:
2984  // Jump: Try hangle, else bounce off
2985  // High Speed Flight: Tumble
2986  if ((OCF & OCF_HitSpeed3) || fDisabled)
2987  { ObjectActionTumble(this, Action.Dir, xdir, ydir); break; }
2988  if (ObjectActionHangle(this)) return;
2989  break;
2990  case DFA_DIG:
2991  // No action
2992  break;
2993  case DFA_HANGLE:
2994  Action.ComDir=COMD_Stop;
2995  break;
2996  }
2997 
2998  //---------------------------- Hit Left Wall ----------------------------------------
2999  if (t_contact & CNAT_Left)
3000  {
3001  switch (iProcedure)
3002  {
3003  case DFA_FLIGHT:
3004  // High Speed Flight: Tumble
3005  if ((OCF & OCF_HitSpeed3) || fDisabled)
3006  { ObjectActionTumble(this, DIR_Left, xdir, ydir); break; }
3007  // Else
3008  else if (!ComDirLike(Action.ComDir, COMD_Right) && ObjectActionScale(this,DIR_Left)) return;
3009  break;
3010  case DFA_WALK:
3011  // Walk: Try scale
3012  if (ComDirLike(Action.ComDir, COMD_Left))
3013  {
3014  if (ObjectActionScale(this,DIR_Left))
3015  {
3016  ydir = C4REAL100(-1);
3017  return;
3018  }
3019  }
3020  // Heading away from solid
3021  if (ComDirLike(Action.ComDir, COMD_Right))
3022  {
3023  // Slide off
3024  ObjectActionJump(this,xdir/2,ydir,false);
3025  }
3026  return;
3027  case DFA_SWIM:
3028  // Only scale if swimming at the surface
3029  if (!GBackSemiSolid(GetX(),GetY()-1+Def->Float*Con/FullCon-1))
3030  {
3031  // Try scale, only if swimming at the surface.
3032  if (ComDirLike(Action.ComDir, COMD_Left))
3033  if (ObjectActionScale(this,DIR_Left)) return;
3034  // Try corner scale out
3035  if (ObjectActionCornerScale(this)) return;
3036  }
3037  return;
3038  case DFA_HANGLE:
3039  // Hangle: Try scale
3040  if (ObjectActionScale(this,DIR_Left))
3041  {
3042  ydir = C4REAL100(1);
3043  return;
3044  }
3045  return;
3046  case DFA_DIG:
3047  // Dig: no action
3048  break;
3049  }
3050  }
3051 
3052  //------------------------------ Hit Right Wall --------------------------------------
3053  if (t_contact & CNAT_Right)
3054  {
3055  switch (iProcedure)
3056  {
3057  case DFA_FLIGHT:
3058  // High Speed Flight: Tumble
3059  if ((OCF & OCF_HitSpeed3) || fDisabled)
3060  { ObjectActionTumble(this, DIR_Right, xdir, ydir); break; }
3061  // Else Scale
3062  else if (!ComDirLike(Action.ComDir, COMD_Left) && ObjectActionScale(this,DIR_Right)) return;
3063  break;
3064  case DFA_WALK:
3065  // Walk: Try scale
3066  if (ComDirLike(Action.ComDir, COMD_Right))
3067  {
3068  if (ObjectActionScale(this,DIR_Right))
3069  {
3070  ydir = C4REAL100(-1);
3071  return;
3072  }
3073  }
3074  // Heading away from solid
3075  if (ComDirLike(Action.ComDir, COMD_Left))
3076  {
3077  // Slide off
3078  ObjectActionJump(this,xdir/2,ydir,false);
3079  }
3080  return;
3081  case DFA_SWIM:
3082  // Only scale if swimming at the surface
3083  if (!GBackSemiSolid(GetX(),GetY()-1+Def->Float*Con/FullCon-1))
3084  {
3085  // Try scale
3086  if (ComDirLike(Action.ComDir, COMD_Right))
3087  if (ObjectActionScale(this,DIR_Right)) return;
3088  // Try corner scale out
3089  if (ObjectActionCornerScale(this)) return;
3090  }
3091  return;
3092  case DFA_HANGLE:
3093  // Hangle: Try scale
3094  if (ObjectActionScale(this,DIR_Right))
3095  {
3096  ydir = C4REAL100(1);
3097  return;
3098  }
3099  return;
3100  case DFA_DIG:
3101  // Dig: no action
3102  break;
3103  }
3104  }
3105 
3106  //---------------------------- Unresolved Cases ---------------------------------------
3107 
3108  // Flight stuck
3109  if (iProcedure==DFA_FLIGHT)
3110  {
3111  // Enforce slide free (might slide through tiny holes this way)
3112  if (!ydir)
3113  {
3114  int fAllowDown = !(t_contact & CNAT_Bottom);
3115  if (t_contact & CNAT_Right)
3116  {
3117  ForcePosition(fix_x - 1, fix_y + fAllowDown);
3118  xdir=ydir=0;
3119  }
3120  if (t_contact & CNAT_Left)
3121  {
3122  ForcePosition(fix_x + 1, fix_y + fAllowDown);
3123  xdir=ydir=0;
3124  }
3125  }
3126  if (!xdir)
3127  {
3128  if (t_contact & CNAT_Top)
3129  {
3130  ForcePosition(fix_x, fix_y + 1);
3131  xdir=ydir=0;
3132  }
3133  }
3134  }
3135 }
3136 
3137 void Towards(C4Real &val, C4Real target, C4Real step)
3138 {
3139  if (val==target) return;
3140  if (Abs(val-target)<=step) { val=target; return; }
3141  if (val<target) val+=step; else val-=step;
3142 }
3143 
3144 bool DoBridge(C4Object *clk)
3145 {
3146  int32_t iBridgeTime; bool fMoveClonk, fWall; int32_t iBridgeMaterial;
3147  clk->Action.GetBridgeData(iBridgeTime, fMoveClonk, fWall, iBridgeMaterial);
3148  if (!iBridgeTime) iBridgeTime = 100; // default bridge time
3149  if (clk->Action.Time>=iBridgeTime) { ObjectActionStand(clk); return false; }
3150  // get bridge advancement
3151  int32_t dtp;
3152  if (fWall) switch (clk->Action.ComDir)
3153  {
3154  case COMD_Left: case COMD_Right: dtp = 4; fMoveClonk = false; break; // vertical wall: default 25 pixels
3155  case COMD_UpLeft: case COMD_UpRight: dtp = 5; fMoveClonk = false; break; // diagonal roof over Clonk: default 20 pixels up and 20 pixels side (28 pixels - optimized to close tunnels completely)
3156  case COMD_Up: dtp = 5; break; // horizontal roof over Clonk
3157  default: return true; // bridge procedure just for show
3158  }
3159  else switch (clk->Action.ComDir)
3160  {
3161  case COMD_Left: case COMD_Right: dtp = 5; break; // horizontal bridges: default 20 pixels
3162  case COMD_Up: dtp = 4; break; // vertical bridges: default 25 pixels (same as
3163  case COMD_UpLeft: case COMD_UpRight: dtp = 6; break; // diagonal bridges: default 16 pixels up and 16 pixels side (23 pixels)
3164  default: return true; // bridge procedure just for show
3165  }
3166  if (clk->Action.Time % dtp) return true; // no advancement in this frame
3167  // get target pos for Clonk and bridge
3168  int32_t cx=clk->GetX(), cy=clk->GetY(), cw=clk->Shape.Wdt, ch=clk->Shape.Hgt;
3169  int32_t tx=cx,ty=cy+ch/2;
3170  int32_t dt;
3171  if (fMoveClonk) dt = 0; else dt = clk->Action.Time / dtp;
3172  if (fWall) switch (clk->Action.ComDir)
3173  {
3174  case COMD_Left: tx-=cw/2; ty+=-dt; break;
3175  case COMD_Right: tx+=cw/2; ty+=-dt; break;
3176  case COMD_Up:
3177  {
3178  int32_t x0;
3179  if (fMoveClonk) x0=-3; else x0=(iBridgeTime/dtp)/-2;
3180  tx+=(x0+dt)*((clk->Action.Dir==DIR_Right)*2-1); cx+=((clk->Action.Dir==DIR_Right)*2-1); ty-=ch+3; break;
3181  }
3182  case COMD_UpLeft: tx-=-4+dt; ty+=-ch-7+dt; break;
3183  case COMD_UpRight: tx+=-4+dt; ty+=-ch-7+dt; break;
3184  }
3185  else switch (clk->Action.ComDir)
3186  {
3187  case COMD_Left: tx+=-3-dt; --cx; break;
3188  case COMD_Right: tx+=+2+dt; ++cx; break;
3189  case COMD_Up: tx+=(-cw/2+(cw-1)*(clk->Action.Dir==DIR_Right))*(!fMoveClonk); ty+=-dt-fMoveClonk; --cy; break;
3190  case COMD_UpLeft: tx+=-5-dt+fMoveClonk*3; ty+=2-dt-fMoveClonk*3; --cx; --cy; break;
3191  case COMD_UpRight: tx+=+5+dt-fMoveClonk*2; ty+=2-dt-fMoveClonk*3; ++cx; --cy; break;
3192  }
3193  // check if Clonk movement is posible
3194  if (fMoveClonk)
3195  {
3196  int32_t cx2=cx, cy2=cy;
3197  if ( clk->Shape.CheckContact(cx2, cy2-1))
3198  {
3199  // Clonk would collide here: Change to nonmoving Clonk mode and redo bridging
3200  iBridgeTime -= clk->Action.Time;
3201  clk->Action.Time = 0;
3202  if (fWall && clk->Action.ComDir==COMD_Up)
3203  {
3204  // special for roof above Clonk: The nonmoving roof is started at bridgelength before the Clonk
3205  // so, when interrupted, an action time halfway through the action must be set
3206  clk->Action.Time = iBridgeTime;
3207  iBridgeTime += iBridgeTime;
3208  }
3209  clk->Action.SetBridgeData(iBridgeTime, false, fWall, iBridgeMaterial);
3210  return DoBridge(clk);
3211  }
3212  }
3213  // draw bridge into landscape
3214  ::Landscape.DrawMaterialRect(iBridgeMaterial,tx-2,ty,4,3);
3215  // Move Clonk
3216  if (fMoveClonk) clk->MovePosition(cx-clk->GetX(), cy-clk->GetY());
3217  return true;
3218 }
3219 
3220 static void DoGravity(C4Object *cobj)
3221 {
3222  // Floatation in liquids
3223  if (cobj->InLiquid && cobj->Def->Float)
3224  {
3225  cobj->ydir-=GravAccel * C4REAL100(80);
3226  if (cobj->ydir<C4REAL100(-160)) cobj->ydir=C4REAL100(-160);
3227  if (cobj->xdir<-FloatFriction) cobj->xdir+=FloatFriction;
3228  if (cobj->xdir>+FloatFriction) cobj->xdir-=FloatFriction;
3229  if (cobj->rdir<-FloatFriction) cobj->rdir+=FloatFriction;
3230  if (cobj->rdir>+FloatFriction) cobj->rdir-=FloatFriction;
3231  // Reduce upwards speed when about to leave liquid to prevent eternal moving up and down.
3232  // Check both for no liquid one and two pixels above the surface, because the object could
3233  // skip one pixel as its speed can be more than 100.
3234  int32_t y_float = cobj->GetY() - 1 + cobj->Def->Float * cobj->GetCon() / FullCon;
3235  if (!GBackLiquid(cobj->GetX(), y_float - 1) || !GBackLiquid(cobj->GetX(), y_float - 2))
3236  if (cobj->ydir < 0)
3237  {
3238  // Reduce the upwards speed and set to zero for small values to prevent fluctuations.
3239  cobj->ydir /= 2;
3240  if (Abs(cobj->ydir) < C4REAL100(10))
3241  cobj->ydir = 0;
3242  }
3243  }
3244  // Free fall gravity
3245  else if (~cobj->Category & C4D_StaticBack)
3246  cobj->ydir+=GravAccel;
3247 }
3248 
3250 {
3251  ObjectComStop(cobj);
3252  cobj->AddCommand(C4CMD_Wait,nullptr,0,0,50);
3253 }
3254 
3255 bool ReduceLineSegments(C4Shape &rShape, bool fAlternate)
3256 {
3257  // try if line could go by a path directly when skipping on evertex. If fAlternate is true, try by skipping two vertices
3258  for (int32_t cnt=0; cnt+2+fAlternate<rShape.VtxNum; cnt++)
3259  if (PathFree(rShape.VtxX[cnt],rShape.VtxY[cnt],
3260  rShape.VtxX[cnt+2+fAlternate],rShape.VtxY[cnt+2+fAlternate]))
3261  {
3262  if (fAlternate) rShape.RemoveVertex(cnt+2);
3263  rShape.RemoveVertex(cnt+1);
3264  return true;
3265  }
3266  return false;
3267 }
3268 
3270 {
3271  C4Real iTXDir;
3272  C4Real lftspeed,tydir;
3273  int32_t iTargetX;
3274  int32_t iPushRange,iPushDistance;
3275 
3276  // Standard phase advance
3277  int32_t iPhaseAdvance=1;
3278 
3279  // Upright attachment check
3280  if (!Mobile)
3281  if (Def->UprightAttach)
3282  if (Inside<int32_t>(GetR(),-StableRange,+StableRange))
3283  {
3284  Action.t_attach|=Def->UprightAttach;
3285  Mobile=true;
3286  }
3287 
3288  C4PropList* pActionDef = GetAction();
3289  // No IncompleteActivity? Reset action if there was one
3290  if (!(OCF & OCF_FullCon) && !Def->IncompleteActivity && pActionDef)
3291  {
3292  SetAction(nullptr);
3293  pActionDef = nullptr;
3294  }
3295 
3296  // InLiquidAction check
3297  if (InLiquid)
3298  if (pActionDef && pActionDef->GetPropertyStr(P_InLiquidAction))
3299  {
3300  SetActionByName(pActionDef->GetPropertyStr(P_InLiquidAction));
3301  pActionDef = GetAction();
3302  }
3303 
3304  // Idle objects do natural gravity only
3305  if (!pActionDef)
3306  {
3307  Action.t_attach = CNAT_None;
3308  if (Mobile) DoGravity(this);
3309  return;
3310  }
3311 
3312  C4Real fWalk,fMove;
3313 
3314  // Action time advance
3315  Action.Time++;
3316 
3317  C4Value Attach;
3318  pActionDef->GetProperty(P_Attach, &Attach);
3319  if (Attach.GetType() != C4V_Nil)
3320  {
3321  Action.t_attach = Attach.getInt();
3322  }
3323  else switch (pActionDef->GetPropertyP(P_Procedure))
3324  {
3325  case DFA_SCALE:
3326  if (Action.Dir == DIR_Left) Action.t_attach = CNAT_Left;
3327  if (Action.Dir == DIR_Right) Action.t_attach = CNAT_Right;
3328  break;
3329  case DFA_HANGLE:
3330  Action.t_attach = CNAT_Top;
3331  break;
3332  case DFA_WALK:
3333  case DFA_KNEEL:
3334  case DFA_THROW:
3335  case DFA_BRIDGE:
3336  case DFA_PUSH:
3337  case DFA_PULL:
3338  case DFA_DIG:
3339  Action.t_attach = CNAT_Bottom;
3340  break;
3341  default:
3342  Action.t_attach = CNAT_None;
3343  }
3344 
3345  // if an object is in controllable state, so it can be assumed that if it dies later because of NO_OWNER's cause,
3346  // it has been its own fault and not the fault of the last one who threw a flint on it
3347  // do not reset for burning objects to make sure the killer is set correctly if they fall out of the map while burning
3348  if (!pActionDef->GetPropertyInt(P_ObjectDisabled) && pActionDef->GetPropertyP(P_Procedure) != DFA_FLIGHT && !OnFire)
3349  LastEnergyLossCausePlayer = NO_OWNER;
3350 
3351  // Handle Default Action Procedure: evaluates Procedure and Action.ComDir
3352  // Update xdir,ydir,Action.Dir,attachment,iPhaseAdvance
3353  int32_t dir = Action.Dir;
3354  C4Real accel = C4REAL100(pActionDef->GetPropertyInt(P_Accel));
3355  C4Real decel = accel;
3356  {
3357  C4Value decel_val;
3358  pActionDef->GetProperty(P_Decel, &decel_val);
3359  if (decel_val.GetType() != C4V_Nil)
3360  decel = C4REAL100(decel_val.getInt());
3361  }
3362  C4Real limit = C4REAL100(pActionDef->GetPropertyInt(P_Speed));
3363 
3364  switch (pActionDef->GetPropertyP(P_Procedure))
3365  {
3366  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3367  case DFA_WALK:
3368  switch (Action.ComDir)
3369  {
3370  case COMD_Left: case COMD_UpLeft: case COMD_DownLeft:
3371  // breaaak!!!
3372  if (dir == DIR_Right)
3373  xdir-=decel;
3374  else
3375  xdir-=accel;
3376  if (xdir<-limit) xdir=-limit;
3377  break;
3378  case COMD_Right: case COMD_UpRight: case COMD_DownRight:
3379  if (dir == DIR_Left)
3380  xdir+=decel;
3381  else
3382  xdir+=accel;
3383  if (xdir>+limit) xdir=+limit;
3384  break;
3385  case COMD_Stop: case COMD_Up: case COMD_Down:
3386  if (xdir<0) xdir+=decel;
3387  if (xdir>0) xdir-=decel;
3388  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
3389  break;
3390  }
3391  iPhaseAdvance=0;
3392  if (xdir<0)
3393  {
3394  if (dir != DIR_Left) { SetDir(DIR_Left); xdir = -1; }
3395  iPhaseAdvance=-fixtoi(xdir*10);
3396  }
3397  if (xdir>0)
3398  {
3399  if (dir != DIR_Right) { SetDir(DIR_Right); xdir = 1; }
3400  iPhaseAdvance=+fixtoi(xdir*10);
3401  }
3402 
3403  Mobile=true;
3404  // object is rotateable? adjust to ground, if in horizontal movement or not attached to the center vertex
3405  if (Def->Rotateable && Shape.AttachMat != MNone && (!!xdir || Def->Shape.VtxX[Shape.iAttachVtx]))
3406  AdjustWalkRotation(20, 20, 100);
3407  else
3408  rdir=0;
3409  break;
3410  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3411  case DFA_KNEEL:
3412  ydir=0;
3413  Mobile=true;
3414  break;
3415  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3416  case DFA_SCALE:
3417  {
3418  int ComDir = Action.ComDir;
3419  if (Shape.CheckScaleToWalk(GetX(), GetY()))
3420  {
3421  ObjectActionWalk(this);
3422  return;
3423  }
3424  if ((Action.Dir == DIR_Left && ComDir == COMD_Left) || (Action.Dir == DIR_Right && ComDir == COMD_Right))
3425  {
3426  ComDir = COMD_Up;
3427  }
3428  switch (ComDir)
3429  {
3430  case COMD_Up: case COMD_UpRight: case COMD_UpLeft:
3431  if (ydir > 0) ydir -= decel;
3432  else ydir -= accel;
3433  if (ydir < -limit) ydir = -limit; break;
3434  case COMD_Down: case COMD_DownRight: case COMD_DownLeft:
3435  if (ydir < 0) ydir += decel;
3436  else ydir += accel;
3437  if (ydir > +limit) ydir = +limit; break;
3438  case COMD_Left: case COMD_Right: case COMD_Stop:
3439  if (ydir < 0) ydir += decel;
3440  if (ydir > 0) ydir -= decel;
3441  if ((ydir > -decel) && (ydir < +decel)) ydir = 0;
3442  break;
3443  }
3444  iPhaseAdvance=0;
3445  if (ydir<0) iPhaseAdvance=-fixtoi(ydir*14);
3446  if (ydir>0) iPhaseAdvance=+fixtoi(ydir*14);
3447  xdir=0;
3448  Mobile=true;
3449  break;
3450  }
3451  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3452  case DFA_HANGLE:
3453  switch (Action.ComDir)
3454  {
3455  case COMD_Left: case COMD_UpLeft: case COMD_DownLeft:
3456  if (xdir > 0) xdir -= decel;
3457  else xdir -= accel;
3458  if (xdir < -limit) xdir = -limit;
3459  break;
3460  case COMD_Right: case COMD_UpRight: case COMD_DownRight:
3461  if (xdir < 0) xdir += decel;
3462  else xdir += accel;
3463  if (xdir > +limit) xdir = +limit;
3464  break;
3465  case COMD_Up:
3466  if (Action.Dir == DIR_Left)
3467  if (xdir > 0) xdir -= decel;
3468  else xdir -= accel;
3469  else
3470  if (xdir < 0) xdir += decel;
3471  else xdir += accel;
3472  if (xdir < -limit) xdir = -limit;
3473  if (xdir > +limit) xdir = +limit;
3474  break;
3475  case COMD_Stop: case COMD_Down:
3476  if (xdir < 0) xdir += decel;
3477  if (xdir > 0) xdir -= decel;
3478  if ((xdir > -decel) && (xdir < +decel)) xdir = 0;
3479  break;
3480  }
3481  iPhaseAdvance=0;
3482  if (xdir<0) { iPhaseAdvance=-fixtoi(xdir*10); SetDir(DIR_Left); }
3483  if (xdir>0) { iPhaseAdvance=+fixtoi(xdir*10); SetDir(DIR_Right); }
3484  ydir=0;
3485  Mobile=true;
3486  break;
3487  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3488  case DFA_FLIGHT:
3489  // Contained: fall out (one try only)
3490  if (!::Game.iTick10)
3491  if (Contained)
3492  {
3493  StopActionDelayCommand(this);
3494  SetCommand(C4CMD_Exit);
3495  }
3496 
3497  switch (Action.ComDir)
3498  {
3499  case COMD_Left: case COMD_UpLeft: case COMD_DownLeft:
3500  xdir -= std::max(std::min(limit + xdir, xdir > 0 ? decel : accel), itofix(0));
3501  break;
3502  case COMD_Right: case COMD_UpRight: case COMD_DownRight:
3503  xdir += std::max(std::min(limit - xdir, xdir < 0 ? decel : accel), itofix(0));
3504  break;
3505  }
3506 
3507  // Gravity/mobile
3508  DoGravity(this);
3509  Mobile=true;
3510  break;
3511  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3512  case DFA_DIG:
3513  {
3514  int32_t smpx = GetX(), smpy = GetY();
3515  bool fAttachOK = false;
3516  if (Action.t_attach & CNAT_Bottom && Shape.Attach(smpx,smpy,CNAT_Bottom)) fAttachOK = true;
3517  else if (Action.t_attach & CNAT_Left && Shape.Attach(smpx,smpy,CNAT_Left)) { fAttachOK = true; }
3518  else if (Action.t_attach & CNAT_Right && Shape.Attach(smpx,smpy,CNAT_Right)) { fAttachOK = true; }
3519  else if (Action.t_attach & CNAT_Top && Shape.Attach(smpx,smpy,CNAT_Top)) fAttachOK = true;
3520  if (!fAttachOK)
3521  { ObjectComStopDig(this); return; }
3522  iPhaseAdvance=fixtoi(itofix(40)*limit);
3523 
3524  if (xdir < 0) SetDir(DIR_Left); else if (xdir > 0) SetDir(DIR_Right);
3525  Action.t_attach=CNAT_None;
3526  Mobile=true;
3527  break;
3528  }
3529  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3530  case DFA_SWIM:
3531  // ComDir changes xdir/ydir
3532  switch (Action.ComDir)
3533  {
3534  case COMD_Up: ydir-=accel; break;
3535  case COMD_UpRight: ydir-=accel; xdir+=accel; break;
3536  case COMD_Right: xdir+=accel; break;
3537  case COMD_DownRight:ydir+=accel; xdir+=accel; break;
3538  case COMD_Down: ydir+=accel; break;
3539  case COMD_DownLeft: ydir+=accel; xdir-=accel; break;
3540  case COMD_Left: xdir-=accel; break;
3541  case COMD_UpLeft: ydir-=accel; xdir-=accel; break;
3542  case COMD_Stop:
3543  if (xdir<0) xdir+=decel;
3544  if (xdir>0) xdir-=decel;
3545  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
3546  if (ydir<0) ydir+=decel;
3547  if (ydir>0) ydir-=decel;
3548  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
3549  break;
3550  }
3551 
3552  // Out of liquid check
3553  if (!InLiquid)
3554  {
3555  // Just above liquid: move down
3556  if (GBackLiquid(GetX(),GetY()+1+Def->Float*Con/FullCon-1)) ydir=+accel;
3557  // Free fall: walk
3558  else { ObjectActionWalk(this); return; }
3559  }
3560 
3561  // xdir/ydir bounds, don't apply if COMD_None
3562  if (Action.ComDir != COMD_None)
3563  {
3564  if (ydir<-limit) ydir=-limit; if (ydir>+limit) ydir=+limit;
3565  if (xdir>+limit) xdir=+limit; if (xdir<-limit) xdir=-limit;
3566  }
3567  // Surface dir bound
3568  if (!GBackLiquid(GetX(),GetY()-1+Def->Float*Con/FullCon-1)) if (ydir<0) ydir=0;
3569  // Dir, Phase, Attach
3570  if (xdir<0) SetDir(DIR_Left);
3571  if (xdir>0) SetDir(DIR_Right);
3572  iPhaseAdvance=fixtoi(limit*10);
3573  Mobile=true;
3574 
3575  break;
3576  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3577  case DFA_THROW:
3578  Mobile=true;
3579  break;
3580  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3581  case DFA_BRIDGE:
3582  {
3583  if (!DoBridge(this)) return;
3584  switch (Action.ComDir)
3585  {
3586  case COMD_Left: case COMD_UpLeft: SetDir(DIR_Left); break;
3587  case COMD_Right: case COMD_UpRight: SetDir(DIR_Right); break;
3588  }
3589  ydir=0; xdir=0;
3590  Mobile=true;
3591  }
3592  break;
3593  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3594  case DFA_PUSH:
3595  // No target
3596  if (!Action.Target) { StopActionDelayCommand(this); return; }
3597  // Inside target
3598  if (Contained==Action.Target) { StopActionDelayCommand(this); return; }
3599  // Target pushing force
3600  bool fStraighten;
3601  iTXDir=0; fStraighten=false;
3602  switch (Action.ComDir)
3603  {
3604  case COMD_Left: case COMD_DownLeft: iTXDir=-limit; break;
3605  case COMD_UpLeft: fStraighten=true; iTXDir=-limit; break;
3606  case COMD_Right: case COMD_DownRight: iTXDir=+limit; break;
3607  case COMD_UpRight: fStraighten=true; iTXDir=+limit; break;
3608  case COMD_Up: fStraighten=true; break;
3609  case COMD_Stop: case COMD_Down: iTXDir=0; break;
3610  }
3611  // Push object
3612  if (!Action.Target->Push(iTXDir,accel,fStraighten))
3613  { StopActionDelayCommand(this); return; }
3614  // Set target controller
3615  Action.Target->Controller=Controller;
3616  // ObjectAction got hold check
3617  iPushDistance = std::max(Shape.Wdt/2-8,0);
3618  iPushRange = iPushDistance + 10;
3619  int32_t sax,say,sawdt,sahgt;
3620  Action.Target->GetArea(sax,say,sawdt,sahgt);
3621  // Object lost
3622  if (!Inside(GetX()-sax,-iPushRange,sawdt-1+iPushRange)
3623  || !Inside(GetY()-say,-iPushRange,sahgt-1+iPushRange))
3624  {
3625  C4Object *prev_target = Action.Target;
3626  // Wait command (why, anyway?)
3627  StopActionDelayCommand(this);
3628  // Grab lost action
3629  GrabLost(this, prev_target);
3630  // Done
3631  return;
3632  }
3633  // Follow object (full xdir reset)
3634  // Vertical follow: If object moves out at top, assume it's being pushed upwards and the Clonk must run after it
3635  if (GetY()-iPushDistance > say+sahgt && iTXDir) { if (iTXDir>0) sax+=sawdt/2; sawdt/=2; }
3636  // Horizontal follow
3637  iTargetX=Clamp(GetX(),sax-iPushDistance,sax+sawdt-1+iPushDistance);
3638  if (GetX()==iTargetX) xdir=0;
3639  else { if (GetX()<iTargetX) xdir=+limit; if (GetX()>iTargetX) xdir=-limit; }
3640  // Phase by XDir
3641  iPhaseAdvance=0;
3642  if (xdir<0) { iPhaseAdvance=-fixtoi(xdir*10); SetDir(DIR_Left); }
3643  if (xdir>0) { iPhaseAdvance=+fixtoi(xdir*10); SetDir(DIR_Right); }
3644  // No YDir
3645  ydir=0;
3646  Mobile=true;
3647  break;
3648  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3649  case DFA_PULL:
3650  // No target
3651  if (!Action.Target) { StopActionDelayCommand(this); return; }
3652  // Inside target
3653  if (Contained==Action.Target) { StopActionDelayCommand(this); return; }
3654  // Target contained
3655  if (Action.Target->Contained) { StopActionDelayCommand(this); return; }
3656 
3657  int32_t iPullDistance;
3658  int32_t iPullX;
3659 
3660  iPullDistance = Action.Target->Shape.Wdt/2 + Shape.Wdt/2;
3661 
3662  iTargetX=GetX();
3663  if (Action.ComDir==COMD_Right) iTargetX = Action.Target->GetX()+iPullDistance;
3664  if (Action.ComDir==COMD_Left) iTargetX = Action.Target->GetX()-iPullDistance;
3665 
3666  iPullX=Action.Target->GetX();
3667  if (Action.ComDir==COMD_Right) iPullX = GetX()-iPullDistance;
3668  if (Action.ComDir==COMD_Left) iPullX = GetX()+iPullDistance;
3669 
3670  fWalk = limit;
3671 
3672  fMove = 0;
3673  if (Action.ComDir==COMD_Right) fMove = +fWalk;
3674  if (Action.ComDir==COMD_Left) fMove = -fWalk;
3675 
3676  iTXDir = fMove + fWalk * Clamp<int32_t>(iPullX-Action.Target->GetX(),-10,+10) / 10;
3677 
3678  // Push object
3679  if (!Action.Target->Push(iTXDir,accel,false))
3680  { StopActionDelayCommand(this); return; }
3681  // Set target controller
3682  Action.Target->Controller=Controller;
3683 
3684  // Train pulling: com dir transfer
3685  if ( (Action.Target->GetProcedure()==DFA_WALK)
3686  || (Action.Target->GetProcedure()==DFA_PULL) )
3687  {
3688  Action.Target->Action.ComDir=COMD_Stop;
3689  if (iTXDir<0) Action.Target->Action.ComDir=COMD_Left;
3690  if (iTXDir>0) Action.Target->Action.ComDir=COMD_Right;
3691  }
3692 
3693  // Pulling range
3694  iPushDistance = std::max(Shape.Wdt/2-8,0);
3695  iPushRange = iPushDistance + 20;
3696  Action.Target->GetArea(sax,say,sawdt,sahgt);
3697  // Object lost
3698  if (!Inside(GetX()-sax,-iPushRange,sawdt-1+iPushRange)
3699  || !Inside(GetY()-say,-iPushRange,sahgt-1+iPushRange))
3700  {
3701  // Remember target. Will be lost on changing action.
3702  C4Object *prev_target = Action.Target;
3703  // Wait command (why, anyway?)
3704  StopActionDelayCommand(this);
3705  // Grab lost action
3706  GrabLost(this, prev_target);
3707  // Lose target
3708  Action.Target=nullptr;
3709  // Done
3710  return;
3711  }
3712 
3713  // Move to pulling position
3714  xdir = fMove + fWalk * Clamp<int32_t>(iTargetX-GetX(),-10,+10) / 10;
3715 
3716  // Phase by XDir
3717  iPhaseAdvance=0;
3718  if (xdir<0) { iPhaseAdvance=-fixtoi(xdir*10); SetDir(DIR_Left); }
3719  if (xdir>0) { iPhaseAdvance=+fixtoi(xdir*10); SetDir(DIR_Right); }
3720  // No YDir
3721  ydir=0;
3722  Mobile=true;
3723 
3724  break;
3725  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3726  case DFA_LIFT:
3727  // Valid check
3728  if (!Action.Target) { SetAction(nullptr); return; }
3729  // Target lifting force
3730  lftspeed=itofix(2); tydir=0;
3731  switch (Action.ComDir)
3732  {
3733  case COMD_Up: tydir=-lftspeed; break;
3734  case COMD_Stop: tydir=-GravAccel; break;
3735  case COMD_Down: tydir=+lftspeed; break;
3736  }
3737  // Lift object
3738  if (!Action.Target->Lift(tydir,C4REAL100(50)))
3739  { SetAction(nullptr); return; }
3740  // Check LiftTop
3741  if (Def->LiftTop)
3742  if (Action.Target->GetY()<=(GetY()+Def->LiftTop))
3743  if (Action.ComDir==COMD_Up)
3744  Call(PSF_LiftTop);
3745  // General
3746  DoGravity(this);
3747  break;
3748  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3749  case DFA_FLOAT:
3750  // ComDir changes xdir/ydir
3751  switch (Action.ComDir)
3752  {
3753  case COMD_Up:
3754  ydir-=accel;
3755  if (xdir<0) xdir+=decel;
3756  if (xdir>0) xdir-=decel;
3757  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
3758  break;
3759  case COMD_UpRight:
3760  ydir-=accel; xdir+=accel; break;
3761  case COMD_Right:
3762  xdir+=accel;
3763  if (ydir<0) ydir+=decel;
3764  if (ydir>0) ydir-=decel;
3765  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
3766  break;
3767  case COMD_DownRight:
3768  ydir+=accel; xdir+=accel; break;
3769  case COMD_Down:
3770  ydir+=accel;
3771  if (xdir<0) xdir+=decel;
3772  if (xdir>0) xdir-=decel;
3773  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
3774  break;
3775  case COMD_DownLeft:
3776  ydir+=accel; xdir-=accel; break;
3777  case COMD_Left:
3778  xdir-=accel;
3779  if (ydir<0) ydir+=decel;
3780  if (ydir>0) ydir-=decel;
3781  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
3782  break;
3783  case COMD_UpLeft:
3784  ydir-=accel; xdir-=accel; break;
3785  case COMD_Stop:
3786  if (xdir<0) xdir+=decel;
3787  if (xdir>0) xdir-=decel;
3788  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
3789  if (ydir<0) ydir+=decel;
3790  if (ydir>0) ydir-=decel;
3791  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
3792  break;
3793  }
3794  // xdir/ydir bounds, don't apply if COMD_None
3795  if (Action.ComDir != COMD_None)
3796  {
3797  if (ydir<-limit) ydir=-limit; if (ydir>+limit) ydir=+limit;
3798  if (xdir>+limit) xdir=+limit; if (xdir<-limit) xdir=-limit;
3799  }
3800 
3801  Mobile=true;
3802  break;
3803  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3804  // ATTACH: Force position to target object
3805  // own vertex index is determined by high-order byte of action data
3806  // target vertex index is determined by low-order byte of action data
3807  case DFA_ATTACH:
3808 
3809  // No target
3810  if (!Action.Target)
3811  {
3812  if (Status)
3813  {
3814  SetAction(nullptr);
3816  }
3817  return;
3818  }
3819 
3820  // Target incomplete and no incomplete activity
3821  if (!(Action.Target->OCF & OCF_FullCon))
3822  if (!Action.Target->Def->IncompleteActivity)
3823  { SetAction(nullptr); return; }
3824 
3825  // Force containment
3826  if (Action.Target->Contained!=Contained)
3827  {
3828  if (Action.Target->Contained)
3829  Enter(Action.Target->Contained);
3830  else
3831  Exit(GetX(),GetY(),GetR());
3832  }
3833 
3834  // Object might have detached in Enter/Exit call
3835  if (!Action.Target) break;
3836 
3837  // Move position (so objects on solidmask move)
3838  MovePosition(Action.Target->fix_x + Action.Target->Shape.VtxX[Action.Data&255]
3839  -Shape.VtxX[Action.Data>>8] - fix_x,
3840  Action.Target->fix_y + Action.Target->Shape.VtxY[Action.Data&255]
3841  -Shape.VtxY[Action.Data>>8] - fix_y);
3842  // must zero motion...
3843  xdir=ydir=0;
3844 
3845  break;
3846  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3847  case DFA_CONNECT:
3848  {
3849  bool fBroke=false;
3850  bool fLineChange = false;
3851 
3852  // Line destruction check: Target missing or incomplete
3853  if (!Action.Target || (Action.Target->Con<FullCon)) fBroke=true;
3854  if (!Action.Target2 || (Action.Target2->Con<FullCon)) fBroke=true;
3855  if (fBroke)
3856  {
3858  AssignRemoval();
3859  return;
3860  }
3861 
3862  // Movement by Target
3863  // Connect to attach vertex
3864  C4Value lineAttachV; C4ValueArray *lineAttach;
3865  Action.Target->GetProperty(P_LineAttach, &lineAttachV);
3866  lineAttach = lineAttachV.getArray();
3867  int32_t iConnectX1, iConnectY1;
3868  iConnectX1 = Action.Target->GetX();
3869  iConnectY1 = Action.Target->GetY();
3870  if (lineAttach)
3871  {
3872  iConnectX1 += lineAttach->GetItem(0).getInt();
3873  iConnectY1 += lineAttach->GetItem(1).getInt();
3874  }
3875  if ((iConnectX1!=Shape.VtxX[0]) || (iConnectY1!=Shape.VtxY[0]))
3876  {
3877  // Regular wrapping line
3878  if (Def->LineIntersect == 0)
3879  if (!Shape.LineConnect(iConnectX1,iConnectY1,0,+1,
3880  Shape.VtxX[0],Shape.VtxY[0])) fBroke=true;
3881  // No-intersection line
3882  if (Def->LineIntersect == 1)
3883  { Shape.VtxX[0]=iConnectX1; Shape.VtxY[0]=iConnectY1; }
3884 
3885  fLineChange = true;
3886  }
3887 
3888  // Movement by Target2
3889  // Connect to attach vertex
3890  Action.Target2->GetProperty(P_LineAttach, &lineAttachV);
3891  lineAttach = lineAttachV.getArray();
3892  int32_t iConnectX2, iConnectY2;
3893  iConnectX2 = Action.Target2->GetX();
3894  iConnectY2 = Action.Target2->GetY();
3895  if (lineAttach)
3896  {
3897  iConnectX2 += lineAttach->GetItem(0).getInt();
3898  iConnectY2 += lineAttach->GetItem(1).getInt();
3899  }
3900  if ((iConnectX2!=Shape.VtxX[Shape.VtxNum-1]) || (iConnectY2!=Shape.VtxY[Shape.VtxNum-1]))
3901  {
3902  // Regular wrapping line
3903  if (Def->LineIntersect == 0)
3904  if (!Shape.LineConnect(iConnectX2,iConnectY2,Shape.VtxNum-1,-1,
3905  Shape.VtxX[Shape.VtxNum-1],Shape.VtxY[Shape.VtxNum-1])) fBroke=true;
3906  // No-intersection line
3907  if (Def->LineIntersect == 1)
3908  { Shape.VtxX[Shape.VtxNum-1]=iConnectX2; Shape.VtxY[Shape.VtxNum-1]=iConnectY2; }
3909 
3910  fLineChange = true;
3911  }
3912 
3913  // Line fBroke
3914  if (fBroke)
3915  {
3916  Call(PSF_OnLineBreak,nullptr);
3917  AssignRemoval();
3918  return;
3919  }
3920 
3921  // Reduce line segments
3922  if (!::Game.iTick35)
3923  if (ReduceLineSegments(Shape, !::Game.iTick2))
3924  fLineChange = true;
3925 
3926  // Line change callback
3927  if (fLineChange)
3929  }
3930  break;
3931  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3932  default:
3933  // Attach
3934  if (Action.t_attach)
3935  {
3936  xdir = ydir = 0;
3937  Mobile = true;
3938  }
3939  // Free gravity
3940  else
3941  DoGravity(this);
3942  break;
3943  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3944  }
3945 
3946  // Phase Advance (zero delay means no phase advance)
3947  if (pActionDef->GetPropertyInt(P_Delay))
3948  {
3949  Action.PhaseDelay+=iPhaseAdvance;
3950  if (Action.PhaseDelay >= pActionDef->GetPropertyInt(P_Delay))
3951  {
3952  // Advance Phase
3953  Action.PhaseDelay=0;
3954  Action.Phase += pActionDef->GetPropertyInt(P_Step);
3955  // Phase call
3956  if (pActionDef->GetPropertyStr(P_PhaseCall))
3957  {
3958  Call(pActionDef->GetPropertyStr(P_PhaseCall)->GetCStr());
3959  }
3960  // Phase end
3961  if (Action.Phase>=pActionDef->GetPropertyInt(P_Length))
3962  {
3963  C4String *next_action = pActionDef->GetPropertyStr(P_NextAction);
3964  // Keep current action if there is no NextAction
3965  if (!next_action)
3966  Action.Phase = 0;
3967  // set new action if it's not Hold
3968  else if (next_action == &Strings.P[P_Hold])
3969  {
3970  Action.Phase = pActionDef->GetPropertyInt(P_Length)-1;
3971  Action.PhaseDelay = pActionDef->GetPropertyInt(P_Delay)-1;
3972  }
3973  else
3974  {
3975  // Set new action
3976  SetActionByName(next_action, nullptr, nullptr, SAC_StartCall | SAC_EndCall);
3977  }
3978  }
3979  }
3980  }
3981 
3982  return;
3983 }
3984 
3985 
3986 bool C4Object::SetOwner(int32_t iOwner)
3987 {
3988  // Check valid owner
3989  if (!(ValidPlr(iOwner) || iOwner == NO_OWNER)) return false;
3990  // always set color, even if no owner-change is done
3991  if (iOwner != NO_OWNER)
3992  if (GetGraphics()->IsColorByOwner())
3993  {
3994  Color=::Players.Get(iOwner)->ColorDw;
3995  UpdateFace(false);
3996  }
3997  // no change?
3998  if (Owner == iOwner) return true;
3999  // set new owner
4000  int32_t iOldOwner=Owner;
4001  Owner=iOwner;
4002  // this automatically updates controller
4003  Controller = Owner;
4004  // script callback
4005  Call(PSF_OnOwnerChanged, &C4AulParSet(Owner, iOldOwner));
4006  // done
4007  return true;
4008 }
4009 
4010 
4011 bool C4Object::SetLightRange(int32_t iToRange, int32_t iToFadeoutRange)
4012 {
4013  // set new range
4014  lightRange = iToRange;
4015  lightFadeoutRange = iToFadeoutRange;
4016  // resort into player's FoW-repeller-list
4017  UpdateLight();
4018  // success
4019  return true;
4020 }
4021 
4022 
4023 bool C4Object::SetLightColor(uint32_t iValue)
4024 {
4025  // set new color value
4026  lightColor = iValue;
4027  // resort into player's FoW-repeller-list
4028  UpdateLight();
4029  // success
4030  return true;
4031 }
4032 
4033 
4035 {
4036  if (Landscape.HasFoW()) Landscape.GetFoW()->Add(this);
4037 }
4038 
4039 void C4Object::SetAudibilityAt(C4TargetFacet &cgo, int32_t iX, int32_t iY, int32_t player)
4040 {
4041  // target pos (parallax)
4042  float offX, offY, newzoom;
4043  GetDrawPosition(cgo, iX, iY, cgo.Zoom, offX, offY, newzoom);
4044  int32_t audible_at_pos = Clamp(100 - 100 * Distance(cgo.X + cgo.Wdt / 2, cgo.Y + cgo.Hgt / 2, offX, offY) / 700, 0, 100);
4045  if (audible_at_pos > Audible)
4046  {
4047  Audible = audible_at_pos;
4048  AudiblePan = Clamp<int>(200 * (offX - cgo.X - (cgo.Wdt / 2)) / cgo.Wdt, -100, 100);
4049  AudiblePlayer = player;
4050  }
4051 }
4052 
4053 bool C4Object::IsVisible(int32_t iForPlr, bool fAsOverlay) const
4054 {
4055  bool fDraw;
4056  C4Value vis;
4057  if (!GetProperty(P_Visibility, &vis))
4058  return true;
4059 
4060  int32_t Visibility;
4061  C4ValueArray *parameters = vis.getArray();
4062  if (parameters && parameters->GetSize())
4063  {
4064  Visibility = parameters->GetItem(0).getInt();
4065  }
4066  else
4067  {
4068  Visibility = vis.getInt();
4069  }
4070  // check layer
4071  if (Layer && Layer != this && !fAsOverlay)
4072  {
4073  fDraw = Layer->IsVisible(iForPlr, false);
4074  if (Layer->GetPropertyInt(P_Visibility) & VIS_LayerToggle) fDraw = !fDraw;
4075  if (!fDraw) return false;
4076  }
4077  // no flags set?
4078  if (!Visibility) return true;
4079  // check overlay
4080  if (Visibility & VIS_OverlayOnly)
4081  {
4082  if (!fAsOverlay) return false;
4083  if (Visibility == VIS_OverlayOnly) return true;
4084  }
4085  // editor visibility
4086  if (::Application.isEditor)
4087  {
4088  if (Visibility & VIS_Editor) return true;
4089  }
4090  // check visibility
4091  fDraw=false;
4092  if (Visibility & VIS_Owner) fDraw = fDraw || (iForPlr==Owner);
4093  if (iForPlr!=NO_OWNER)
4094  {
4095  // check all
4096  if (Visibility & VIS_Allies) fDraw = fDraw || (iForPlr!=Owner && !Hostile(iForPlr, Owner));
4097  if (Visibility & VIS_Enemies) fDraw = fDraw || (iForPlr!=Owner && Hostile(iForPlr, Owner));
4098  if (parameters)
4099  {
4100  if (Visibility & VIS_Select) fDraw = fDraw || parameters->GetItem(1+iForPlr).getBool();
4101  }
4102  }
4103  else fDraw = fDraw || (Visibility & VIS_God);
4104  return fDraw;
4105 }
4106 
4108 {
4109  return GBackLiquid(GetX(),GetY()+Def->Float*Con/FullCon-1);
4110 }
4111 
4112 void C4Object::SetRotation(int32_t nr)
4113 {
4114  while (nr<0) nr+=360; nr%=360;
4115  // remove solid mask
4116  if (pSolidMaskData) pSolidMaskData->Remove(false);
4117  // set rotation
4118  fix_r=itofix(nr);
4119  // Update face
4120  UpdateFace(true);
4121 }
4122 
4124 {
4125  // color modulation
4126  if (ColorMod != 0xffffffff || (BlitMode & (C4GFXBLIT_MOD2 | C4GFXBLIT_CLRSFC_MOD2))) pDraw->ActivateBlitModulation(ColorMod);
4127  // other blit modes
4128  pDraw->SetBlitMode(BlitMode);
4129 }
4130 
4132 {
4133  // color modulation
4135  // extra blitting flags
4136  pDraw->ResetBlitMode();
4137 }
4138 
4140 {
4141  // mask must exist
4142  if (!pSolidMaskData) return;
4143  // draw it
4144  pSolidMaskData->Draw(cgo);
4145 }
4146 
4147 void C4Object::UpdateSolidMask(bool fRestoreAttachedObjects)
4148 {
4149  // solidmask doesn't make sense with non-existant objects
4150  // (the solidmask has already been destroyed in AssignRemoval -
4151  // do not reset it!)
4152  if (!Status) return;
4153  // Determine necessity, update cSolidMask, put or remove mask
4154  // Mask if enabled, fullcon, not contained
4155  if (SolidMask.Wdt > 0 && Con >= FullCon && !Contained)
4156  {
4157  // Recheck and put mask
4158  if (!pSolidMaskData)
4159  {
4160  pSolidMaskData = new C4SolidMask(this);
4161  }
4162  else
4163  pSolidMaskData->Remove(false);
4164  pSolidMaskData->Put(true, nullptr, fRestoreAttachedObjects);
4165  SetHalfVehicleSolidMask(HalfVehicleSolidMask);
4166  }
4167  // Otherwise, remove and destroy mask
4168  else if (pSolidMaskData)
4169  {
4170  delete pSolidMaskData; pSolidMaskData = nullptr;
4171  }
4172 }
4173 
4175 {
4176  // Object enter container
4177  bool fRejectCollect;
4178  if (!pObj->Enter(this, true, false, &fRejectCollect))
4179  return false;
4180  // Cancel attach (hacky)
4181  ObjectComCancelAttach(pObj);
4182  // Container Collection call
4184  // Object Hit call
4185  if (pObj->Status && pObj->OCF & OCF_HitSpeed1) pObj->Call(PSF_Hit);
4186  if (pObj->Status && pObj->OCF & OCF_HitSpeed2) pObj->Call(PSF_Hit2);
4187  if (pObj->Status && pObj->OCF & OCF_HitSpeed3) pObj->Call(PSF_Hit3);
4188  // post-copy the motion of the new container
4189  if (pObj->Contained == this) pObj->CopyMotion(this);
4190  // done, success
4191  return true;
4192 }
4193 
4195 {
4196  // safety
4197  if (!pFrom) return false; if (!Status || !pFrom->Status) return false;
4198  // even more safety (own info: success)
4199  if (pFrom == this) return true;
4200  // only if other object has info
4201  if (!pFrom->Info) return false;
4202  // clear own info object
4203  if (Info)
4204  {
4205  Info->Retire();
4206  ClearInfo (Info);
4207  }
4208  // remove objects from any owning crews
4209  ::Players.ClearPointers(pFrom);
4210  ::Players.ClearPointers(this);
4211  // set info
4212  Info = pFrom->Info; pFrom->ClearInfo (pFrom->Info);
4213  // set name
4214  SetName(Info->Name);
4215  // retire from old crew
4216  Info->Retire();
4217  // if alive, recruit to new crew
4218  if (Alive) Info->Recruit();
4219  // make new crew member
4220  C4Player *pPlr = ::Players.Get(Owner);
4221  if (pPlr) pPlr->MakeCrewMember(this);
4222  // done, success
4223  return true;
4224 }
4225 
4226 bool C4Object::ShiftContents(bool fShiftBack, bool fDoCalls)
4227 {
4228  // get current object
4229  C4Object *c_obj = Contents.GetObject();
4230  if (!c_obj) return false;
4231  // get next/previous
4232  auto it = fShiftBack ? Contents.reverse().begin() : ++Contents.begin();
4233  while (!it.atEnd())
4234  {
4235  auto pObj = (*it);
4236  // check object
4237  if (pObj->Status)
4238  if (!c_obj->CanConcatPictureWith(pObj))
4239  {
4240  // object different: shift to this
4241  DirectComContents(pObj, !!fDoCalls);
4242  return true;
4243  }
4244  // next/prev item
4245  it++;
4246  }
4247  return false;
4248 }
4249 
4250 void C4Object::DirectComContents(C4Object *pTarget, bool fDoCalls)
4251 {
4252  // safety
4253  if (!pTarget || !pTarget->Status || pTarget->Contained != this) return;
4254  // Desired object already at front?
4255  if (Contents.GetObject() == pTarget) return;
4256  // select object via script?
4257  if (fDoCalls)
4258  if (Call("~ControlContents", &C4AulParSet(pTarget)))
4259  return;
4260  // default action
4261  if (!(Contents.ShiftContents(pTarget))) return;
4262  // Selection sound
4263  if (fDoCalls) if (!Contents.GetObject()->Call("~Selection", &C4AulParSet(this))) StartSoundEffect("Clonk::Action::Grab",false,100,this);
4264  // update menu with the new item in "put" entry
4265  if (Menu && Menu->IsActive() && Menu->IsContextMenu())
4266  {
4267  Menu->Refill();
4268  }
4269  // Done
4270  return;
4271 }
4272 
4273 void C4Object::GetParallaxity(int32_t *parX, int32_t *parY) const
4274 {
4275  assert(parX); assert(parY);
4276  *parX = 100; *parY = 100;
4277  if (Category & C4D_Foreground)
4278  {
4279  *parX = 0; *parY = 0;
4280  return;
4281  }
4282  if (!(Category & C4D_Parallax)) return;
4283  C4Value parV; GetProperty(P_Parallaxity, &parV);
4284  C4ValueArray *par = parV.getArray();
4285  if (!par) return;
4286  *parX = par->GetItem(0).getInt();
4287  *parY = par->GetItem(1).getInt();
4288 }
4289 
4290 bool C4Object::GetDragImage(C4Object **drag_object, C4Def **drag_def) const
4291 {
4292  // drag is possible if MouseDragImage is assigned
4293  C4Value parV; GetProperty(P_MouseDragImage, &parV);
4294  if (!parV) return false;
4295  // determine drag object/id
4296  C4Object *obj = parV.getObj();
4297  C4Def * def = nullptr;
4298  if (!obj) def = parV.getDef();
4299  if (drag_object) *drag_object = obj;
4300  if (drag_def) *drag_def = def;
4301  // drag possible, even w./o image
4302  return true;
4303 }
4304 
4305 int32_t C4Object::AddObjectAndContentsToArray(C4ValueArray *target_array, int32_t index)
4306 {
4307  // add self, contents and child contents count recursively to value array. Return index after last added item.
4308  target_array->SetItem(index++, C4VObj(this));
4309  for (C4Object *cobj : Contents)
4310  {
4311  index = cobj->AddObjectAndContentsToArray(target_array, index);
4312  }
4313  return index;
4314 }
4315 
4317 {
4318  // selection allowed?
4319  if (CrewDisabled) return false;
4320  // do callback
4322  // done
4323  return true;
4324 }
4325 
4327 {
4328  // do callback
4330 }
4331 
4332 void C4Object::GetViewPos(float & riX, float & riY, float tx, float ty, const C4Facet & fctViewport) const // get position this object is seen at (for given scroll)
4333 {
4334  if (Category & C4D_Parallax) GetViewPosPar(riX, riY, tx, ty, fctViewport); else { riX = float(GetX()); riY = float(GetY()); }
4335 }
4336 
4338  float & resultx, float & resulty, float & resultzoom) const
4339 {
4340  return GetDrawPosition(cgo, fixtof(fix_x), fixtof(fix_y), cgo.Zoom, resultx, resulty, resultzoom);
4341 }
4342 
4343 bool C4Object::GetDrawPosition(const C4TargetFacet & cgo, float objx, float objy, float zoom, float & resultx, float & resulty, float & resultzoom) const
4344 {
4345  // for HUD
4346  if(Category & C4D_Foreground)
4347  {
4348  resultzoom = zoom;
4349 
4350  if(fix_x < 0)
4351  resultx = cgo.X + objx + cgo.Wdt;
4352  else
4353  resultx = cgo.X + objx;
4354 
4355  if(fix_y < 0)
4356  resulty = cgo.Y + objy + cgo.Hgt;
4357  else
4358  resulty = cgo.Y + objy;
4359 
4360  return true;
4361  }
4362 
4363  // zoom with parallaxity
4364  int iParX, iParY;
4365  GetParallaxity(&iParX, &iParY);
4366  float targetx = cgo.TargetX; float targety = cgo.TargetY;
4367  float parx = iParX / 100.0f; float pary = iParY / 100.0f;
4368  float par = parx; // and pary?
4369 
4370  // Step 1: project to landscape coordinates
4371  resultzoom = 1.0 / (1.0 - (par - par/zoom));
4372  // it would be par / (1.0 - (par - par/zoom)) if objects would get smaller farther away
4373  if (resultzoom <= 0 || resultzoom > 100) // FIXME: optimize treshhold
4374  return false;
4375 
4376  float rx = ((1 - parx) * cgo.ParRefX) * resultzoom + objx / (parx + zoom - parx * zoom);
4377  float ry = ((1 - pary) * cgo.ParRefY) * resultzoom + objy / (pary + zoom - pary * zoom);
4378 
4379  // Step 2: convert to screen coordinates
4380  if(parx == 0 && fix_x < 0)
4381  resultx = cgo.X + (objx + cgo.Wdt) * zoom / resultzoom;
4382  else
4383  resultx = cgo.X + (rx - targetx) * zoom / resultzoom;
4384 
4385  if(pary == 0 && fix_y < 0)
4386  resulty = cgo.Y + (objy + cgo.Hgt) * zoom / resultzoom;
4387  else
4388  resulty = cgo.Y + (ry - targety) * zoom / resultzoom;
4389 
4390  return true;
4391 }
4392 
4393 void C4Object::GetViewPosPar(float &riX, float &riY, float tx, float ty, const C4Facet &fctViewport) const
4394 {
4395  int iParX, iParY;
4396  GetParallaxity(&iParX, &iParY);
4397  // get drawing pos, then subtract original target pos to get drawing pos on landscape
4398  if (!iParX && GetX()<0)
4399  // HUD element at right viewport pos
4400  riX=fixtof(fix_x)+tx+fctViewport.Wdt;
4401  else
4402  // regular parallaxity
4403  riX=fixtof(fix_x)-(tx*(iParX-100)/100);
4404  if (!iParY && GetY()<0)
4405  // HUD element at bottom viewport pos
4406  riY=fixtof(fix_y)+ty+fctViewport.Hgt;
4407  else
4408  // regular parallaxity
4409  riY=fixtof(fix_y)-(ty*(iParY-100)/100);
4410 }
4411 
4412 bool C4Object::PutAwayUnusedObject(C4Object *pToMakeRoomForObject)
4413 {
4414  // get unused object
4415  C4Object *pUnusedObject;
4416  C4AulFunc *pFnObj2Drop = GetFunc(PSF_GetObject2Drop);
4417  if (pFnObj2Drop)
4418  pUnusedObject = pFnObj2Drop->Exec(this, &C4AulParSet(pToMakeRoomForObject)).getObj();
4419  else
4420  {
4421  // is there any unused object to put away?
4422  if (!Contents.GetLastObject()) return false;
4423  // defaultly, it's the last object in the list
4424  // (contents list cannot have invalid status-objects)
4425  pUnusedObject = Contents.GetLastObject();
4426  }
4427  // no object to put away? fail
4428  if (!pUnusedObject) return false;
4429  // grabbing something?
4430  bool fPushing = (GetProcedure()==DFA_PUSH);
4431  if (fPushing)
4432  // try to put it in there
4433  if (ObjectComPut(this, Action.Target, pUnusedObject))
4434  return true;
4435  // in container? put in there
4436  if (Contained)
4437  {
4438  // try to put it in directly
4439  // note that this works too, if an object is grabbed inside the container
4440  if (ObjectComPut(this, Contained, pUnusedObject))
4441  return true;
4442  // now putting didn't work - drop it outside
4443  AddCommand(C4CMD_Drop, pUnusedObject);
4444  AddCommand(C4CMD_Exit);
4445  return true;
4446  }
4447  else
4448  // if uncontained, simply try to drop it
4449  // if this doesn't work, it won't ever
4450  return !!ObjectComDrop(this, pUnusedObject);
4451 }
4452 
4453 bool C4Object::SetGraphics(const char *szGraphicsName, C4Def *pSourceDef)
4454 {
4455  // safety
4456  if (!Status) return false;
4457  // default def
4458  if (!pSourceDef) pSourceDef = Def;
4459  // get graphics
4460  C4DefGraphics *pGrp = pSourceDef->Graphics.Get(szGraphicsName);
4461  if (!pGrp) return false;
4462  // set new graphics
4463  pGraphics = pGrp;
4464  // update Color, etc.
4465  UpdateGraphics(true);
4466  // success
4467  return true;
4468 }
4469 
4470 bool C4Object::SetGraphics(C4DefGraphics *pNewGfx, bool fTemp)
4471 {
4472  // safety
4473  if (!pNewGfx) return false;
4474  // set it and update related stuff
4475  pGraphics = pNewGfx;
4476  UpdateGraphics(true, fTemp);
4477  return true;
4478 }
4479 
4481 {
4482  // search in list until ID is found or passed
4483  C4GraphicsOverlay *pOverlay = pGfxOverlay;
4484  while (pOverlay && pOverlay->GetID() < iForID) pOverlay = pOverlay->GetNext();
4485  // exact match found?
4486  if (pOverlay && pOverlay->GetID() == iForID) return pOverlay;
4487  // none found
4488  return nullptr;
4489 }
4490 
4491 C4GraphicsOverlay *C4Object::GetGraphicsOverlay(int32_t iForID, bool fCreate)
4492 {
4493  // search in list until ID is found or passed
4494  C4GraphicsOverlay *pOverlay = pGfxOverlay, *pPrevOverlay = nullptr;
4495  while (pOverlay && pOverlay->GetID() < iForID) { pPrevOverlay = pOverlay; pOverlay = pOverlay->GetNext(); }
4496  // exact match found?
4497  if (pOverlay && pOverlay->GetID() == iForID) return pOverlay;
4498  // ID has been passed: Create new if desired
4499  if (!fCreate) return nullptr;
4500  C4GraphicsOverlay *pNewOverlay = new C4GraphicsOverlay();
4501  pNewOverlay->SetID(iForID);
4502  pNewOverlay->SetNext(pOverlay);
4503  if (pPrevOverlay) pPrevOverlay->SetNext(pNewOverlay); else pGfxOverlay = pNewOverlay;
4504  // return newly created overlay
4505  return pNewOverlay;
4506 }
4507 
4508 bool C4Object::RemoveGraphicsOverlay(int32_t iOverlayID)
4509 {
4510  // search in list until ID is found or passed
4511  C4GraphicsOverlay *pOverlay = pGfxOverlay, *pPrevOverlay = nullptr;
4512  while (pOverlay && pOverlay->GetID() < iOverlayID) { pPrevOverlay = pOverlay; pOverlay = pOverlay->GetNext(); }
4513  // exact match found?
4514  if (pOverlay && pOverlay->GetID() == iOverlayID)
4515  {
4516  // remove it
4517  if (pPrevOverlay) pPrevOverlay->SetNext(pOverlay->GetNext()); else pGfxOverlay = pOverlay->GetNext();
4518  pOverlay->SetNext(nullptr); // prevents deletion of following overlays
4519  delete pOverlay;
4520  // removed
4521  return true;
4522  }
4523  // no match found
4524  return false;
4525 }
4526 
4528 {
4529  C4Object *pGfxOvrlObj;
4530  if (pGfxOverlay)
4531  for (C4GraphicsOverlay *pGfxOvrl = pGfxOverlay; pGfxOvrl; pGfxOvrl = pGfxOvrl->GetNext())
4532  if ((pGfxOvrlObj = pGfxOvrl->GetOverlayObject()))
4533  {
4534  if (pGfxOvrlObj == pCheckObj) return true;
4535  if (pGfxOvrlObj->HasGraphicsOverlayRecursion(pCheckObj)) return true;
4536  }
4537  return false;
4538 }
4539 
4541 {
4542  // readd to main list
4544  Status = C4OS_NORMAL;
4545  ::Objects.Add(this);
4546  // update some values
4547  UpdateGraphics(false);
4548  UpdateFace(true);
4549  UpdatePos();
4550  UpdateLight();
4552  // done, success
4553  return true;
4554 }
4555 
4556 bool C4Object::StatusDeactivate(bool fClearPointers)
4557 {
4558  // clear particles
4559  ClearParticleLists();
4560 
4561  // put into inactive list
4562  ::Objects.Remove(this);
4563  Status = C4OS_INACTIVE;
4564  if (Landscape.HasFoW()) Landscape.GetFoW()->Remove(this);
4566  // if desired, clear game pointers
4567  if (fClearPointers)
4568  {
4569  // in this case, the object must also exit any container, and any contained objects must be exited
4570  ClearContentsAndContained();
4571  Game.ClearPointers(this);
4572  }
4573  else
4574  {
4575  // always clear transfer
4577  }
4578  // done, success
4579  return true;
4580 }
4581 
4583 {
4584  // exit contents from container
4585  for (C4Object *cobj : Contents)
4586  {
4587  cobj->Exit(GetX(), GetY(), 0,Fix0,Fix0,Fix0, fDoCalls);
4588  }
4589  // remove from container *after* contents have been removed!
4590  if (Contained) Exit(GetX(), GetY(), 0, Fix0, Fix0, Fix0, fDoCalls);
4591 }
4592 
4593 bool C4Object::AdjustWalkRotation(int32_t iRangeX, int32_t iRangeY, int32_t iSpeed)
4594 {
4595  int32_t iDestAngle;
4596  // attachment at middle (bottom) vertex?
4597  if (Shape.iAttachVtx<0 || !Def->Shape.VtxX[Shape.iAttachVtx])
4598  {
4599  // evaluate floor around attachment pos
4600  int32_t iSolidLeft=0, iSolidRight=0;
4601  // left
4602  int32_t iXCheck = Shape.iAttachX-iRangeX;
4603  if (GBackSolid(iXCheck, Shape.iAttachY))
4604  {
4605  // up
4606  while (--iSolidLeft>-iRangeY)
4607  if (GBackSolid(iXCheck, Shape.iAttachY+iSolidLeft))
4608  { ++iSolidLeft; break; }
4609  }
4610  else
4611  // down
4612  while (++iSolidLeft<iRangeY)
4613  if (GBackSolid(iXCheck, Shape.iAttachY+iSolidLeft))
4614  { --iSolidLeft; break; }
4615  // right
4616  iXCheck += 2*iRangeX;
4617  if (GBackSolid(iXCheck, Shape.iAttachY))
4618  {
4619  // up
4620  while (--iSolidRight>-iRangeY)
4621  if (GBackSolid(iXCheck, Shape.iAttachY+iSolidRight))
4622  { ++iSolidRight; break; }
4623  }
4624  else
4625  // down
4626  while (++iSolidRight<iRangeY)
4627  if (GBackSolid(iXCheck, Shape.iAttachY+iSolidRight))
4628  { --iSolidRight; break; }
4629  // calculate destination angle
4630  // 100% accurate for large values of Pi ;)
4631  iDestAngle=(iSolidRight-iSolidLeft)*(35/std::max<int32_t>(iRangeX, 1));
4632  }
4633  else
4634  {
4635  // attachment at other than horizontal middle vertex: get your feet to the ground!
4636  // rotate target to large angle is OK, because rotation will stop once the real
4637  // bottom vertex hits solid ground
4638  if (Shape.VtxX[Shape.iAttachVtx] > 0)
4639  iDestAngle = -50;
4640  else
4641  iDestAngle = 50;
4642  }
4643  // move to destination angle
4644  if (Abs(iDestAngle-GetR())>2)
4645  {
4646  rdir = itofix(Clamp<int32_t>(iDestAngle-GetR(), -15,+15));
4647  rdir/=(10000/iSpeed);
4648  }
4649  else rdir=0;
4650  // done, success
4651  return true;
4652 }
4653 
4654 static void BubbleOut(int32_t tx, int32_t ty)
4655 {
4656  // No bubbles from nowhere
4657  if (!GBackSemiSolid(tx,ty)) return;
4658  // Enough bubbles out there already
4659  if (::Objects.ObjectCount(C4ID::Bubble) >= 150) return;
4660  // Create bubble
4661  Game.CreateObject(C4ID::Bubble,nullptr,NO_OWNER,tx,ty);
4662 }
4663 
4664 void C4Object::Splash()
4665 {
4666  int32_t tx = GetX(); int32_t ty = GetY()+1;
4667  int32_t amt = std::min(Shape.Wdt*Shape.Hgt/10,20);
4668  // Splash only if there is free space above
4669  if (GBackSemiSolid(tx, ty - 15)) return;
4670  // get back mat
4671  int32_t iMat = GBackMat(tx, ty);
4672  // check liquid
4673  if (MatValid(iMat))
4674  if (DensityLiquid(::MaterialMap.Map[iMat].Density) && ::MaterialMap.Map[iMat].Instable)
4675  {
4676  int32_t sy = ty;
4677  while (GBackLiquid(tx, sy) && sy > ty - 20 && sy >= 0) sy--;
4678  // Splash bubbles and liquid
4679  for (int32_t cnt=0; cnt<amt; cnt++)
4680  {
4681  int32_t bubble_x = tx+Random(16)-8;
4682  int32_t bubble_y = ty+Random(16)-6;
4683  BubbleOut(bubble_x,bubble_y);
4684  if (GBackLiquid(tx,ty) && !GBackSemiSolid(tx, sy))
4685  {
4686  C4Real xdir = C4REAL100(Random(151)-75);
4687  C4Real ydir = C4REAL100(-int32_t(Random(200)));
4688  ::PXS.Create(::Landscape.ExtractMaterial(tx,ty,false),
4689  itofix(tx),itofix(sy),
4690  xdir,
4691  ydir);
4692  }
4693  }
4694  }
4695  // Splash sound
4696  if (amt>=20)
4697  StartSoundEffect("Liquids::Splash2", false, 50, this);
4698  else if (amt>1) StartSoundEffect("Liquids::Splash1", false, 50, this);
4699 }
4700 
4702 {
4703  // InLiquid check
4704  if (IsInLiquidCheck()) // In Liquid
4705  {
4706  if (!InLiquid) // Enter liquid
4707  {
4708  if (OCF & OCF_HitSpeed2)
4709  if (Mass>3) Splash();
4710  InLiquid=true;
4711  }
4712  }
4713  else // Out of liquid
4714  {
4715  if (InLiquid) // Leave liquid
4716  InLiquid=false;
4717  }
4718 }
4719 
4721 {
4722  StdStrBuf sResult;
4723  // no info for invalid objects
4724  if (!Status) return sResult;
4725  // go through all effects and add their desc
4726  for (C4Effect *pEff = pEffects; pEff; pEff = pEff->pNext)
4727  {
4728  C4Value par[7];
4729  C4Value vInfo = pEff->DoCall(this, PSFS_FxInfo, par[0], par[1], par[2], par[3], par[4], par[5], par[6]);
4730  if (!vInfo) continue;
4731  // debug: warn for wrong return types
4732  if (vInfo.GetType() != C4V_String)
4733  DebugLogF("Effect %s(#%d) on object %s (#%d) returned wrong info type %d.", pEff->GetName(), pEff->Number, GetName(), Number, vInfo.GetType());
4734  // get string val
4735  C4String *psInfo = vInfo.getStr(); const char *szEffInfo;
4736  if (psInfo && (szEffInfo = psInfo->GetCStr()))
4737  if (*szEffInfo)
4738  {
4739  // OK; this effect has a desc. Add it!
4740  if (sResult.getLength()) sResult.AppendChar('|');
4741  sResult.Append(szEffInfo);
4742  }
4743  }
4744  // done
4745  return sResult;
4746 }
4747 
4749 {
4750  // create a temp list of all objects and transfer it
4751  // this prevents nasty deadlocks caused by RejectEntrance-scripts
4752  C4ObjectList tmpList; tmpList.Copy(pFrom->Contents);
4753  for (C4Object *obj : tmpList)
4754  if (obj->Status)
4755  obj->Enter(this);
4756 }
4757 
4758 bool C4Object::CanConcatPictureWith(C4Object *pOtherObject) const
4759 {
4760  // check current definition ID
4761  if (id != pOtherObject->id) return false;
4762  // def overwrite of stack conditions
4763  int32_t allow_picture_stack = Def->AllowPictureStack;
4764  if (!(allow_picture_stack & APS_Color))
4765  {
4766  // check color if ColorByOwner (flags)
4767  if (Color != pOtherObject->Color && Def->ColorByOwner) return false;
4768  // check modulation
4769  if (ColorMod != pOtherObject->ColorMod) return false;
4770  if (BlitMode != pOtherObject->BlitMode) return false;
4771  }
4772  if (!(allow_picture_stack & APS_Graphics))
4773  {
4774  // check graphics
4775  if (pGraphics != pOtherObject->pGraphics) return false;
4776  // check any own picture rect
4777  if (PictureRect != pOtherObject->PictureRect) return false;
4778  }
4779  if (!(allow_picture_stack & APS_Name))
4780  {
4781  // check name, so zagabar's sandwiches don't stack
4782  if (GetName() != pOtherObject->GetName()) return false;
4783  }
4784  if (!(allow_picture_stack & APS_Overlay))
4785  {
4786  // check overlay graphics
4787  for (C4GraphicsOverlay *pOwnOverlay = pGfxOverlay; pOwnOverlay; pOwnOverlay = pOwnOverlay->GetNext())
4788  if (pOwnOverlay->IsPicture())
4789  {
4790  C4GraphicsOverlay *pOtherOverlay = pOtherObject->GetGraphicsOverlay(pOwnOverlay->GetID(), false);
4791  if (!pOtherOverlay || !(*pOtherOverlay == *pOwnOverlay)) return false;
4792  }
4793  for (C4GraphicsOverlay *pOtherOverlay = pOtherObject->pGfxOverlay; pOtherOverlay; pOtherOverlay = pOtherOverlay->GetNext())
4794  if (pOtherOverlay->IsPicture())
4795  if (!GetGraphicsOverlay(pOtherOverlay->GetID())) return false;
4796  }
4797  // concat OK
4798  return true;
4799 }
4800 
4801 bool C4Object::IsMoveableBySolidMask(int ComparisonPlane) const
4802 {
4803  return (Status == C4OS_NORMAL)
4804  && !(Category & C4D_StaticBack)
4805  && (ComparisonPlane < GetPlane())
4806  && !Contained
4807  ;
4808 }
4809 
4811 {
4812  if (pEffects)
4813  pEffects->ReAssignAllCallbackFunctions();
4814 }
4815 
4816 bool C4Object::IsPlayerObject(int32_t iPlayerNumber) const
4817 {
4818  bool fAnyPlr = (iPlayerNumber == NO_OWNER);
4819  // if an owner is specified: only owned objects
4820  if (fAnyPlr && !ValidPlr(Owner)) return false;
4821  // and crew objects
4822  if (fAnyPlr || Owner == iPlayerNumber)
4823  {
4824  C4Player *pOwner = ::Players.Get(Owner);
4825  if (pOwner)
4826  {
4827  if (pOwner && pOwner->Crew.IsContained(this)) return true;
4828  }
4829  else
4830  {
4831  // Do not force that the owner exists because the function must work for unjoined players (savegame resume)
4832  if (Def->CrewMember)
4833  return true;
4834  }
4835  }
4836  // otherwise, not a player object
4837  return false;
4838 }
4839 
4841 {
4842  // must be a player object at all
4843  if (!IsPlayerObject()) return false;
4844  // and the owner must not be a script player
4845  C4Player *pOwner = ::Players.Get(Owner);
4846  if (!pOwner || pOwner->GetType() != C4PT_User) return false;
4847  // otherwise, it's a user playeer object
4848  return true;
4849 }
4850 
4852 {
4853  if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
4854  {
4855  switch(k - &Strings.P[0])
4856  {
4857  case P_Plane:
4858  if (!to.getInt()) throw C4AulExecError("invalid Plane 0");
4859  SetPlane(to.getInt());
4860  return;
4861  }
4862  }
4864 }
4865 
4867 {
4868  if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
4869  {
4870  switch(k - &Strings.P[0])
4871  {
4872  case P_Plane:
4873  SetPlane(GetPropertyInt(P_Plane));
4874  return;
4875  }
4876  }
4878 }
4879 
4880 bool C4Object::GetPropertyByS(const C4String *k, C4Value *pResult) const
4881 {
4882  if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
4883  {
4884  switch(k - &Strings.P[0])
4885  {
4886  case P_Plane: *pResult = C4VInt(Plane); return true;
4887  }
4888  }
4889  return C4PropListNumbered::GetPropertyByS(k, pResult);
4890 }
4891 
4893 {
4895  int i;
4896  i = a->GetSize();
4897  a->SetSize(i + 1);
4898  (*a)[i++] = C4VString(&::Strings.P[P_Plane]);
4899  return a;
4900 }
4901 
4903 {
4904  // use SolidMaskPlane property. Fallback to object plane if unassigned.
4905  int32_t plane = GetPropertyInt(P_SolidMaskPlane);
4906  return plane ? plane : GetPlane();
4907 }
C4Effect * pNext
Definition: C4Effect.h:75
virtual void ResetProperty(C4String *k)
Definition: C4PropList.cpp:992
#define PSF_RejectEntrance
Definition: C4GameScript.h:132
C4Def * ID2Def(C4ID id)
#define PSF_AttachTargetLost
Definition: C4GameScript.h:85
void SetMeshTransform(const StdMeshMatrix *Transform)
Definition: C4Draw.h:200
const int32_t C4MaxPhysical
Definition: C4InfoCore.h:28
C4Object * getObj() const
Definition: C4Value.cpp:68
const uint32_t OCF_HitSpeed1
Definition: C4Constants.h:84
C4String P[P_LAST]
void ClearPointers(C4Object *pObj)
bool DoSelect()
Definition: C4Object.cpp:4316
#define PSF_OnMaterialChanged
Definition: C4GameScript.h:153
#define VIS_God
Definition: C4Object.h:68
C4Object * cObj
Definition: C4Command.h:80
#define PSF_ControlCommand
Definition: C4GameScript.h:70
void SetFoW(const C4FoWRegion *fow)
Definition: C4Draw.h:193
bool SetLightColor(uint32_t iValue)
Definition: C4Object.cpp:4023
#define COMD_UpRight
Definition: C4Object.h:52
void GrabContents(C4Object *pFrom)
Definition: C4Object.cpp:4748
#define VIS_OverlayOnly
Definition: C4Object.h:70
C4ID id
Definition: C4Def.h:101
void Towards(C4Real &val, C4Real target, C4Real step)
Definition: C4Object.cpp:3137
int32_t getInt() const
Definition: C4Value.h:112
bool DoBridge(C4Object *clk)
Definition: C4Object.cpp:3144
C4Real fy
Definition: C4Record.h:132
void SetItem(int32_t iElemNr, const C4Value &Value)
void SetTransformAt(C4DrawTransform &rCopy, float iOffX, float iOffY)
Definition: C4Facet.cpp:541
int32_t GetX() const
Definition: C4Object.h:287
C4Config Config
Definition: C4Config.cpp:833
int32_t iTick255
Definition: C4Game.h:130
C4String * getStr() const
Definition: C4Value.h:117
const uint32_t OCF_Alive
Definition: C4Constants.h:103
float Y
Definition: C4Facet.h:118
int32_t GetCon() const
Definition: C4Object.h:273
const char * CommandName(int32_t iCommand)
Definition: C4Command.cpp:47
bool Enter(C4Object *pTarget, bool fCalls=true, bool fCopyMotion=true, bool *pfRejectCollect=nullptr)
Definition: C4Object.cpp:1263
C4Real fr
Definition: C4Record.h:132
#define C4GFXBLIT_MOD2
Definition: C4Surface.h:27
C4GraphicsOverlay * GetNext() const
const uint32_t OCF_Entrance
Definition: C4Constants.h:90
#define PSF_Hit
Definition: C4GameScript.h:49
#define C4FxCall_RemoveDeath
Definition: C4Effect.h:46
const uint32_t OCF_Collection
Definition: C4Constants.h:93
C4ID id
Definition: C4Object.h:108
void MovePosition(int32_t dx, int32_t dy)
Definition: C4Movement.cpp:522
float Zoom
Definition: C4Facet.h:165
uint32_t Random()
Definition: C4Random.cpp:43
#define GfxR
virtual bool Add(C4Object *nObj, SortType eSort, C4ObjectList *pLstSorted=nullptr)
void SetHalfVehicleSolidMask(bool set)
Definition: C4Object.cpp:2472
bool Add(C4Object *nObj)
int32_t Time
Definition: C4Object.h:83
int32_t iTick5
Definition: C4Game.h:130
#define COMD_Down
Definition: C4Object.h:55
int Hgt
Definition: CSurface8.h:28
bool MakeCrewMember(C4Object *pObj, bool fForceInfo=true, bool fDoCalls=true)
Definition: C4Player.cpp:1008
bool IsInLiquidCheck() const
Definition: C4Object.cpp:4107
int Wdt
Definition: C4Surface.h:65
const int32_t C4CMD_Mode_Base
Definition: C4Command.h:60
#define PSF_Ejection
Definition: C4GameScript.h:58
#define PSF_Collection2
Definition: C4GameScript.h:57
C4Game Game
Definition: C4Globals.cpp:52
bool ObjectActionKneel(C4Object *cObj)
Definition: C4ObjectCom.cpp:94
bool ObjectComCancelAttach(C4Object *cObj)
C4GameScriptHost GameScript
void IncRef()
Definition: C4StringTable.h:27
C4ScriptUniform scriptUniform
Definition: C4Draw.h:100
C4Value Data
Definition: C4Command.h:85
int32_t Float
Definition: C4Def.h:120
int32_t iTick35
Definition: C4Game.h:130
#define COMD_DownRight
Definition: C4Object.h:54
C4Command * Next
Definition: C4Command.h:90
void SetDir(int32_t tdir)
Definition: C4Object.cpp:2845
int32_t GetValue(C4Object *pInBase, int32_t iForPlayer)
Definition: C4Object.cpp:1690
bool StatusDeactivate(bool fClearPointers)
Definition: C4Object.cpp:4556
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4Scenario C4S
Definition: C4Game.h:74
C4ConfigGeneral General
Definition: C4Config.h:251
#define sprintf
Definition: Standard.h:164
C4ParticleSystem Particles
const uint32_t OCF_NotContained
Definition: C4Constants.h:96
C4String * GetPropertyStr(C4PropertyName k) const
Definition: C4PropList.cpp:775
void ResetBlitMode()
Definition: C4Draw.h:192
StdStrBuf GetData() const
Definition: C4StringTable.h:50
const int StableRange
Definition: C4Physics.h:23
bool IsContained(const C4Object *pObj) const
void Draw(C4TargetFacet &cgo, int32_t iByPlayer=-1, DrawMode eDrawMode=ODM_Normal, float offX=0, float offY=0)
Definition: C4Object.cpp:1788
~C4Object() override
Definition: C4Object.cpp:236
void CompileFunc(StdCompiler *pComp, C4ValueNumbers *)
Definition: C4Object.cpp:2161
#define PSF_EnergyChange
Definition: C4GameScript.h:142
C4Rect PictureRect
Definition: C4Object.h:152
bool CreateContentsByList(C4IDList &idlist)
Definition: C4Object.cpp:1447
void DrawPicture(C4Facet &cgo, bool fSelected=false, C4DrawTransform *transform=nullptr)
Definition: C4Object.cpp:2340
void Denumerate(C4ValueNumbers *) override
Definition: C4Object.cpp:2312
int32_t Instable
Definition: C4Material.h:102
bool MenuCommand(const char *szCommand)
Definition: C4Object.cpp:2455
void SetAudibilityAt(C4TargetFacet &cgo, int32_t iX, int32_t iY, int32_t player)
Definition: C4Object.cpp:4039
bool Replay
Definition: C4Scenario.h:72
const int32_t C4D_VehicleControl_Inside
Definition: C4Def.h:78
bool SetGraphics(const char *szGraphicsName=nullptr, C4Def *pSourceDef=nullptr)
Definition: C4Object.cpp:4453
void AssignDeath(bool fForced)
Definition: C4Object.cpp:991
C4PropList * getPropList() const
Definition: C4Value.h:116
bool Push(C4Real txdir, C4Real dforce, bool fStraighten)
Definition: C4Object.cpp:1362
void DoBreath(int32_t iChange)
Definition: C4Object.cpp:1146
#define DIR_Right
Definition: C4Object.h:42
C4Command * Command
Definition: C4Object.h:167
int32_t GetSolidMaskPlane() const
Definition: C4Object.cpp:4902
C4Material * Map
Definition: C4Material.h:169
#define C4OS_NORMAL
Definition: C4Object.h:35
C4String * RegString(StdStrBuf String)
bool Remove(C4Object *pObj) override
#define VIS_Enemies
Definition: C4Object.h:66
C4Real rdir
Definition: C4Object.h:126
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
bool SetActionByName(C4String *ActName, C4Object *pTarget=nullptr, C4Object *pTarget2=nullptr, int32_t iCalls=SAC_StartCall|SAC_AbortCall, bool fForce=false)
Definition: C4Object.cpp:2819
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val=0) const
Definition: C4PropList.cpp:886
#define C4FxCall_RemoveClear
Definition: C4Effect.h:45
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:131
bool TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, float iTx, float iTy, DWORD dwFCol=0xffffffff, BYTE byForm=ALeft, bool fDoMarkup=true)
Definition: C4Draw.cpp:567
const StdMeshInstance::AttachedMesh::DenumeratorFactoryFunc C4MeshDenumeratorFactory
bool ShiftContents(bool fShiftBack, bool fDoCalls)
Definition: C4Object.cpp:4226
int32_t Ty
Definition: C4Command.h:83
bool HasFoW() const
const uint32_t OCF_Container
Definition: C4Constants.h:102
bool AddCommand(int32_t iCommand, C4Object *pTarget, C4Value iTx, int32_t iTy=0, int32_t iUpdateInterval=0, C4Object *pTarget2=nullptr, bool fInitEvaluation=true, C4Value iData=C4VNull, bool fAppend=false, int32_t iRetries=0, C4String *szText=nullptr, int32_t iBaseMode=0)
Definition: C4Object.cpp:2567
void UpdateInLiquid()
Definition: C4Object.cpp:4701
bool GetProperty(C4PropertyName k, C4Value *pResult) const
Definition: C4PropList.h:101
const uint32_t OCF_InSolid
Definition: C4Constants.h:99
const uint32_t OCF_CrewMember
Definition: C4Constants.h:97
#define GravAccel
Definition: C4Physics.h:27
C4ValueArray * getArray() const
Definition: C4Value.h:118
void DrawClr(C4Facet &cgo, bool fAspect=true, DWORD dwClr=0)
Definition: C4Facet.cpp:213
bool GetPropertyByS(const C4String *k, C4Value *pResult) const override
Definition: C4Object.cpp:4880
C4RankSystem DefaultRanks
void GetViewPosPar(float &riX, float &riY, float tx, float ty, const C4Facet &fctViewport) const
Definition: C4Object.cpp:4393
const uint32_t OCF_Normal
Definition: C4Constants.h:79
Definition: C4Rect.h:27
#define PSF_RejectCollection
Definition: C4GameScript.h:133
class C4FoW * GetFoW()
C4Real C4REAL100(int x)
Definition: C4Real.h:267
uint8_t BYTE
bool GetTextExtent(const char *szText, int32_t &rsx, int32_t &rsy, bool fCheckMarkup=true)
bool ReduceLineSegments(C4Shape &rShape, bool fAlternate)
Definition: C4Object.cpp:3255
const BYTE CNAT_NoCollision
Definition: C4Constants.h:116
int32_t Finished
Definition: C4Command.h:87
void Copy(const C4ObjectList &rList)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
static bool CheckPropList(C4PropList *)
Definition: C4PropList.cpp:80
const int32_t C4FCT_Right
Definition: C4Facet.h:28
#define DEBUGREC_ON
Definition: C4Record.h:29
void StopSoundEffect(const char *szSndName, C4Object *pObj)
C4AulExec AulExec
Definition: C4AulExec.cpp:29
void UpdateOCF()
Definition: C4Object.cpp:760
const C4Real HitSpeed2
Definition: C4Movement.cpp:37
C4Viewport * GetViewport(int32_t iPlayer, C4Viewport *pPrev=nullptr)
void DrawTopFace(C4TargetFacet &cgo, int32_t iByPlayer=-1, DrawMode eDrawMode=ODM_Normal, float offX=0, float offY=0)
Definition: C4Object.cpp:2048
constexpr bool DEBUGREC_OCF
Definition: C4Include.h:35
bool GetDragImage(C4Object **drag_object, C4Def **drag_id) const
Definition: C4Object.cpp:4290
void ClearPointers(C4Object *pObj)
Definition: C4Command.cpp:1289
C4Value Tx
Definition: C4Command.h:82
#define PSF_OnLineBreak
Definition: C4GameScript.h:76
#define a
bool Create(int32_t mat, C4Real ix, C4Real iy, C4Real ixdir=Fix0, C4Real iydir=Fix0)
Definition: C4PXS.cpp:175
void GameMsgObjectError(const char *szText, C4Object *pTarget, bool Red)
void Clear()
Definition: C4Object.cpp:2436
static StdMeshMatrix Rotate(float angle, float rx, float ry, float rz)
void Set(C4Surface &rSfc)
Definition: C4Facet.cpp:459
#define PSF_Entrance
Definition: C4GameScript.h:59
int Hgt
Definition: C4Surface.h:65
C4GraphicsResource GraphicsResource
void DirectComContents(C4Object *pTarget, bool fDoCalls)
Definition: C4Object.cpp:4250
void UpdatLastEnergyLossCause(int32_t iNewCausePlr)
Definition: C4Object.cpp:1135
#define PSF_OnInIncendiaryMaterial
Definition: C4GameScript.h:95
bool ValidateOwner()
Definition: C4Object.cpp:2380
void ClearCommand(C4Command *pUntil)
Definition: C4Object.cpp:2550
#define VIS_Select
Definition: C4Object.h:67
C4ObjectPtr Layer
Definition: C4Object.h:136
BYTE GetEntranceArea(int32_t &aX, int32_t &aY, int32_t &aWdt, int32_t &aHgt) const
Definition: C4Object.cpp:1591
void ResetProperty(C4String *k) override
Definition: C4Object.cpp:4866
const C4FoWRegion * GetFoW() const
Definition: C4Draw.h:194
void SetSolidMask(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iTX, int32_t iTY)
Definition: C4Object.cpp:2462
void ClearParticleLists()
Definition: C4Object.cpp:246
void DrawLineDw(C4Surface *sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:614
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:88
void SetBlitMode(DWORD dwBlitMode)
Definition: C4Draw.h:191
void ClearPointers(C4Object *tptr, bool fDeath)
Definition: C4Player.cpp:98
void Set(const C4Facet &cpy)
Definition: C4FacetEx.h:46
int32_t Angle(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iPrec)
Definition: Standard.cpp:37
virtual C4ValueArray * GetProperties() const
Definition: C4PropList.cpp:914
void DrawFaceImpl(C4TargetFacet &cgo, bool action, float fx, float fy, float fwdt, float fhgt, float tx, float ty, float twdt, float thgt, C4DrawTransform *transform) const
Definition: C4Object.cpp:480
#define PSF_Damage
Definition: C4GameScript.h:63
C4Facet GetFraction(int32_t percentWdt, int32_t percentHgt=0, int32_t alignX=C4FCT_Left, int32_t alignY=C4FCT_Top)
Definition: C4Facet.cpp:555
bool Promote(int32_t torank, bool exception, bool fForceRankName)
Definition: C4Object.cpp:1715
void UpdateGraphics(bool fGraphicsChanged, bool fTemp=false)
Definition: C4Object.cpp:406
StdMeshInstanceAnimationNode * Animation
Definition: C4Object.h:90
void DrawActionFace(C4TargetFacet &cgo, float offX, float offY) const
Definition: C4Object.cpp:577
const int32_t C4D_Grab_Get
Definition: C4Def.h:63
bool AssignLightRange()
Definition: C4Object.cpp:2420
#define PSF_CrewSelection
Definition: C4GameScript.h:86
const uint32_t OCF_FullCon
Definition: C4Constants.h:85
C4ObjectInfo * Info
Definition: C4Object.h:145
C4Object * GetObject() override
Definition: C4Object.h:374
StdStrBuf GetRankName(int iRank, bool fReturnLastIfOver)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
Definition: C4Real.h:58
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
void UpdateScriptPointers()
Definition: C4Object.cpp:4810
C4V_Type GetType() const
Definition: C4Value.h:161
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:190
const int32_t FullCon
Definition: C4Constants.h:181
void GetOCFForPos(int32_t ctx, int32_t cty, DWORD &ocf) const
Definition: C4Object.cpp:975
void Default()
Definition: C4FacetEx.h:43
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
void AppendChar(char cChar)
Definition: StdBuf.h:588
C4Def * getDef() const
Definition: C4Value.cpp:78
int32_t Distance(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2)
Definition: Standard.cpp:25
bool ObjectComStop(C4Object *cObj)
uint32_t ColorDw
Definition: C4Player.h:89
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
void UpdatePos()
Definition: C4Object.cpp:372
C4ObjectList InactiveObjects
Definition: C4GameObjects.h:43
void AssertObjectNotInList(C4Object *pObj)
Definition: C4Sector.cpp:185
class C4GraphicsOverlay * GetGraphicsOverlay(int32_t iForID) const
Definition: C4Object.cpp:4480
bool ObjectComPut(C4Object *cObj, C4Object *pTarget, C4Object *pThing)
static const C4ObjectPtr Null
Definition: C4ObjectPtr.h:28
int32_t Wdt
Definition: C4Rect.h:30
C4DefGraphics * Get(const char *szGrpName)
void DrawFrameDw(C4Surface *sfcDest, int x1, int y1, int x2, int y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:641
virtual void Denumerate(C4ValueNumbers *)
Definition: C4PropList.cpp:350
bool IsMoveableBySolidMask(int ComparisonPlane) const
Definition: C4Object.cpp:4801
const int32_t C4D_IgnoreFoW
Definition: C4Def.h:55
bool Lift(C4Real tydir, C4Real dforce)
Definition: C4Object.cpp:1412
int32_t GetAudibility(int32_t iX, int32_t iY, int32_t *iPan, int32_t iAudibilityRadius=0, int32_t *outPlayer=nullptr)
bool ObjectActionWalk(C4Object *cObj)
Definition: C4ObjectCom.cpp:39
const uint32_t OCF_Inflammable
Definition: C4Constants.h:86
int32_t MWater
Definition: C4Material.cpp:36
void DwTo4UB(DWORD dwClr, unsigned char(&r)[4])
Definition: C4Draw.h:29
bool InLiquid
Definition: C4Object.h:131
C4ID GetID(size_t index, int32_t *ipCount=nullptr) const
Definition: C4IDList.cpp:99
#define PSF_GetObject2Drop
Definition: C4GameScript.h:87
static StdMeshMatrix Identity()
bool At(int32_t ctx, int32_t cty) const
Definition: C4Object.cpp:952
void ExecAction()
Definition: C4Object.cpp:3269
#define PSF_OnLineChange
Definition: C4GameScript.h:77
#define PSF_Initialize
Definition: C4GameScript.h:34
const uint32_t OCF_HitSpeed2
Definition: C4Constants.h:91
bool CloseMenu(bool fForce)
Definition: C4Object.cpp:1573
#define C4FxCall_EngCorrosion
Definition: