FIFE  be64c707dea6b3250bd4355bf5c825d25920087d
mapsaver.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 * Copyright (C) 2005-2019 by the FIFE team *
3 * http://www.fifengine.net *
4 * This file is part of FIFE. *
5 * *
6 * FIFE is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Lesser General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2.1 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with this library; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
21 
22 // Standard C++ library includes
23 
24 // 3rd party library includes
25 #include <tinyxml.h>
26 
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31 #include "model/structures/map.h"
32 #include "model/structures/layer.h"
34 #include "model/structures/cell.h"
38 #include "model/metamodel/object.h"
40 #include "util/structures/point.h"
41 #include "util/structures/rect.h"
42 #include "view/visual.h"
43 #include "view/camera.h"
44 
45 #include "mapsaver.h"
46 
47 namespace FIFE {
48  static Logger _log(LM_NATIVE_SAVERS);
49 
51  //m_objectSaver = new ObjectSaver();
52  //m_animationSaver = new AnimationSaver();
53  //m_atlasSaver = new AtlasSaver();
54  }
55 
57  {
58 
59  }
60 
62  m_objectSaver = objectSaver;
63  }
64 
65 
67  m_animationSaver = animationSaver;
68  }
69 
70 
71  void MapSaver::setAtlasSaver(const FIFE::AtlasSaverPtr& atlasSaver) {
72  m_atlasSaver = atlasSaver;
73  }
74 
75  void MapSaver::save(const Map& map, const std::string& filename, const std::vector<std::string>& importFiles) {
76  TiXmlDocument doc;
77 
78  // add xml declaration
79  TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "ascii", "");
80  doc.LinkEndChild(decl);
81 
82  // add map element
83  TiXmlElement* mapElement = new TiXmlElement("map");
84  mapElement->SetAttribute("id", map.getId());
85  mapElement->SetAttribute("format", "1.0");
86  doc.LinkEndChild(mapElement);
87 
88  for (std::vector<std::string>::const_iterator iter = importFiles.begin(); iter != importFiles.end(); ++iter)
89  {
90  TiXmlElement* importElement = new TiXmlElement("import");
91  importElement->SetAttribute("file", *iter);
92 
93  // link to map element
94  mapElement->LinkEndChild(importElement);
95  }
96 
97  typedef std::list<Layer*> LayerList;
98  LayerList layers = map.getLayers();
99  for (LayerList::iterator iter = layers.begin(); iter != layers.end(); ++iter)
100  {
101  TiXmlElement* layerElement = new TiXmlElement("layer");
102  CellGrid* grid = (*iter)->getCellGrid();
103  layerElement->SetAttribute("id", (*iter)->getId());
104  layerElement->SetDoubleAttribute("x_offset", grid->getXShift());
105  layerElement->SetDoubleAttribute("y_offset", grid->getYShift());
106  layerElement->SetDoubleAttribute("z_offset", grid->getZShift());
107  layerElement->SetDoubleAttribute("x_scale", grid->getXScale());
108  layerElement->SetDoubleAttribute("y_scale", grid->getYScale());
109  layerElement->SetDoubleAttribute("rotation", grid->getRotation());
110  layerElement->SetAttribute("grid_type", grid->getType());
111  layerElement->SetAttribute("transparency", (*iter)->getLayerTransparency());
112 
113  std::string pathingStrategy;
114  switch ((*iter)->getPathingStrategy())
115  {
116  case CELL_EDGES_ONLY:
117  {
118  pathingStrategy = "cell_edges_only";
119  }
120  break;
122  {
123  pathingStrategy = "cell_edges_and_diagonals";
124  }
125  break;
126  default:
127  {
128  pathingStrategy = "cell_edges_only";
129  }
130  break;
131  }
132  layerElement->SetAttribute("pathing", pathingStrategy);
133 
134  std::string sortingStrategy;
135  switch ((*iter)->getSortingStrategy()) {
136  case SORTING_CAMERA:
137  {
138  sortingStrategy = "camera";
139  }
140  break;
141  case SORTING_LOCATION:
142  {
143  sortingStrategy = "location";
144  }
145  break;
147  {
148  sortingStrategy = "camera_and_location";
149  }
150  break;
151  default:
152  {
153  sortingStrategy = "camera";
154  }
155  break;
156  }
157  layerElement->SetAttribute("sorting", sortingStrategy);
158 
159  if ((*iter)->isWalkable()) {
160  layerElement->SetAttribute("layer_type", "walkable");
161  } else if ((*iter)->isInteract()) {
162  layerElement->SetAttribute("layer_type", "interact");
163  layerElement->SetAttribute("layer_type_id", (*iter)->getWalkableId());
164  }
165 
166  // add layer to document
167  mapElement->LinkEndChild(layerElement);
168 
169  // add instances tag to document
170  TiXmlElement* instancesElement = new TiXmlElement("instances");
171  layerElement->LinkEndChild(instancesElement);
172 
173  std::string currentNamespace = "";
174  typedef std::vector<Instance*> InstancesContainer;
175  InstancesContainer instances = (*iter)->getInstances();
176  for (InstancesContainer::iterator iter = instances.begin(); iter != instances.end(); ++iter)
177  {
178  Object* obj = (*iter)->getObject();
179  // don't save part instances
180  if (obj->isMultiPart()) {
181  continue;
182  }
183 
184  // create instance element
185  TiXmlElement* instanceElement = new TiXmlElement("i");
186 
187  if (!obj->getNamespace().empty() && currentNamespace != obj->getNamespace())
188  {
189  instanceElement->SetAttribute("ns", obj->getNamespace());
190 
191  // update current namespace
192  currentNamespace = obj->getNamespace();
193  }
194 
195  if (!(*iter)->getId().empty())
196  {
197  instanceElement->SetAttribute("id", (*iter)->getId());
198  }
199 
200  instanceElement->SetAttribute("o", obj->getId());
201 
202  ExactModelCoordinate position = (*iter)->getLocationRef().getExactLayerCoordinates();
203  instanceElement->SetDoubleAttribute("x", position.x);
204  instanceElement->SetDoubleAttribute("y", position.y);
205  instanceElement->SetDoubleAttribute("z", position.z);
206  instanceElement->SetAttribute("r", (*iter)->getRotation());
207 
208  if ((*iter)->isBlocking())
209  {
210  instanceElement->SetAttribute("blocking", (*iter)->isBlocking());
211  }
212 
213  if ((*iter)->getCellStackPosition() != obj->getCellStackPosition()) {
214  instanceElement->SetAttribute("cellstack", (*iter)->getCellStackPosition());
215  }
216 
217  if ((*iter)->isSpecialCost()) {
218  if (!obj->isSpecialCost()) {
219  instanceElement->SetAttribute("cost_id", (*iter)->getCostId());
220  instanceElement->SetDoubleAttribute("cost", (*iter)->getCost());
221  } else if ((*iter)->getCostId() != obj->getCostId() ||
222  !Mathd::Equal((*iter)->getCost(), obj->getCost())) {
223  instanceElement->SetAttribute("cost_id", (*iter)->getCostId());
224  instanceElement->SetDoubleAttribute("cost", (*iter)->getCost());
225  }
226  }
227 
228  InstanceVisual* instanceVisual = (*iter)->getVisual<InstanceVisual>();
229  instanceElement->SetAttribute("stackpos", instanceVisual->getStackPosition());
230 
231  instancesElement->LinkEndChild(instanceElement);
232  }
233  }
234  // add cellcaches tag to document
235  TiXmlElement* cellcachesElement = new TiXmlElement("cellcaches");
236  mapElement->LinkEndChild(cellcachesElement);
237  for (LayerList::iterator iter = layers.begin(); iter != layers.end(); ++iter) {
238  CellCache* cache = (*iter)->getCellCache();
239  if (!cache) {
240  continue;
241  }
242  // add cellcache tag to document
243  TiXmlElement* cellcacheElement = new TiXmlElement("cellcache");
244  cellcacheElement->SetAttribute("id", (*iter)->getId());
245  cellcacheElement->SetDoubleAttribute("default_cost", cache->getDefaultCostMultiplier());
246  cellcacheElement->SetDoubleAttribute("default_speed", cache->getDefaultSpeedMultiplier());
247  cellcacheElement->SetAttribute("search_narrow", cache->isSearchNarrowCells());
248 
249  const std::set<Cell*>& narrowCells = cache->getNarrowCells();
250  bool saveNarrows = !cache->isSearchNarrowCells() && !narrowCells.empty();
251 
252  const std::vector<std::vector<Cell*> >& cells = cache->getCells();
253  std::vector<std::vector<Cell*> >::const_iterator it = cells.begin();
254  for (; it != cells.end(); ++it) {
255  std::vector<Cell*>::const_iterator cit = (*it).begin();
256  for (; cit != (*it).end(); ++cit) {
257  Cell* cell = *cit;
258  std::list<std::string> costIds = cache->getCosts();
259  bool costsEmpty = costIds.empty();
260  bool defaultCost = cell->defaultCost();
261  bool defaultSpeed = cell->defaultSpeed();
262 
263  // check if area is part of the cell or object
264  std::vector<std::string> areaIds = cache->getCellAreas(cell);
265  std::vector<std::string> cellAreaIds;
266  bool areasEmpty = areaIds.empty();
267  if (!areasEmpty) {
268  const std::set<Instance*>& cellInstances = cell->getInstances();
269  if (!cellInstances.empty()) {
270  std::vector<std::string>::iterator area_it = areaIds.begin();
271  for (; area_it != areaIds.end(); ++area_it) {
272  bool objectArea = false;
273  std::set<Instance*>::const_iterator instance_it = cellInstances.begin();
274  for (; instance_it != cellInstances.end(); ++instance_it) {
275  if ((*instance_it)->getObject()->getArea() == *area_it) {
276  objectArea = true;
277  break;
278  }
279  }
280  if (!objectArea) {
281  cellAreaIds.push_back(*area_it);
282  }
283  }
284  } else {
285  cellAreaIds = areaIds;
286  }
287  areasEmpty = cellAreaIds.empty();
288  }
289 
290  CellTypeInfo cti = cell->getCellType();
291  bool cellBlocker = (cti != CTYPE_CELL_NO_BLOCKER && cti != CTYPE_CELL_BLOCKER);
292  TransitionInfo* transition = cell->getTransition();
293  bool isNarrow = false;
294  if (saveNarrows) {
295  std::set<Cell*>::const_iterator narrow_it = narrowCells.find(cell);
296  if (narrow_it != narrowCells.end()) {
297  isNarrow = true;
298  }
299  }
300  if (costsEmpty && defaultCost && defaultSpeed && areasEmpty &&
301  cellBlocker && !transition && !isNarrow) {
302  continue;
303  }
304  // add cell tag to document
305  ModelCoordinate cellCoord = cell->getLayerCoordinates();
306  TiXmlElement* cellElement = new TiXmlElement("cell");
307  cellElement->SetAttribute("x", cellCoord.x);
308  cellElement->SetAttribute("y", cellCoord.y);
309  if (!defaultCost) {
310  cellElement->SetDoubleAttribute("default_cost", cell->getCostMultiplier());
311  }
312  if (!defaultSpeed) {
313  cellElement->SetDoubleAttribute("default_speed", cell->getSpeedMultiplier());
314  }
315 
316  if (!cellBlocker) {
317  if (cti == CTYPE_CELL_NO_BLOCKER) {
318  cellElement->SetAttribute("blocker_type", "no_blocker");
319  } else {
320  cellElement->SetAttribute("blocker_type", "blocker");
321  }
322  }
323  if (isNarrow) {
324  cellElement->SetAttribute("narrow", true);
325  }
326  // add cost tag
327  if (!costsEmpty) {
328  std::list<std::string>::iterator cost_it = costIds.begin();
329  for (; cost_it != costIds.end(); ++cost_it) {
330  if (cache->existsCostForCell(*cost_it, cell)) {
331  TiXmlElement* costElement = new TiXmlElement("cost");
332  costElement->SetAttribute("id", *cost_it);
333  costElement->SetDoubleAttribute("value", cache->getCost(*cost_it));
334  cellElement->LinkEndChild(costElement);
335  }
336  }
337  }
338  // add area tag
339  if (!areasEmpty) {
340  std::vector<std::string>::iterator area_it = cellAreaIds.begin();
341  for (; area_it != cellAreaIds.end(); ++area_it) {
342  TiXmlElement* areaElement = new TiXmlElement("area");
343  areaElement->SetAttribute("id", *area_it);
344  areaElement->LinkEndChild(areaElement);
345  }
346  }
347  // add transition tag
348  if (transition) {
349  TiXmlElement* transitionElement = new TiXmlElement("transition");
350  transitionElement->SetAttribute("id", transition->m_layer->getId());
351  transitionElement->SetAttribute("x", transition->m_mc.x);
352  transitionElement->SetAttribute("y", transition->m_mc.y);
353  if (transition->m_mc.z != 0) {
354  transitionElement->SetAttribute("z", transition->m_mc.z);
355  }
356  if (transition->m_immediate) {
357  transitionElement->SetAttribute("immediate", true);
358  } else {
359  transitionElement->SetAttribute("immediate", false);
360  }
361  cellElement->LinkEndChild(transitionElement);
362  }
363  cellcacheElement->LinkEndChild(cellElement);
364  }
365  }
366  cellcachesElement->LinkEndChild(cellcacheElement);
367  }
368 
369  TriggerController* triggerController = map.getTriggerController();
370  std::vector<Trigger*> triggers = triggerController->getAllTriggers();
371  if (!triggers.empty()) {
372  // add triggers tag to document
373  TiXmlElement* triggersElement = new TiXmlElement("triggers");
374  mapElement->LinkEndChild(triggersElement);
375  for (std::vector<Trigger*>::iterator iter = triggers.begin(); iter != triggers.end(); ++iter) {
376  // add trigger tag to document
377  TiXmlElement* triggerElement = new TiXmlElement("trigger");
378  triggerElement->SetAttribute("name", (*iter)->getName());
379  triggerElement->SetAttribute("triggered", (*iter)->isTriggered());
380  triggerElement->SetAttribute("all_instances", (*iter)->isEnabledForAllInstances());
381  if ((*iter)->getAttached()) {
382  triggerElement->SetAttribute("attached_instance", (*iter)->getAttached()->getId());
383  triggerElement->SetAttribute("attached_layer", (*iter)->getAttached()->getLocationRef().getLayer()->getId());
384  }
385  const std::vector<Cell*>& cells = (*iter)->getAssignedCells();
386  if (!cells.empty()) {
387  for (std::vector<Cell*>::const_iterator citer = cells.begin(); citer != cells.end(); ++citer) {
388  TiXmlElement* cellElement = new TiXmlElement("assign");
389  cellElement->SetAttribute("layer_id", (*citer)->getLayer()->getId());
390  cellElement->SetAttribute("x", (*citer)->getLayerCoordinates().x);
391  cellElement->SetAttribute("y", (*citer)->getLayerCoordinates().y);
392  triggerElement->LinkEndChild(cellElement);
393  }
394  }
395  const std::vector<Instance*>& instances = (*iter)->getEnabledInstances();
396  if (!instances.empty()) {
397  for (std::vector<Instance*>::const_iterator citer = instances.begin(); citer != instances.end(); ++citer) {
398  TiXmlElement* instanceElement = new TiXmlElement("enabled");
399  instanceElement->SetAttribute("layer_id", (*citer)->getLocationRef().getLayer()->getId());
400  instanceElement->SetAttribute("instance_id", (*citer)->getId());
401  triggerElement->LinkEndChild(instanceElement);
402  }
403  }
404  const std::vector<TriggerCondition>& conditions = (*iter)->getTriggerConditions();
405  if (!conditions.empty()) {
406  for (std::vector<TriggerCondition>::const_iterator citer = conditions.begin(); citer != conditions.end(); ++citer) {
407  TiXmlElement* conditionElement = new TiXmlElement("condition");
408  conditionElement->SetAttribute("id", (*citer));
409  triggerElement->LinkEndChild(conditionElement);
410  }
411  }
412  triggersElement->LinkEndChild(triggerElement);
413  }
414  }
415 
416  typedef std::vector<Camera*> CameraContainer;
417  CameraContainer cameras = map.getCameras();
418  for (CameraContainer::iterator iter = cameras.begin(); iter != cameras.end(); ++iter)
419  {
420  if ((*iter)->getMap()->getId() == map.getId())
421  {
422  TiXmlElement* cameraElement = new TiXmlElement("camera");
423 
424  cameraElement->SetAttribute("id", (*iter)->getId());
425  cameraElement->SetDoubleAttribute("zoom", (*iter)->getZoom());
426  cameraElement->SetDoubleAttribute("tilt", (*iter)->getTilt());
427  cameraElement->SetDoubleAttribute("rotation", (*iter)->getRotation());
428  if ((*iter)->isZToYEnabled()) {
429  cameraElement->SetDoubleAttribute("ztoy", (*iter)->getZToY());
430  }
431 
432  Rect viewport = (*iter)->getViewPort();
433  std::ostringstream viewportString;
434  viewportString << viewport.x << ","
435  << viewport.y << ","
436  << viewport.w << ","
437  << viewport.h;
438 
439  cameraElement->SetAttribute("viewport", viewportString.str());
440 
441  Point p = (*iter)->getCellImageDimensions();
442  cameraElement->SetAttribute("ref_cell_width", p.x);
443  cameraElement->SetAttribute("ref_cell_height", p.y);
444 
445  std::vector<float> lightingColor = (*iter)->getLightingColor();
446  bool writeLightingColor = false;
447  for (uint32_t i=0; i < lightingColor.size(); ++i)
448  {
449  if (lightingColor[i] < 1.0)
450  {
451  writeLightingColor = true;
452  break;
453  }
454  }
455 
456  if (writeLightingColor)
457  {
458  std::ostringstream lightingColorString;
459  for (uint32_t i=0; i < lightingColor.size(); ++i)
460  {
461  if (i > 0)
462  {
463  lightingColorString << ",";
464  }
465 
466  lightingColorString << lightingColor[i];
467 
468  cameraElement->SetAttribute("light_color", lightingColorString.str());
469  }
470  }
471 
472  mapElement->LinkEndChild(cameraElement);
473  }
474  }
475 
476  FILE* fp = 0;
477  #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
478  fp = _fsopen( filename.c_str(), "w", _SH_DENYNO );
479  #else
480  fp = fopen( filename.c_str(), "w" );
481  #endif
482  // save the map xml file
483  doc.SaveFile(fp);
484  fclose(fp);
485  }
486 }
const std::vector< Camera * > & getCameras() const
Get a list containing all cameras.
Definition: map.cpp:291
std::string getCostId() const
Returns the cost id.
Definition: object.cpp:292
double getCostMultiplier()
Returns the current cell cost.
Definition: cell.cpp:237
virtual void setAnimationSaver(const FIFE::AnimationSaverPtr &animationSaver)
allows setting which animation saver will be used to save animation files
Definition: mapsaver.cpp:66
Layer * m_layer
target layer
Definition: cell.h:72
const std::string & getId() const
Get the identifier for this map.
Definition: map.h:104
const double getYShift() const
Get the cellgrid y shift.
Definition: cellgrid.h:163
int32_t getStackPosition()
Gets current stack position of instance.
Definition: visual.cpp:221
T h
Height of the rectangle.
Definition: rect.h:93
Object class.
Definition: object.h:51
Instance visual contains data that is needed to visualize the instance on screen. ...
Definition: visual.h:158
double getSpeedMultiplier()
Returns the current cell speed.
Definition: cell.cpp:253
T x
The X Coordinate.
Definition: rect.h:84
AtlasSaverPtr m_atlasSaver
Definition: mapsaver.h:73
A CellCache is an abstract depiction of one or a few layers and contains additional information...
Definition: cellcache.h:111
const std::string & getId() const
Get the id of this layer.
Definition: layer.cpp:81
const std::list< Layer * > & getLayers() const
Get the layers on this map.
Definition: map.h:120
ObjectSaverPtr m_objectSaver
Definition: mapsaver.h:71
virtual const std::string & getType() const =0
Type of cellgrid.
static Logger _log(LM_AUDIO)
bool defaultCost()
Returns if cell use default cost.
Definition: cell.cpp:229
virtual void setObjectSaver(const FIFE::ObjectSaverPtr &objectSaver)
allows setting which object saver will be used to save object files
Definition: mapsaver.cpp:61
const double getZShift() const
Get the cellgrid z shift.
Definition: cellgrid.h:176
const std::string & getId() const
Definition: object.h:68
std::vector< Trigger * > getAllTriggers()
Returns a vector with all trigger pointers.
const double getRotation() const
Get the cellgrid rotation.
Definition: cellgrid.h:228
double getCost() const
Returns the cost.
Definition: object.cpp:309
uint8_t getCellStackPosition() const
Returns cell stack position.
Definition: object.cpp:265
Simple class to hold the data for transistions.
Definition: cell.h:69
virtual void setAtlasSaver(const FIFE::AtlasSaverPtr &atlasSaver)
allows setting which atlas saver will be used to save atlas files
Definition: mapsaver.cpp:71
static bool Equal(T _val1, T _val2)
Definition: fife_math.h:287
double getDefaultSpeedMultiplier()
Gets default speed for this CellCache.
Definition: cellcache.cpp:1165
const std::set< Instance * > & getInstances()
Returns all instances on this cell.
Definition: cell.cpp:298
uint8_t CellTypeInfo
Definition: cell.h:65
const std::string & getNamespace() const
Definition: object.h:69
std::list< std::string > getCosts()
Returns all registered cost ids.
Definition: cellcache.cpp:1016
CellTypeInfo getCellType()
Returns blocker type.
Definition: cell.cpp:290
bool existsCostForCell(const std::string &costId, Cell *cell)
Gets if cell is assigned to cost identifier.
Definition: cellcache.cpp:1100
const ModelCoordinate getLayerCoordinates() const
Returns the layer coordinates of this cell.
Definition: cell.cpp:310
const double getXShift() const
Get the cellgrid x shift.
Definition: cellgrid.h:150
std::vector< std::string > getCellAreas(Cell *cell)
Returns all areas of a cell.
Definition: cellcache.cpp:1470
A basic cell on a CellCache.
Definition: cell.h:123
T y
The Y Coordinate.
Definition: rect.h:87
const double getXScale() const
Get the cellgrid x-scaling.
Definition: cellgrid.h:205
const double getYScale() const
Get the cellgrid y-scaling.
Definition: cellgrid.h:210
const std::vector< std::vector< Cell * > > & getCells()
Returns all cells of this CellCache.
Definition: cellcache.cpp:715
double getCost(const std::string &costId)
Returns the cost value for the given id.
Definition: cellcache.cpp:1000
virtual void save(const Map &map, const std::string &filename, const std::vector< std::string > &importFiles)
responsible for saving the map resource used to save map files
Definition: mapsaver.cpp:75
TransitionInfo * getTransition()
Returns the transition.
Definition: cell.cpp:380
This class serves as a central place to manage triggers for a Map.
A container of Layer(s).
Definition: map.h:88
const std::set< Cell * > & getNarrowCells()
Returns narrow cells.
Definition: cellcache.cpp:1377
unsigned int uint32_t
Definition: core.h:40
double getDefaultCostMultiplier()
Gets default cost for this CellCache.
Definition: cellcache.cpp:1157
TriggerController * getTriggerController() const
Definition: map.h:220
MapSaver()
constructor
Definition: mapsaver.cpp:50
bool isSpecialCost() const
Gets if object uses special cost.
Definition: object.cpp:275
~MapSaver()
destructor
Definition: mapsaver.cpp:56
bool defaultSpeed()
Returns if cell use default speed.
Definition: cell.cpp:245
T w
Width of the rectangle.
Definition: rect.h:90
bool m_immediate
use immediate
Definition: cell.h:80
bool isSearchNarrowCells()
Gets if narrow cells should be searched automatic.
Definition: cellcache.cpp:1397
bool isMultiPart() const
Gets if object is a part of a multi object.
Definition: object.cpp:393
AnimationSaverPtr m_animationSaver
Definition: mapsaver.h:72
ModelCoordinate m_mc
target coordinates
Definition: cell.h:76