OpenClonk
C4ObjectContents.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 /* Logic for C4Object: Contents, containers, collection */
19 
20 #include "C4Include.h"
22 #include "object/C4Object.h"
23 
24 #include "game/C4Physics.h"
25 #include "object/C4Def.h"
26 #include "object/C4DefList.h"
27 #include "object/C4ObjectCom.h"
28 #include "object/C4ObjectMenu.h"
29 #include "platform/C4SoundSystem.h"
30 
31 
32 bool C4Object::Exit(int32_t iX, int32_t iY, int32_t iR, C4Real iXDir, C4Real iYDir, C4Real iRDir, bool fCalls)
33 {
34  // 1. Exit the current container.
35  // 2. Update Contents of container object and set Contained to nullptr.
36  // 3. Set offset position/motion if desired.
37  // 4. Call Ejection for container and Departure for object.
38 
39  // Not contained
40  C4Object *pContainer=Contained;
41  if (!pContainer) return false;
42  // Remove object from container
43  pContainer->Contents.Remove(this);
44  pContainer->UpdateMass();
45  pContainer->SetOCF();
46  // No container
47  Contained=nullptr;
48  // Position/motion
49  fix_x=itofix(iX); fix_y=itofix(iY);
50  fix_r=itofix(iR);
52  xdir=iXDir; ydir=iYDir; rdir=iRDir;
53  // Misc updates
54  Mobile=true;
55  InLiquid=false;
56  CloseMenu(true);
57  UpdateFace(true);
58  SetOCF();
59  // Object list callback (before script callbacks, because script callbacks may enter again)
60  ObjectListChangeListener.OnObjectContainerChanged(this, pContainer, nullptr);
61  // Engine calls
62  if (fCalls) pContainer->Call(PSF_Ejection,&C4AulParSet(this));
63  if (fCalls) Call(PSF_Departure,&C4AulParSet(pContainer));
64  // Success (if the obj wasn't "re-entered" by script)
65  return !Contained;
66 }
67 
68 bool C4Object::Enter(C4Object *pTarget, bool fCalls, bool fCopyMotion, bool *pfRejectCollect)
69 {
70  // 0. Query entrance and collection
71  // 1. Exit if contained.
72  // 2. Set new container.
73  // 3. Update Contents and mass of the new container.
74  // 4. Call collection for container
75  // 5. Call entrance for object.
76 
77  // No valid target or target is self
78  if (!pTarget || (pTarget==this)) return false;
79  // check if entrance is allowed
80  if (!! Call(PSF_RejectEntrance, &C4AulParSet(pTarget))) return false;
81  // check if we end up in an endless container-recursion
82  for (C4Object *pCnt=pTarget->Contained; pCnt; pCnt=pCnt->Contained)
83  if (pCnt==this) return false;
84  // Check RejectCollect, if desired
85  if (pfRejectCollect)
86  {
87  if (!!pTarget->Call(PSF_RejectCollection,&C4AulParSet(Def, this)))
88  {
89  *pfRejectCollect = true;
90  return false;
91  }
92  *pfRejectCollect = false;
93  }
94  // Exit if contained
95  if (Contained) if (!Exit(GetX(),GetY())) return false;
96  if (Contained || !Status || !pTarget->Status) return false;
97  // Failsafe updates
98  if (Menu)
99  {
100  CloseMenu(true);
101  // CloseMenu might do bad stuff
102  if (Contained || !Status || !pTarget->Status) return false;
103  }
104  SetOCF();
105  // Set container
106  Contained=pTarget;
107  // Enter
109  {
110  Contained=nullptr;
111  return false;
112  }
113  // Assume that the new container controls this object, if it cannot control itself (i.e.: Alive)
114  // So it can be traced back who caused the damage, if a projectile hits its target
115  if (!Alive)
116  Controller = pTarget->Controller;
117  // Misc updates
118  // motion must be copied immediately, so the position will be correct when OCF is set, and
119  // OCF_Available will be set for newly bought items, even if 50/50 is solid in the landscape
120  // however, the motion must be preserved sometimes to keep flags like OCF_HitSpeed upon collection
121  if (fCopyMotion)
122  {
123  // remove any solidmask before copying the motion...
124  UpdateSolidMask(false);
126  }
127  SetOCF();
128  UpdateFace(true);
129  // Update container
131  Contained->SetOCF();
132  // Object list callback (before script callbacks, because script callbacks may exit again)
134  // Collection call
135  if (fCalls) pTarget->Call(PSF_Collection2,&C4AulParSet(this));
136  if (!Contained || !Contained->Status || !pTarget->Status) return true;
137  // Entrance call
138  if (fCalls) Call(PSF_Entrance,&C4AulParSet(Contained));
139  if (!Contained || !Contained->Status || !pTarget->Status) return true;
140  // Success
141  return true;
142 }
143 
145 {
146  C4Object *nobj;
147  if (!(nobj=Game.CreateObject(PropList,this,Owner))) return nullptr;
148  if (!nobj->Enter(this)) { nobj->AssignRemoval(); return nullptr; }
149  return nobj;
150 }
151 
153 {
154  int32_t cnt,cnt2;
155  for (cnt=0; idlist.GetID(cnt); cnt++)
156  for (cnt2=0; cnt2<idlist.GetCount(cnt); cnt2++)
157  if (!CreateContents(C4Id2Def(idlist.GetID(cnt))))
158  return false;
159  return true;
160 }
161 
163 {
164  // Object enter container
165  bool fRejectCollect;
166  if (!pObj->Enter(this, true, false, &fRejectCollect))
167  return false;
168  // Cancel attach (hacky)
169  ObjectComCancelAttach(pObj);
170  // Container Collection call
172  // Object Hit call
173  if (pObj->Status && pObj->OCF & OCF_HitSpeed1) pObj->Call(PSF_Hit);
174  if (pObj->Status && pObj->OCF & OCF_HitSpeed2) pObj->Call(PSF_Hit2);
175  if (pObj->Status && pObj->OCF & OCF_HitSpeed3) pObj->Call(PSF_Hit3);
176  // post-copy the motion of the new container
177  if (pObj->Contained == this) pObj->CopyMotion(this);
178  // done, success
179  return true;
180 }
181 
182 bool C4Object::ShiftContents(bool fShiftBack, bool fDoCalls)
183 {
184  // get current object
185  C4Object *c_obj = Contents.GetObject();
186  if (!c_obj) return false;
187  // get next/previous
188  auto it = fShiftBack ? Contents.reverse().begin() : ++Contents.begin();
189  while (!it.atEnd())
190  {
191  auto pObj = (*it);
192  // check object
193  if (pObj->Status)
194  if (!c_obj->CanConcatPictureWith(pObj))
195  {
196  // object different: shift to this
197  DirectComContents(pObj, !!fDoCalls);
198  return true;
199  }
200  // next/prev item
201  it++;
202  }
203  return false;
204 }
205 
206 void C4Object::DirectComContents(C4Object *pTarget, bool fDoCalls)
207 {
208  // safety
209  if (!pTarget || !pTarget->Status || pTarget->Contained != this) return;
210  // Desired object already at front?
211  if (Contents.GetObject() == pTarget) return;
212  // select object via script?
213  if (fDoCalls)
214  if (Call("~ControlContents", &C4AulParSet(pTarget)))
215  return;
216  // default action
217  if (!(Contents.ShiftContents(pTarget))) return;
218  // Selection sound
219  if (fDoCalls) if (!Contents.GetObject()->Call("~Selection", &C4AulParSet(this))) StartSoundEffect("Clonk::Action::Grab",false,100,this);
220  // update menu with the new item in "put" entry
221  if (Menu && Menu->IsActive() && Menu->IsContextMenu())
222  {
223  Menu->Refill();
224  }
225  // Done
226  return;
227 }
228 
229 int32_t C4Object::AddObjectAndContentsToArray(C4ValueArray *target_array, int32_t index)
230 {
231  // add self, contents and child contents count recursively to value array. Return index after last added item.
232  target_array->SetItem(index++, C4VObj(this));
233  for (C4Object *cobj : Contents)
234  {
235  index = cobj->AddObjectAndContentsToArray(target_array, index);
236  }
237  return index;
238 }
239 
241 {
242  // exit contents from container
243  for (C4Object *cobj : Contents)
244  {
245  cobj->Exit(GetX(), GetY(), 0,Fix0,Fix0,Fix0, fDoCalls);
246  }
247  // remove from container *after* contents have been removed!
248  if (Contained) Exit(GetX(), GetY(), 0, Fix0, Fix0, Fix0, fDoCalls);
249 }
250 
252 {
253  // create a temp list of all objects and transfer it
254  // this prevents nasty deadlocks caused by RejectEntrance-scripts
255  C4ObjectList tmpList; tmpList.Copy(pFrom->Contents);
256  for (C4Object *obj : tmpList)
257  if (obj->Status)
258  obj->Enter(this);
259 }
const uint32_t OCF_HitSpeed1
Definition: C4Constants.h:84
const uint32_t OCF_HitSpeed3
Definition: C4Constants.h:92
const uint32_t OCF_HitSpeed2
Definition: C4Constants.h:91
C4Def * C4Id2Def(C4ID id)
Definition: C4DefList.h:84
#define PSF_Entrance
Definition: C4GameScript.h:59
#define PSF_RejectCollection
Definition: C4GameScript.h:133
#define PSF_RejectEntrance
Definition: C4GameScript.h:132
#define PSF_Ejection
Definition: C4GameScript.h:58
#define PSF_Hit
Definition: C4GameScript.h:49
#define PSF_Collection2
Definition: C4GameScript.h:57
#define PSF_Departure
Definition: C4GameScript.h:60
#define PSF_Hit2
Definition: C4GameScript.h:50
#define PSF_Hit3
Definition: C4GameScript.h:51
#define PSF_Collection
Definition: C4GameScript.h:56
C4Game Game
Definition: C4Globals.cpp:52
bool ObjectComCancelAttach(C4Object *cObj)
C4ObjectListChangeListener & ObjectListChangeListener
C4Fixed itofix(int32_t x)
Definition: C4Real.h:261
const C4Real Fix0
Definition: C4Real.h:312
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:88
Definition: C4Real.h:59
C4Object * CreateObject(C4PropList *type, C4Object *creator, int32_t owner=NO_OWNER, int32_t x=50, int32_t y=50, int32_t r=0, bool grow_from_center=false, C4Real xdir=Fix0, C4Real ydir=Fix0, C4Real rdir=Fix0, int32_t controller=NO_OWNER)
Definition: C4Game.cpp:1334
int32_t GetCount(size_t index) const
Definition: C4IDList.cpp:125
C4ID GetID(size_t index, int32_t *ipCount=nullptr) const
Definition: C4IDList.cpp:103
bool Refill()
Definition: C4Menu.cpp:915
bool IsActive()
Definition: C4Menu.cpp:480
bool IsContextMenu()
Definition: C4Menu.h:164
void UpdateSolidMask(bool fRestoreAttachedObjects)
bool Enter(C4Object *pTarget, bool fCalls=true, bool fCopyMotion=true, bool *pfRejectCollect=nullptr)
C4Real ydir
Definition: C4Object.h:124
C4Real fix_y
Definition: C4Object.h:123
bool ShiftContents(bool fShiftBack, bool fDoCalls)
void UpdateMass()
C4Real xdir
Definition: C4Object.h:124
void CopyMotion(C4Object *from)
Definition: C4Movement.cpp:653
bool CreateContentsByList(C4IDList &idlist)
void UpdateFace(bool bUpdateShape, bool fTemp=false)
C4Real fix_x
Definition: C4Object.h:123
C4Object * CreateContents(C4PropList *)
int32_t Owner
Definition: C4Object.h:108
int32_t AddObjectAndContentsToArray(C4ValueArray *target_array, int32_t index=0)
int32_t GetX() const
Definition: C4Object.h:285
class C4ObjectMenu * Menu
Definition: C4Object.h:138
int32_t Controller
Definition: C4Object.h:109
uint32_t OCF
Definition: C4Object.h:132
C4Real fix_r
Definition: C4Object.h:123
bool Mobile
Definition: C4Object.h:126
void DirectComContents(C4Object *pTarget, bool fDoCalls)
bool CloseMenu(bool fForce)
int32_t GetY() const
Definition: C4Object.h:286
void BoundsCheck(C4Real &target_x, C4Real &target_y)
Definition: C4Object.h:330
bool Alive
Definition: C4Object.h:174
void AssignRemoval(bool exit_contents=false)
Definition: C4Object.cpp:215
void ClearContentsAndContained(bool fDoCalls=true)
bool Collect(C4Object *pObj)
C4NotifyingObjectList Contents
Definition: C4Object.h:151
C4ObjectPtr Contained
Definition: C4Object.h:142
C4Real rdir
Definition: C4Object.h:124
bool Exit(int32_t iX=0, int32_t iY=0, int32_t iR=0, C4Real iXDir=Fix0, C4Real iYDir=Fix0, C4Real iRDir=Fix0, bool fCalls=true)
void SetOCF()
Definition: C4ObjectOCF.cpp:30
bool InLiquid
Definition: C4Object.h:129
void GrabContents(C4Object *pFrom)
bool CanConcatPictureWith(C4Object *pOtherObject) const
C4Def * Def
Definition: C4Object.h:141
virtual void OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container)
Definition: C4ObjectList.h:38
bool ShiftContents(C4Object *new_first)
void Copy(const C4ObjectList &list)
iterator begin() const
virtual bool Add(C4Object *new_obj, SortType sort_type, C4ObjectList *sorted_list=nullptr)
const ReverseView reverse() const
Definition: C4ObjectList.h:104
virtual bool Remove(C4Object *obj)
C4Object * GetObject(int index=0) const
int32_t Status
Definition: C4PropList.h:173
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:114
void SetItem(int32_t iElemNr, const C4Value &Value)