1 /******************************
2 ** Tsunagari Tile Engine **
4 ** Copyright 2011 OmegaSDG **
5 ******************************/
7 #include <boost/shared_ptr.hpp>
8 #include <libxml/parser.h>
9 #include <libxml/tree.h>
15 #include "resourcer.h"
18 Area::Area(Resourcer
* rc
, Entity
* player
, const std::string descriptor
)
19 : rc(rc
), player(player
), descriptor(descriptor
)
30 if (!processDescriptor()) // Try to load in descriptor.
35 void Area::buttonDown(const Gosu::Button btn
)
37 if (btn
== Gosu::kbRight
)
38 player
->moveByTile(coord(1, 0, 0));
39 else if (btn
== Gosu::kbLeft
)
40 player
->moveByTile(coord(-1, 0, 0));
41 else if (btn
== Gosu::kbUp
)
42 player
->moveByTile(coord(0, -1, 0));
43 else if (btn
== Gosu::kbDown
)
44 player
->moveByTile(coord(0, 1, 0));
49 for (unsigned int layer
= 0; layer
!= map
.size(); layer
++)
51 grid_t grid
= map
[layer
];
52 for (unsigned int y
= 0; y
!= grid
.size(); y
++)
55 for (unsigned int x
= 0; x
!= row
.size(); x
++)
57 // TODO support animations
59 Gosu::Image
* img
= tile
->type
->graphics
[0];
60 img
->draw(x
*img
->width(), y
*img
->height(), 0);
67 bool Area::needsRedraw() const
69 return player
->needsRedraw();
72 bool Area::processDescriptor()
74 xmlDoc
* doc
= rc
->getXMLDoc(descriptor
);
78 // use RAII to ensure doc is freed
79 boost::shared_ptr
<void> alwaysFreeTheDoc(doc
, xmlFreeDoc
);
81 // Iterate and process children of <map>
82 xmlNode
* root
= xmlDocGetRootElement(doc
); // <map> element
84 xmlChar
* width
= xmlGetProp(root
, BAD_CAST("width"));
85 xmlChar
* height
= xmlGetProp(root
, BAD_CAST("height"));
86 dim
.x
= atol((const char*)width
);
87 dim
.y
= atol((const char*)height
);
89 xmlNode
* child
= root
->xmlChildrenNode
;
90 for (; child
!= NULL
; child
= child
->next
) {
91 if (!xmlStrncmp(child
->name
, BAD_CAST("properties"), 11)) {
92 if (!processMapProperties(child
))
95 else if (!xmlStrncmp(child
->name
, BAD_CAST("tileset"), 8)) {
96 if (!processTileset(child
))
99 else if (!xmlStrncmp(child
->name
, BAD_CAST("layer"), 6)) {
100 if (!processLayer(child
))
103 else if (!xmlStrncmp(child
->name
, BAD_CAST("objectgroup"), 12)) {
104 if (!processObjectGroup(child
))
112 bool Area::processMapProperties(xmlNode
* node
)
117 <property name="areaspec" value="1"/>
118 <property name="author" value="Michael D. Reiley"/>
119 <property name="name" value="Baby's First Area"/>
120 <property name="music_loop" value="true"/>
121 <property name="music_main" value="wind.music"/>
122 <property name="onLoad" value="babysfirst_init()"/>
123 <property name="scripts" value="areainits.event,test.event"/>
127 xmlNode
* child
= node
->xmlChildrenNode
;
128 for (; child
!= NULL
; child
= child
->next
) {
129 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
130 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
131 if (!xmlStrncmp(name
, BAD_CAST("author"), 7))
132 author
= (const char*)value
;
133 else if (!xmlStrncmp(name
, BAD_CAST("name"), 5))
134 this->name
= (const char*)value
;
135 else if (!xmlStrncmp(name
, BAD_CAST("music_loop"), 11))
136 main
.loop
= parseBool((const char*)value
);
137 else if (!xmlStrncmp(name
, BAD_CAST("music_main"), 11))
138 main
.filename
= (const char*)value
;
139 else if (!xmlStrncmp(name
, BAD_CAST("onLoad"), 7))
140 onLoadEvents
= (const char*)value
;
141 else if (!xmlStrncmp(name
, BAD_CAST("scripts"), 8))
142 scripts
= (const char*)value
; // TODO split(), load
147 bool Area::processTileset(xmlNode
* node
)
151 <tileset firstgid="1" name="tiles.sheet" tilewidth="64" tileheight="64">
152 <image source="tiles.sheet" width="256" height="256"/>
159 xmlChar
* width
= xmlGetProp(node
, BAD_CAST("tilewidth"));
160 xmlChar
* height
= xmlGetProp(node
, BAD_CAST("tileheight"));
161 ts
.tiledim
.x
= atol((const char*)width
);
162 ts
.tiledim
.y
= atol((const char*)height
);
164 xmlNode
* child
= node
->xmlChildrenNode
;
165 for (; child
!= NULL
; child
= child
->next
) {
166 if (!xmlStrncmp(child
->name
, BAD_CAST("tile"), 5)) {
167 xmlChar
* idstr
= xmlGetProp(child
, BAD_CAST("id"));
168 unsigned id
= atol((const char*)idstr
);
170 // Undeclared TileTypes have default properties.
171 while (ts
.defaults
.size() != id
) {
172 TileType tt
= defaultTileType(ts
.source
,
173 ts
.tiledim
, ts
.defaults
.size());
174 ts
.defaults
.push_back(tt
);
177 // Handle explicit TileType
178 if (!processTileType(child
, ts
))
181 else if (!xmlStrncmp(child
->name
, BAD_CAST("image"), 6)) {
182 xmlChar
* source
= xmlGetProp(child
, BAD_CAST("source"));
183 ts
.source
= rc
->getBitmap((const char*)source
);
187 // Generate default tile types in range (m,n] where m is the last
188 // explicitly declared type and n is the number we require.
189 unsigned srcsz
= ts
.source
.width() * ts
.source
.height();
190 unsigned tilesz
= ts
.tiledim
.x
* ts
.tiledim
.y
;
191 while (ts
.defaults
.size() != srcsz
/ tilesz
) {
192 TileType tt
= defaultTileType(ts
.source
,
193 ts
.tiledim
, ts
.defaults
.size());
194 ts
.defaults
.push_back(tt
);
197 tilesets
.push_back(ts
);
201 Area::TileType
Area::defaultTileType(const Gosu::Bitmap source
, coord_t tiledim
,
204 int x
= (tiledim
.x
* id
) % source
.width();
205 int y
= (tiledim
.y
* id
) / source
.height() * tiledim
.y
; // ???
208 Gosu::Image
* img
= rc
->bitmapSection(source
, x
, y
,
209 tiledim
.x
, tiledim
.y
, true);
210 tt
.graphics
.push_back(img
);
216 bool Area::processTileType(xmlNode
* node
, Tileset
& ts
)
222 <property name="flags" value="nowalk"/>
223 <property name="onEnter" value="skid();speed(2)"/>
224 <property name="onLeave" value="undo()"/>
229 <property name="animated" value="1"/>
230 <property name="size" value="2"/>
231 <property name="speed" value="2"/>
236 // Initialize a default TileType, we'll build on that.
237 TileType tt
= defaultTileType(ts
.source
,
238 ts
.tiledim
, ts
.defaults
.size());
240 xmlChar
* idstr
= xmlGetProp(node
, BAD_CAST("id"));
241 unsigned id
= atol((const char*)idstr
);
242 if (id
!= ts
.defaults
.size()) {
243 // XXX we need to know the Area we're loading...
244 Log::err("unknown area", std::string("expected TileType id ") +
245 itostr(ts
.defaults
.size()) + ", but got " + itostr(id
));
249 xmlNode
* child
= node
->xmlChildrenNode
; // <properties>
250 child
= node
->xmlChildrenNode
; // <property>
251 for (; child
!= NULL
; child
= child
->next
) {
252 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
253 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
254 if (!xmlStrncmp(child
->name
, BAD_CAST("flags"), 6)) {
257 else if (!xmlStrncmp(name
, BAD_CAST("onEnter"), 8)) {
260 else if (!xmlStrncmp(name
, BAD_CAST("onLeave"), 8)) {
263 else if (!xmlStrncmp(name
, BAD_CAST("animated"), 9)) {
264 tt
.animated
= parseBool((const char*)value
);
266 else if (!xmlStrncmp(name
, BAD_CAST("size"), 5)) {
269 else if (!xmlStrncmp(name
, BAD_CAST("speed"), 6)) {
270 tt
.ani_speed
= atol((const char*)value
);
274 ts
.defaults
.push_back(tt
);
278 bool Area::processLayer(xmlNode
* node
)
282 <layer name="Tiles0" width="5" height="5">
298 xmlChar
* width
= xmlGetProp(node
, BAD_CAST("width"));
299 xmlChar
* height
= xmlGetProp(node
, BAD_CAST("height"));
300 unsigned x
= atol((const char*)width
);
301 unsigned y
= atol((const char*)height
);
303 if (dim
.x
!= x
|| dim
.y
!= y
) {
304 // XXX we need to know the Area we're loading...
305 Log::err("unknown area", "layer x,y size != map x,y size");
309 xmlNode
* child
= node
->xmlChildrenNode
;
310 for (; child
!= NULL
; child
= child
->next
) {
311 if (!xmlStrncmp(child
->name
, BAD_CAST("properties"), 11)) {
312 if (!processLayerProperties(child
))
315 else if (!xmlStrncmp(child
->name
, BAD_CAST("data"), 5)) {
316 if (!processLayerData(child
))
323 bool Area::processLayerProperties(xmlNode
* node
)
328 <property name="layer" value="0"/>
332 xmlNode
* child
= node
->xmlChildrenNode
;
333 for (; child
!= NULL
; child
= child
->next
) {
334 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
335 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
336 if (!xmlStrncmp(name
, BAD_CAST("layer"), 6)) {
337 unsigned depth
= atol((const char*)value
);
338 if (depth
!= dim
.z
) {
339 Log::err("unknown area", "invalid layer depth");
348 bool Area::processLayerData(xmlNode
* node
)
366 xmlNode
* child
= node
->xmlChildrenNode
;
367 for (int i
= 1; child
!= NULL
; i
++, child
= child
->next
) {
368 if (!xmlStrncmp(child
->name
, BAD_CAST("tile"), 5)) {
369 xmlChar
* gidStr
= xmlGetProp(child
, BAD_CAST("gid"));
370 unsigned gid
= atol((const char*)gidStr
);
372 t
->type
= &tilesets
[0].defaults
[gid
]; // XXX can only access first tileset
374 if (i
% dim
.x
== 0) {
386 bool Area::processObjectGroup(xmlNode
* node
)
390 <objectgroup name="Prop0" width="5" height="5">
392 <property name="layer" value="0"/>
394 <object name="tile2" type="Tile" gid="7" x="64" y="320">
396 <property name="onEnter" value="speed(0.5)"/>
397 <property name="onLeave" value="undo()"/>
398 <property name="door" value="grassfield.area,1,1,0"/>
399 <property name="flags" value="npc_nowalk"/>
405 xmlChar
* width
= xmlGetProp(node
, BAD_CAST("width"));
406 xmlChar
* height
= xmlGetProp(node
, BAD_CAST("height"));
407 unsigned x
= atol((const char*)width
);
408 unsigned y
= atol((const char*)height
);
412 if (dim
.x
!= x
|| dim
.y
!= y
) {
413 // XXX we need to know the Area we're loading...
414 Log::err("unknown area", "objectgroup x,y size != map x,y size");
418 xmlNode
* child
= node
->xmlChildrenNode
;
419 for (; child
!= NULL
; child
= child
->next
) {
420 if (!xmlStrncmp(child
->name
, BAD_CAST("properties"), 11)) {
421 if (!processObjectGroupProperties(child
, &zpos
))
424 else if (!xmlStrncmp(child
->name
, BAD_CAST("object"), 7)) {
425 if (zpos
== (unsigned)-1 || !processObject(child
, zpos
))
433 bool Area::processObjectGroupProperties(xmlNode
* node
, unsigned* zpos
)
438 <property name="layer" value="0"/>
442 xmlNode
* child
= node
->xmlChildrenNode
;
443 for (; child
!= NULL
; child
= child
->next
) {
444 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
445 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
446 if (!xmlStrncmp(name
, BAD_CAST("layer"), 6)) {
447 int layer
= atol((const char*)value
);
448 if (0 < layer
|| layer
>= (int)dim
.z
) {
449 // XXX we need to know the Area we're loading...
450 Log::err("unknown area",
451 "objectgroup must correspond with layer"
461 bool Area::processObject(xmlNode
* node
, unsigned zpos
)
465 <object name="tile2" type="Tile" gid="7" x="64" y="320">
467 <property name="onEnter" value="speed(0.5)"/>
468 <property name="onLeave" value="undo()"/>
469 <property name="door" value="grassfield.area,1,1,0"/>
470 <property name="flags" value="npc_nowalk"/>
475 xmlChar
* type
= xmlGetProp(node
, BAD_CAST("type"));
476 if (xmlStrncmp(type
, BAD_CAST("Tile"), 5)) {
477 Log::err("unknown area", "object type must be Tile");
481 xmlChar
* xStr
= xmlGetProp(node
, BAD_CAST("x"));
482 xmlChar
* yStr
= xmlGetProp(node
, BAD_CAST("y"));
483 // XXX we ignore the object gid... is that okay?
485 // wouldn't have to access tilesets if we had tiledim ourselves
486 unsigned x
= atol((const char*)xStr
) / tilesets
[0].tiledim
.x
;
487 unsigned y
= atol((const char*)yStr
) / tilesets
[0].tiledim
.y
;
488 y
= y
- 1; // bug in tiled? y is 1 too high
490 // We know which Tile is being talked about now... yay
491 Tile
* t
= map
[zpos
][y
][x
];
493 xmlNode
* child
= node
->xmlChildrenNode
; // <properties>
494 child
= node
->xmlChildrenNode
; // <property>
495 for (; child
!= NULL
; child
= child
->next
) {
496 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
497 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
498 if (!xmlStrncmp(child
->name
, BAD_CAST("flags"), 6)) {
501 else if (!xmlStrncmp(name
, BAD_CAST("onEnter"), 8)) {
504 else if (!xmlStrncmp(name
, BAD_CAST("onLeave"), 8)) {
507 else if (!xmlStrncmp(name
, BAD_CAST("door"), 5)) {
514 coord_t
Area::getDimensions() const
519 Area::Tile
* Area::getTile(coord_t c
)
521 return map
[c
.z
][c
.y
][c
.x
];