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
++)
54 row_t row
= grid
[layer
];
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 tilesets
.push_back(ts
);
191 Area::TileType
Area::defaultTileType(const Gosu::Bitmap source
, coord_t tiledim
,
194 int x
= tiledim
.x
* (id
% source
.width());
195 int y
= tiledim
.y
* (id
/ source
.height());
198 Gosu::Image
* img
= rc
->bitmapSection(source
, x
, y
,
199 tiledim
.x
, tiledim
.y
, true);
200 tt
.graphics
.push_back(img
);
206 bool Area::processTileType(xmlNode
* node
, Tileset
& ts
)
211 <property name="flags" value="nowalk"/>
212 <property name="onEnter" value="skid();speed(2)"/>
213 <property name="onLeave" value="undo()"/>
218 <property name="animated" value="1"/>
219 <property name="size" value="2"/>
220 <property name="speed" value="2"/>
225 // Initialize a default TileType, we'll build on that.
226 TileType tt
= defaultTileType(ts
.source
,
227 ts
.tiledim
, ts
.defaults
.size());
229 xmlChar
* idstr
= xmlGetProp(node
, BAD_CAST("id"));
230 unsigned id
= atol((const char*)idstr
);
231 if (id
!= ts
.defaults
.size()) {
232 // XXX we need to know the Area we're loading...
233 Log::err("unknown area", std::string("expected TileType id ") +
234 itostr(ts
.defaults
.size()) + ", but got " + itostr(id
));
238 xmlNode
* child
= node
->xmlChildrenNode
; // <properties>
239 child
= node
->xmlChildrenNode
; // <property>
240 for (; child
!= NULL
; child
= child
->next
) {
241 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
242 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
243 if (!xmlStrncmp(child
->name
, BAD_CAST("flags"), 6)) {
246 else if (!xmlStrncmp(name
, BAD_CAST("onEnter"), 8)) {
249 else if (!xmlStrncmp(name
, BAD_CAST("onLeave"), 8)) {
252 else if (!xmlStrncmp(name
, BAD_CAST("animated"), 9)) {
253 tt
.animated
= parseBool((const char*)value
);
255 else if (!xmlStrncmp(name
, BAD_CAST("size"), 5)) {
258 else if (!xmlStrncmp(name
, BAD_CAST("speed"), 6)) {
259 tt
.ani_speed
= atol((const char*)value
);
263 ts
.defaults
.push_back(tt
);
267 bool Area::processLayer(xmlNode
* node
)
271 <layer name="Tiles0" width="5" height="5">
287 xmlChar
* width
= xmlGetProp(node
, BAD_CAST("width"));
288 xmlChar
* height
= xmlGetProp(node
, BAD_CAST("height"));
289 unsigned x
= atol((const char*)width
);
290 unsigned y
= atol((const char*)height
);
292 if (dim
.x
!= x
|| dim
.y
!= y
) {
293 // XXX we need to know the Area we're loading...
294 Log::err("unknown area", "layer x,y size != map x,y size");
298 xmlNode
* child
= node
->xmlChildrenNode
;
299 for (; child
!= NULL
; child
= child
->next
) {
300 if (!xmlStrncmp(child
->name
, BAD_CAST("properties"), 11)) {
301 if (!processLayerProperties(child
))
304 else if (!xmlStrncmp(child
->name
, BAD_CAST("data"), 5)) {
305 if (!processLayerData(child
))
312 bool Area::processLayerProperties(xmlNode
* node
)
317 <property name="layer" value="0"/>
321 xmlNode
* child
= node
->xmlChildrenNode
;
322 for (; child
!= NULL
; child
= child
->next
) {
323 xmlChar
* name
= xmlGetProp(child
, BAD_CAST("name"));
324 xmlChar
* value
= xmlGetProp(child
, BAD_CAST("value"));
325 if (!xmlStrncmp(name
, BAD_CAST("layer"), 6)) {
326 unsigned depth
= atol((const char*)value
);
327 if (depth
!= dim
.z
) {
328 Log::err("unknown area", "invalid layer depth");
337 bool Area::processLayerData(xmlNode
* node
)
355 xmlNode
* child
= node
->xmlChildrenNode
;
356 for (int i
= 1; child
!= NULL
; i
++, child
= child
->next
) {
357 if (!xmlStrncmp(child
->name
, BAD_CAST("tile"), 5)) {
358 xmlChar
* gidStr
= xmlGetProp(child
, BAD_CAST("gid"));
359 unsigned gid
= atol((const char*)gidStr
);
361 t
->type
= &tilesets
[0].defaults
[gid
]; // XXX can only access first tileset
363 if (i
% dim
.x
== 0) {
375 bool Area::processObjectGroup(xmlNode
*)
380 coord_t
Area::getDimensions() const
385 Area::Tile
* Area::getTile(coord_t c
)
387 return map
[c
.z
][c
.y
][c
.x
];