2 // Copyright (C) 2008 Matthias Braun <matze@braunis.de>
3 // Ingo Ruhnke <grumbel@gmx.de>
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "supertux/tile_set_parser.hpp"
23 #include "lisp/list_iterator.hpp"
24 #include "lisp/parser.hpp"
25 #include "supertux/tile_set.hpp"
26 #include "util/file_system.hpp"
28 TileSetParser::TileSetParser(TileSet& tileset, const std::string& filename) :
36 TileSetParser::parse()
38 m_tiles_path = FileSystem::dirname(m_filename);
40 m_tileset.tiles.resize(1, 0);
41 m_tileset.tiles[0] = new Tile(m_tileset);
44 const lisp::Lisp* root = parser.parse(m_filename);
46 const lisp::Lisp* tiles_lisp = root->get_lisp("supertux-tiles");
48 throw std::runtime_error("file is not a supertux tiles file.");
50 lisp::ListIterator iter(tiles_lisp);
53 if (iter.item() == "tile")
55 std::auto_ptr<Tile> tile(new Tile(m_tileset));
56 uint32_t id = parse_tile(*tile, *iter.lisp());
58 if (id >= m_tileset.tiles.size())
59 m_tileset.tiles.resize(id+1, 0);
61 if (m_tileset.tiles[id] != 0)
63 log_warning << "Tile with ID " << id << " redefined" << std::endl;
67 m_tileset.tiles[id] = tile.release();
70 else if (iter.item() == "tilegroup")
72 /* tilegroups are only interesting for the editor */
74 else if (iter.item() == "tiles")
76 parse_tiles(*iter.lisp());
80 log_warning << "Unknown symbol '" << iter.item() << "' in tileset file" << std::endl;
86 TileSetParser::parse_tile(Tile& tile, const Reader& reader)
89 if (!reader.get("id", id))
91 throw std::runtime_error("Missing tile-id.");
95 if(reader.get("solid", value) && value)
96 tile.attributes |= Tile::SOLID;
97 if(reader.get("unisolid", value) && value)
98 tile.attributes |= Tile::UNISOLID | Tile::SOLID;
99 if(reader.get("brick", value) && value)
100 tile.attributes |= Tile::BRICK;
101 if(reader.get("ice", value) && value)
102 tile.attributes |= Tile::ICE;
103 if(reader.get("water", value) && value)
104 tile.attributes |= Tile::WATER;
105 if(reader.get("hurts", value) && value)
106 tile.attributes |= Tile::HURTS;
107 if(reader.get("fire", value) && value)
108 tile.attributes |= Tile::FIRE;
109 if(reader.get("fullbox", value) && value)
110 tile.attributes |= Tile::FULLBOX;
111 if(reader.get("coin", value) && value)
112 tile.attributes |= Tile::COIN;
113 if(reader.get("goal", value) && value)
114 tile.attributes |= Tile::GOAL;
116 if(reader.get("north", value) && value)
117 tile.data |= Tile::WORLDMAP_NORTH;
118 if(reader.get("south", value) && value)
119 tile.data |= Tile::WORLDMAP_SOUTH;
120 if(reader.get("west", value) && value)
121 tile.data |= Tile::WORLDMAP_WEST;
122 if(reader.get("east", value) && value)
123 tile.data |= Tile::WORLDMAP_EAST;
124 if(reader.get("stop", value) && value)
125 tile.data |= Tile::WORLDMAP_STOP;
127 reader.get("data", tile.data);
128 reader.get("anim-fps", tile.anim_fps);
130 if(reader.get("slope-type", tile.data)) {
131 tile.attributes |= Tile::SOLID | Tile::SLOPE;
134 const lisp::Lisp* images;
136 images = reader.get_lisp("editor-images");
138 parse_tile_images(tile, *images);
141 images = reader.get_lisp("images");
143 parse_tile_images(tile, *images);
148 tile.correct_attributes();
154 TileSetParser::parse_tile_images(Tile& tile, const Reader& images_lisp)
156 const lisp::Lisp* list = &images_lisp;
159 const lisp::Lisp* cur = list->get_car();
161 if(cur->get_type() == lisp::Lisp::TYPE_STRING)
165 tile.imagespecs.push_back(Tile::ImageSpec(m_tiles_path + file, Rect(0, 0, 0, 0)));
167 else if(cur->get_type() == lisp::Lisp::TYPE_CONS &&
168 cur->get_car()->get_type() == lisp::Lisp::TYPE_SYMBOL &&
169 cur->get_car()->get_symbol() == "region")
171 const lisp::Lisp* ptr = cur->get_cdr();
178 ptr->get_car()->get(file); ptr = ptr->get_cdr();
179 ptr->get_car()->get(x); ptr = ptr->get_cdr();
180 ptr->get_car()->get(y); ptr = ptr->get_cdr();
181 ptr->get_car()->get(w); ptr = ptr->get_cdr();
182 ptr->get_car()->get(h);
183 tile.imagespecs.push_back(Tile::ImageSpec(m_tiles_path + file, Rect(x, y, x+w, y+h)));
187 log_warning << "Expected string or list in images tag" << std::endl;
190 list = list->get_cdr();
195 TileSetParser::parse_tiles(const Reader& reader)
197 // List of ids (use 0 if the tile should be ignored)
198 std::vector<uint32_t> ids;
199 // List of attributes of the tile
200 std::vector<uint32_t> attributes;
201 // List of data for the tiles
202 std::vector<uint32_t> datas;
203 //List of frames that the tiles come in
204 std::vector<std::string> images;
206 // width and height of the image in tile units, this is used for two
208 // a) so we don't have to load the image here to know its dimensions
209 // b) so that the resulting 'tiles' entry is more robust,
210 // ie. enlarging the image won't break the tile id mapping
211 // FIXME: height is actually not used, since width might be enough for
212 // all purposes, still feels somewhat more natural this way
213 unsigned int width = 0;
214 unsigned int height = 0;
216 reader.get("ids", ids);
217 bool has_attributes = reader.get("attributes", attributes);
218 bool has_datas = reader.get("datas", datas);
220 if (!reader.get("image", images))
222 reader.get( "images", images);
225 // make the image path absolute
226 for(std::vector<std::string>::iterator i = images.begin(); i != images.end(); ++i)
228 *i = m_tiles_path + *i;
231 reader.get("width", width);
232 reader.get("height", height);
235 reader.get("anim-fps", animfps);
237 if (images.size() <= 0)
239 throw std::runtime_error("No images in tile.");
241 else if (animfps < 0)
243 throw std::runtime_error("Negative fps.");
245 else if (ids.size() != width*height)
247 std::ostringstream err;
248 err << "Number of ids (" << ids.size() << ") and size of image (" << width*height
249 << ") mismatch for image '" << images[0] << "', but must be equal";
250 throw std::runtime_error(err.str());
252 else if (has_attributes && ids.size() != attributes.size())
254 std::ostringstream err;
255 err << "Number of ids (" << ids.size() << ") and attributes (" << attributes.size()
256 << ") mismatch for image '" << images[0] << "', but must be equal";
257 throw std::runtime_error(err.str());
259 else if (has_datas && ids.size() != datas.size())
261 std::ostringstream err;
262 err << "Number of ids (" << ids.size() << ") and datas (" << datas.size()
263 << ") mismatch for image '" << images[0] << "', but must be equal";
264 throw std::runtime_error(err.str());
268 for(std::vector<uint32_t>::size_type i = 0; i < ids.size() && i < width*height; ++i)
272 if (ids[i] >= m_tileset.tiles.size())
273 m_tileset.tiles.resize(ids[i]+1, 0);
275 int x = 32*(i % width);
276 int y = 32*(i / width);
277 std::auto_ptr<Tile> tile(new Tile(m_tileset, images, Rect(x, y, x + 32, y + 32),
278 (has_attributes ? attributes[i] : 0), (has_datas ? datas[i] : 0), animfps));
279 if (m_tileset.tiles[ids[i]] == 0) {
280 m_tileset.tiles[ids[i]] = tile.release();
282 log_warning << "Tile with ID " << ids[i] << " redefined" << std::endl;