eris  1.4.0
A WorldForge client library.
Entity.cpp
1 #include <utility>
2 
3 #ifdef HAVE_CONFIG_H
4  #include "config.h"
5 #endif
6 
7 #include "Entity.h"
8 #include "Connection.h"
9 #include "TypeInfo.h"
10 #include "LogStream.h"
11 #include "Exceptions.h"
12 #include "Avatar.h"
13 #include "Task.h"
14 
15 #include <wfmath/atlasconv.h>
16 #include <Atlas/Objects/Entity.h>
17 #include <Atlas/Objects/Operation.h>
18 #include <Atlas/Objects/BaseObject.h>
19 
20 #include <algorithm>
21 #include <set>
22 #include <cassert>
23 
24 using namespace Atlas::Objects::Operation;
25 using Atlas::Objects::Root;
26 using Atlas::Objects::Entity::RootEntity;
27 using Atlas::Message::Element;
28 using Atlas::Message::ListType;
29 using Atlas::Message::MapType;
30 using Atlas::Objects::smart_static_cast;
31 using Atlas::Objects::smart_dynamic_cast;
32 
33 using WFMath::TimeStamp;
34 using WFMath::TimeDiff;
35 
36 namespace Eris {
37 
38 Entity::Entity(std::string id, TypeInfo* ty) :
39  m_type(ty),
40  m_location(nullptr),
41  m_id(std::move(id)),
42  m_stamp(-1.0f),
43  m_visible(false),
44  m_waitingForParentBind(false),
45  m_angularMag(0),
46  m_updateLevel(0),
47  m_hasBBox(false),
48  m_moving(false),
49  m_recentlyCreated(false)
50 {
51  assert(!m_id.empty());
52  m_orientation.identity();
53 
54 
55  if (m_type) {
56  m_type->PropertyChanges.connect(sigc::mem_fun(this, &Entity::typeInfo_PropertyChanges));
57  }
58 }
59 
60 Entity::~Entity()
61 {
62  shutdown();
63 }
64 
65 void Entity::shutdown() {
66  setLocation(nullptr);
67 
68  for (auto& child: m_contents) {
69  //Release all children.
70  child->setLocation(nullptr, false);
71  }
72  m_contents.clear();
73 
74  //Delete any lingering tasks.
75  for (auto& entry : m_tasks) {
76  TaskRemoved(entry.first, entry.second.get());
77  }
78 }
79 
80 void Entity::init(const RootEntity& ge, bool fromCreateOp)
81 {
82  // setup initial state
83  firstSight(ge);
84 
85  if (fromCreateOp)
86  {
87  m_recentlyCreated = true;
88  }
89 }
90 
91 
92 Entity* Entity::getTopEntity()
93 {
94  if (m_waitingForParentBind) {
95  return nullptr;
96  }
97  if (!m_location) {
98  return this;
99  }
100  return m_location->getTopEntity();
101 }
102 
103 bool Entity::isAncestorTo(Eris::Entity& entity) const
104 {
105  if (!entity.getLocation()) {
106  return false;
107  }
108  if (static_cast<const Eris::Entity*>(this) == entity.getLocation()) {
109  return true;
110  }
111  return isAncestorTo(*entity.getLocation());
112 
113 }
114 
115 const Element& Entity::valueOfProperty(const std::string& name) const
116 {
118  auto A = m_properties.find(name);
119  if (A == m_properties.end())
120  {
121  if (m_type) {
123  const Element* element = m_type->getProperty(name);
124  if (element) {
125  return *element;
126  }
127  }
128  error() << "did valueOfProperty(" << name << ") on entity " << m_id << " which has no such name";
129  throw InvalidOperation("no such property " + name);
130  } else {
131  return A->second;
132  }
133 }
134 
135 bool Entity::hasProperty(const std::string& p) const
136 {
138  if (m_properties.find(p) != m_properties.end()) {
139  return true;
140  } else if (m_type) {
142  if (m_type->getProperty(p) != nullptr) {
143  return true;
144  }
145  }
146  return false;
147 }
148 
149 const Element* Entity::ptrOfProperty(const std::string& name) const
150 {
152  auto A = m_properties.find(name);
153  if (A == m_properties.end())
154  {
155  if (m_type) {
157  const Element* element = m_type->getProperty(name);
158  if (element) {
159  return element;
160  }
161  }
162  return nullptr;
163  } else {
164  return &A->second;
165  }
166 }
167 
168 
169 Entity::PropertyMap Entity::getProperties() const
170 {
172  PropertyMap properties;
173  properties.insert(m_properties.begin(), m_properties.end());
174  if (m_type) {
175  fillPropertiesFromType(properties, *m_type);
176  }
177  return properties;
178 }
179 
180 const Entity::PropertyMap& Entity::getInstanceProperties() const
181 {
182  return m_properties;
183 }
184 
185 void Entity::fillPropertiesFromType(Entity::PropertyMap& properties, const TypeInfo& typeInfo) const
186 {
187  properties.insert(typeInfo.getProperties().begin(), typeInfo.getProperties().end());
189 
190  if (typeInfo.getParent()) {
191  fillPropertiesFromType(properties, *typeInfo.getParent());
192  }
193 
194 }
195 
196 sigc::connection Entity::observe(const std::string& propertyName, const PropertyChangedSlot& slot, bool evaluateNow)
197 {
198  // sometimes, I realize how great SigC++ is
199  auto connection = m_observers[propertyName].connect(slot);
200  if (evaluateNow) {
201  auto prop = ptrOfProperty(propertyName);
202  if (prop) {
203  slot(*prop);
204  }
205  }
206  return connection;
207 }
208 
209 const WFMath::Point<3>& Entity::getPredictedPos() const
210 {
211  return (m_moving ? m_predicted.position : m_position);
212 }
213 
214 const WFMath::Vector<3>& Entity::getPredictedVelocity() const
215 {
216  return (m_moving ? m_predicted.velocity : m_velocity);
217 }
218 
219 const WFMath::Quaternion& Entity::getPredictedOrientation() const
220 {
221  return (m_moving ? m_predicted.orientation : m_orientation);
222 }
223 
224 bool Entity::isMoving() const
225 {
226  return m_moving;
227 }
228 
229 void Entity::updatePredictedState(const WFMath::TimeStamp& t, double simulationSpeed)
230 {
231  assert(isMoving());
232 
233  if (m_acc.isValid() && m_acc != WFMath::Vector<3>::ZERO()) {
234  double posDeltaTime = static_cast<double>((t - m_lastPosTime).milliseconds()) / 1000.0;
235  m_predicted.velocity = m_velocity + (m_acc * posDeltaTime * simulationSpeed);
236  m_predicted.position = m_position + (m_velocity * posDeltaTime * simulationSpeed) + (m_acc * 0.5 * posDeltaTime * posDeltaTime * simulationSpeed);
237  } else {
238  m_predicted.velocity = m_velocity;
239  if (m_predicted.velocity != WFMath::Vector<3>::ZERO()) {
240  double posDeltaTime = static_cast<double>((t - m_lastPosTime).milliseconds()) / 1000.0;
241  m_predicted.position = m_position + (m_velocity * posDeltaTime * simulationSpeed);
242  } else {
243  m_predicted.position = m_position;
244  }
245  }
246  if (m_angularVelocity.isValid() && m_angularMag != .0) {
247  double orientationDeltaTime = static_cast<double>((t - m_lastOrientationTime).milliseconds()) / 1000.0;
248  m_predicted.orientation = m_orientation * WFMath::Quaternion(m_angularVelocity, m_angularMag * orientationDeltaTime * simulationSpeed);
249  } else {
250  m_predicted.orientation = m_orientation;
251  }
252 }
253 
254 void Entity::firstSight(const RootEntity &gent)
255 {
256  if (!gent->isDefaultLoc()) {
257  setLocationFromAtlas(gent->getLoc());
258  } else {
259  setLocation(nullptr);
260  }
261 
262  setContentsFromAtlas(gent->getContains());
263  //Since this is the first sight of this entity we should include all type props too.
264  setFromRoot(gent, true);
265 }
266 
267 void Entity::setFromRoot(const Root& obj, bool includeTypeInfoProperties)
268 {
269  beginUpdate();
270 
271  Atlas::Message::MapType properties;
272  obj->addToMessage(properties);
273 
274  properties.erase("id"); //Id can't be changed once it's initially set, which it's at Entity creation time.
275  properties.erase("contains"); //Contains are handled by the setContentsFromAtlas method which should be called separately.
276 
277  for (auto& entry : properties) {
278  // see if the value in the sight matches the existing value
279  auto I = m_properties.find(entry.first);
280  if ((I != m_properties.end()) && (I->second == entry.second)) {
281  continue;
282  }
283  try {
284  setProperty(entry.first, entry.second);
285  } catch (const std::exception& ex) {
286  warning() << "Error when setting property '" << entry.first << "'. Message: " << ex.what();
287  }
288  }
289 
290  //Add any values found in the type, if they aren't defined in the entity already.
291  if (includeTypeInfoProperties && m_type) {
292  Atlas::Message::MapType typeProperties;
293  fillPropertiesFromType(typeProperties, *m_type);
294  for (auto& entry : typeProperties) {
295  propertyChangedFromTypeInfo(entry.first, entry.second);
296  }
297  }
298 
299  endUpdate();
300 
301 }
302 
303 void Entity::onTalk(const Atlas::Objects::Operation::RootOperation& talk)
304 {
305  const std::vector<Root>& talkArgs = talk->getArgs();
306  if (talkArgs.empty())
307  {
308  warning() << "entity " << getId() << " got sound(talk) with no args";
309  return;
310  }
311 
312  for (const auto& arg: talkArgs) {
313  Say.emit(arg);
314  }
315  //Noise.emit(talk);
316 }
317 
318 void Entity::onLocationChanged(Entity* oldLoc)
319 {
320  LocationChanged.emit(oldLoc);
321 }
322 
323 void Entity::onMoved(const WFMath::TimeStamp& timeStamp)
324 {
325  if (m_moving) {
326  //We should update the predicted pos and velocity.
327  updatePredictedState(timeStamp, 1.0);
328  }
329  Moved.emit();
330 }
331 
332 void Entity::onAction(const Atlas::Objects::Operation::RootOperation& arg, const TypeInfo& typeInfo)
333 {
334  Acted.emit(arg, typeInfo);
335 }
336 
337 void Entity::onHit(const Atlas::Objects::Operation::Hit& arg, const TypeInfo& typeInfo)
338 {
339  Hit.emit(arg, typeInfo);
340 }
341 
342 void Entity::onSoundAction(const Atlas::Objects::Operation::RootOperation& op, const TypeInfo& typeInfo)
343 {
344  Noise.emit(op, typeInfo);
345 }
346 
347 void Entity::onImaginary(const Atlas::Objects::Root& arg)
348 {
349  Atlas::Message::Element attr;
350  if (arg->copyAttr("description", attr) == 0 && attr.isString()) {
351  Emote.emit(attr.asString());
352  }
353 }
354 
355 void Entity::setMoving(bool inMotion)
356 {
357  assert(m_moving != inMotion);
358 
359  m_moving = inMotion;
360  Moving.emit(inMotion);
361 
362 }
363 
364 void Entity::onChildAdded(Entity* child)
365 {
366  ChildAdded.emit(child);
367 }
368 
369 void Entity::onChildRemoved(Entity* child)
370 {
371  ChildRemoved(child);
372 }
373 
374 void Entity::onTaskAdded(const std::string& id, Task* task)
375 {
376  TaskAdded(id, task);
377 }
378 
379 
380 void Entity::setProperty(const std::string &p, const Element &v)
381 {
382  beginUpdate();
383 
384  m_properties[p] = v;
385 
386  nativePropertyChanged(p, v);
387  onPropertyChanged(p, v);
388 
389  // fire observers
390 
391  auto obs = m_observers.find(p);
392  if (obs != m_observers.end()) {
393  obs->second.emit(v);
394  }
395 
396  addToUpdate(p);
397  endUpdate();
398 }
399 
400 bool Entity::nativePropertyChanged(const std::string& p, const Element& v)
401 {
402  // in the future, hash these names to a compile-time integer index, and
403  // make this a switch statement. The same index could also be used
404  // in endUpdate
405 
406  if (p == "name") {
407  m_name = v.asString();
408  return true;
409  } else if (p == "stamp") {
410  m_stamp = v.asFloat();
411  return true;
412  } else if (p == "pos") {
413  m_position.fromAtlas(v);
414  return true;
415  } else if (p == "velocity") {
416  m_velocity.fromAtlas(v);
417  return true;
418  } else if (p == "angular") {
419  m_angularVelocity.fromAtlas(v);
420  m_angularMag = m_angularVelocity.mag();
421  return true;
422  } else if (p == "accel") {
423  m_acc.fromAtlas(v);
424  return true;
425  } else if (p == "orientation") {
426  m_orientation.fromAtlas(v);
427  return true;
428  } else if (p == "bbox") {
429  m_bboxUnscaled.fromAtlas(v);
430  m_bbox = m_bboxUnscaled;
431  if (m_scale.isValid() && m_bbox.isValid()) {
432  m_bbox.lowCorner().x() *= m_scale.x();
433  m_bbox.lowCorner().y() *= m_scale.y();
434  m_bbox.lowCorner().z() *= m_scale.z();
435  m_bbox.highCorner().x() *= m_scale.x();
436  m_bbox.highCorner().y() *= m_scale.y();
437  m_bbox.highCorner().z() *= m_scale.z();
438  }
439  m_hasBBox = m_bbox.isValid();
440  return true;
441  } else if (p == "loc") {
442  setLocationFromAtlas(v.asString());
443  return true;
444  } else if (p == "contains") {
445  throw InvalidOperation("tried to set contains via setProperty");
446  } else if (p == "tasks") {
447  updateTasks(v);
448  return true;
449  } else if (p == "scale") {
450  if (v.isList()) {
451  if (v.List().size() == 1) {
452  if (v.List().front().isNum()) {
453  auto num = static_cast<WFMath::CoordType>(v.List().front().asNum());
454  m_scale = WFMath::Vector<3>(num, num, num);
455  }
456  } else {
457  m_scale.fromAtlas(v.List());
458  }
459  } else {
460  m_scale = WFMath::Vector<3>();
461  }
462  m_bbox = m_bboxUnscaled;
463  if (m_scale.isValid() && m_bbox.isValid()) {
464  m_bbox.lowCorner().x() *= m_scale.x();
465  m_bbox.lowCorner().y() *= m_scale.y();
466  m_bbox.lowCorner().z() *= m_scale.z();
467  m_bbox.highCorner().x() *= m_scale.x();
468  m_bbox.highCorner().y() *= m_scale.y();
469  m_bbox.highCorner().z() *= m_scale.z();
470  }
471  return true;
472  }
473 
474  return false; // not a native property
475 }
476 
477 void Entity::onPropertyChanged(const std::string& propertyName, const Element& v)
478 {
479  // no-op by default
480 }
481 
482 
483 void Entity::typeInfo_PropertyChanges(const std::string& propertyName, const Atlas::Message::Element& element)
484 {
485  propertyChangedFromTypeInfo(propertyName, element);
486 }
487 
488 void Entity::propertyChangedFromTypeInfo(const std::string& propertyName, const Atlas::Message::Element& element)
489 {
491  if (m_properties.find(propertyName) == m_properties.end()) {
492  beginUpdate();
493  nativePropertyChanged(propertyName, element);
494  onPropertyChanged(propertyName, element);
495 
496  // fire observers
497 
498  ObserverMap::const_iterator obs = m_observers.find(propertyName);
499  if (obs != m_observers.end()) {
500  obs->second.emit(element);
501  }
502 
503  addToUpdate(propertyName);
504  endUpdate();
505  }
506 }
507 
508 
509 void Entity::beginUpdate()
510 {
511  ++m_updateLevel;
512 }
513 
514 void Entity::addToUpdate(const std::string& propertyName)
515 {
516  assert(m_updateLevel > 0);
517  m_modifiedProperties.insert(propertyName);
518 }
519 
520 void Entity::endUpdate()
521 {
522  if (m_updateLevel < 1)
523  {
524  error() << "mismatched begin/end update pair on entity";
525  return;
526  }
527 
528  if (--m_updateLevel == 0) // unlocking updates
529  {
530  Changed.emit(m_modifiedProperties);
531 
532  if (m_modifiedProperties.find("pos") != m_modifiedProperties.end() ||
533  m_modifiedProperties.find("velocity") != m_modifiedProperties.end() ||
534  m_modifiedProperties.find("orientation") != m_modifiedProperties.end() ||
535  m_modifiedProperties.find("angular") != m_modifiedProperties.end())
536  {
537  auto now = TimeStamp::now();
538  if (m_modifiedProperties.find("pos") != m_modifiedProperties.end()) {
539  m_lastPosTime = now;
540  }
541  if (m_modifiedProperties.find("orientation") != m_modifiedProperties.end()) {
542  m_lastOrientationTime = now;
543  }
544 
545  const WFMath::Vector<3> & velocity = getVelocity();
546  bool nowMoving = (velocity.isValid() && (velocity.sqrMag() > 1e-3)) || (m_angularVelocity.isValid() && m_angularVelocity != WFMath::Vector<3>::ZERO());
547  if (nowMoving != m_moving) {
548  setMoving(nowMoving);
549  }
550 
551  onMoved(now);
552  }
553 
554  m_modifiedProperties.clear();
555  }
556 }
557 
558 
559 void Entity::updateTasks(const Element& e)
560 {
561  if (e.isNone()) {
562  for (auto& entry : m_tasks) {
563  TaskRemoved(entry.first, entry.second.get());
564  }
565  m_tasks.clear();
566  return;
567  }
568  if (!e.isMap()) {
569  return; // malformed
570  }
571  auto& taskMap = e.Map();
572 
573  auto previousTasks = std::move(m_tasks);
574  m_tasks.clear();
575 
576  for (auto& entry : taskMap) {
577  auto& taskElement = entry.second;
578  if (!taskElement.isMap()) {
579  continue;
580  }
581  const MapType& tkmap(taskElement.Map());
582  auto it = tkmap.find("name");
583  if (it == tkmap.end())
584  {
585  error() << "task without name";
586  continue;
587  }
588  if (!it->second.isString())
589  {
590  error() << "task with invalid name";
591  continue;
592  }
593 
594  auto tasksI = previousTasks.find(entry.first);
595  std::unique_ptr<Task> task;
596 
597  bool newTask = false;
598  if (tasksI == previousTasks.end())
599  { // not found, create a new one
600  task = std::make_unique<Task>(*this, it->second.asString());
601  newTask = true;
602  } else {
603  task = std::move(tasksI->second);
604  previousTasks.erase(entry.first);
605  }
606 
607  task->updateFromAtlas(tkmap);
608  if (newTask) {
609  onTaskAdded(entry.first, task.get());
610  }
611  m_tasks.emplace(entry.first, std::move(task));
612  } // of Atlas-specified tasks iteration
613 
614  for (auto& entry : previousTasks) {
615 
616  if (entry.second) {
617  TaskRemoved(entry.first, entry.second.get());
618  }
619  } // of previous-task cleanup iteration
620 }
621 
622 void Entity::setLocationFromAtlas(const std::string& locId) {
623  if (locId.empty()) {
624  return;
625  }
626 
627  Entity* newLocation = getEntity(locId);
628  if (!newLocation) {
629 
630  m_waitingForParentBind = true;
631  setVisible(false); // fire disappearance, VisChanged if necessary
632 
633  if (m_location) {
634  removeFromLocation();
635  }
636  m_location = nullptr;
637  assert(!m_visible);
638  return;
639  }
640 
641  setLocation(newLocation);
642 }
643 
644 void Entity::setLocation(Entity* newLocation, bool removeFromOldLocation)
645 {
646  if (newLocation == m_location) return;
647 
648  if (newLocation) {
649  m_waitingForParentBind = newLocation->m_waitingForParentBind;
650  }
651 
652 // do the actual member updating
653  bool wasVisible = isVisible();
654  if (m_location && removeFromOldLocation) {
655  removeFromLocation();
656  }
657 
658  Entity* oldLocation = m_location;
659  m_location = newLocation;
660 
661  onLocationChanged(oldLocation);
662 
663 // fire VisChanged and Appearance/Disappearance signals
664  updateCalculatedVisibility(wasVisible);
665 
666  if (m_location) {
667  addToLocation();
668  }
669 }
670 
671 void Entity::addToLocation()
672 {
673  assert(!m_location->hasChild(m_id));
674  m_location->addChild(this);
675 }
676 
677 void Entity::removeFromLocation()
678 {
679  assert(m_location->hasChild(m_id));
680  m_location->removeChild(this);
681 }
682 
683 void Entity::buildEntityDictFromContents(IdEntityMap& dict)
684 {
685  for (auto& child : m_contents) {
686  dict[child->getId()] = child;
687  }
688 }
689 
690 void Entity::setContentsFromAtlas(const std::vector<std::string>& contents)
691 {
692 // convert existing contents into a map, for fast membership tests
693  IdEntityMap oldContents;
694  buildEntityDictFromContents(oldContents);
695 
696 // iterate over new contents
697  for (auto& content : contents) {
698  Entity* child = nullptr;
699 
700  auto J = oldContents.find(content);
701  if (J != oldContents.end()) {
702  child = J->second;
703  assert(child->getLocation() == this);
704  oldContents.erase(J);
705  } else {
706  child = getEntity(content);
707  if (!child) {
708  continue;
709  }
710 
711  if (child->m_waitingForParentBind) {
712  assert(!child->m_visible);
713  child->m_waitingForParentBind = false;
714  }
715 
716  /* we have found the child, update it's location */
717  child->setLocation(this);
718  }
719 
720  child->setVisible(true);
721  } // of contents list iteration
722 
723 // mark previous contents which are not in new contents as invisible
724  for (auto& entry : oldContents) {
725  entry.second->setVisible(false);
726  }
727 }
728 
729 bool Entity::hasChild(const std::string& eid) const
730 {
731  for (auto& m_content : m_contents) {
732  if (m_content->getId() == eid) {
733  return true;
734  }
735  }
736 
737  return false;
738 }
739 
740 void Entity::addChild(Entity* e)
741 {
742  m_contents.push_back(e);
743  onChildAdded(e);
744  assert(e->getLocation() == this);
745 }
746 
747 void Entity::removeChild(Entity* e)
748 {
749  assert(e->getLocation() == this);
750 
751  auto I = std::find(m_contents.begin(), m_contents.end(), e);
752  if (I != m_contents.end()) {
753  m_contents.erase(I);
754  onChildRemoved(e);
755  return;
756  }
757  error() << "child " << e->getId() << " of entity " << m_id << " not found doing remove";
758 }
759 
760 // visiblity related methods
761 
762 void Entity::setVisible(bool vis)
763 {
764  // force visibility to false if in limbo; necessary for the character entity,
765  // which otherwise gets double appearances on activation
766  if (m_waitingForParentBind) vis = false;
767 
768  bool wasVisible = isVisible(); // store before we update m_visible
769  m_visible = vis;
770 
771  updateCalculatedVisibility(wasVisible);
772 }
773 
774 bool Entity::isVisible() const
775 {
776  if (m_waitingForParentBind) return false;
777 
778  if (m_location) {
779  return m_visible && m_location->isVisible();
780  } else {
781  return m_visible; // only for the root entity
782  }
783 }
784 
785 void Entity::updateCalculatedVisibility(bool wasVisible)
786 {
787  bool nowVisible = isVisible();
788  if (nowVisible == wasVisible) return;
789 
790  /* the following code looks odd, so remember that only one of nowVisible and
791  wasVisible can ever be true. The structure is necessary so that we fire
792  Appearances top-down, but Disappearances bottom-up. */
793 
794  if (nowVisible) {
795  onVisibilityChanged(true);
796  }
797 
798  for (auto& item : m_contents) {
799  /* in case this isn't clear; if we were visible, then child visibility
800  was simply it's locally set value; if we were invisible, that the
801  child must also have been invisible too. */
802  bool childWasVisible = wasVisible && item->m_visible;
803  item->updateCalculatedVisibility(childWasVisible);
804  }
805 
806  if (wasVisible) {
807  onVisibilityChanged(false);
808  }
809 }
810 
811 void Entity::onVisibilityChanged(bool vis)
812 {
813  VisibilityChanged.emit(vis);
814 }
815 
816 boost::optional<std::string> Entity::extractEntityId(const Atlas::Message::Element& element)
817 {
818  if (element.isString()) {
819  return element.String();
820  } else if (element.isMap()) {
821  auto I = element.asMap().find("$eid");
822  if (I != element.asMap().end() && I->second.isString()) {
823  return I->second.String();
824  }
825  }
826  return boost::none;
827 
828 }
829 
830 
831 } // of namespace
Entity is a concrete (instantiable) class representing one game entity.
Definition: Entity.h:56
void setVisible(bool vis)
Definition: Entity.cpp:762
void setLocation(Entity *newLocation, bool removeFromOldLocation=true)
Definition: Entity.cpp:644
Entity * getTopEntity()
Gets the top level entity for this entity, i.e. the parent location which has no parent....
Definition: Entity.cpp:92
bool m_waitingForParentBind
waiting for parent bind
Definition: Entity.h:575
Entity * getLocation() const
The containing entity, or null if this is a top-level visible entity.
Definition: Entity.h:656
sigc::slot< void, const Atlas::Message::Element & > PropertyChangedSlot
A slot which can be used for receiving property update signals.
Definition: Entity.h:123
The representation of an Atlas type (i.e a class or operation definition). This class supports effice...
Definition: TypeInfo.h:33
const Atlas::Message::MapType & getProperties() const
Gets the default properties for this entity type. Note that the map returned does not include inherit...
Definition: TypeInfo.h:207
const TypeInfo * getParent() const
Gets the currently resolved parent TypeInfo instances.
Definition: TypeInfo.h:231
Definition: Account.cpp:33