1 | // SuperTux |
2 | // Copyright (C) 2018 Ingo Ruhnke <grumbel@gmail.com> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "worldmap/worldmap_parser.hpp" |
18 | |
19 | #include <physfs.h> |
20 | |
21 | #include "object/ambient_light.hpp" |
22 | #include "object/background.hpp" |
23 | #include "object/decal.hpp" |
24 | #include "object/music_object.hpp" |
25 | #include "object/path_gameobject.hpp" |
26 | #include "object/tilemap.hpp" |
27 | #include "physfs/physfs_file_system.hpp" |
28 | #include "physfs/util.hpp" |
29 | #include "supertux/tile_manager.hpp" |
30 | #include "util/file_system.hpp" |
31 | #include "util/log.hpp" |
32 | #include "util/reader.hpp" |
33 | #include "util/reader_document.hpp" |
34 | #include "util/reader_mapping.hpp" |
35 | #include "util/reader_object.hpp" |
36 | #include "worldmap/level_tile.hpp" |
37 | #include "worldmap/spawn_point.hpp" |
38 | #include "worldmap/special_tile.hpp" |
39 | #include "worldmap/sprite_change.hpp" |
40 | #include "worldmap/teleporter.hpp" |
41 | #include "worldmap/tux.hpp" |
42 | #include "worldmap/worldmap.hpp" |
43 | #include "worldmap/worldmap_parser.hpp" |
44 | #include "worldmap/worldmap_screen.hpp" |
45 | |
46 | namespace worldmap { |
47 | |
48 | WorldMapParser::WorldMapParser(WorldMap& worldmap) : |
49 | m_worldmap(worldmap) |
50 | { |
51 | } |
52 | |
53 | void |
54 | WorldMapParser::load_worldmap(const std::string& filename) |
55 | { |
56 | m_worldmap.m_map_filename = physfsutil::realpath(filename); |
57 | m_worldmap.m_levels_path = FileSystem::dirname(m_worldmap.m_map_filename); |
58 | |
59 | try { |
60 | register_translation_directory(m_worldmap.m_map_filename); |
61 | auto doc = ReaderDocument::from_file(m_worldmap.m_map_filename); |
62 | auto root = doc.get_root(); |
63 | |
64 | if (root.get_name() != "supertux-level" ) |
65 | throw std::runtime_error("file isn't a supertux-level file." ); |
66 | |
67 | auto level_ = root.get_mapping(); |
68 | |
69 | level_.get("name" , m_worldmap.m_name); |
70 | |
71 | std::string tileset_name; |
72 | if (level_.get("tileset" , tileset_name)) { |
73 | if (m_worldmap.m_tileset != nullptr) { |
74 | log_warning << "multiple tilesets specified in level_" << std::endl; |
75 | } else { |
76 | m_worldmap.m_tileset = TileManager::current()->get_tileset(tileset_name); |
77 | } |
78 | } |
79 | /* load default tileset */ |
80 | if (m_worldmap.m_tileset == nullptr) { |
81 | m_worldmap.m_tileset = TileManager::current()->get_tileset("images/worldmap.strf" ); |
82 | } |
83 | |
84 | boost::optional<ReaderMapping> sector; |
85 | if (!level_.get("sector" , sector)) { |
86 | throw std::runtime_error("No sector specified in worldmap file." ); |
87 | } else { |
88 | auto iter = sector->get_iter(); |
89 | while (iter.next()) { |
90 | if (iter.get_key() == "tilemap" ) { |
91 | m_worldmap.add<TileMap>(m_worldmap.m_tileset, iter.as_mapping()); |
92 | } else if (iter.get_key() == "background" ) { |
93 | m_worldmap.add<Background>(iter.as_mapping()); |
94 | } else if (iter.get_key() == "music" ) { |
95 | const auto& sx = iter.get_sexp(); |
96 | if (sx.is_array() && sx.as_array().size() == 2 && sx.as_array()[1].is_string()) { |
97 | std::string value; |
98 | iter.get(value); |
99 | m_worldmap.add<MusicObject>().set_music(value); |
100 | } else { |
101 | m_worldmap.add<MusicObject>(iter.as_mapping()); |
102 | } |
103 | } else if (iter.get_key() == "init-script" ) { |
104 | iter.get(m_worldmap.m_init_script); |
105 | } else if (iter.get_key() == "worldmap-spawnpoint" ) { |
106 | auto sp = std::make_unique<SpawnPoint>(iter.as_mapping()); |
107 | m_worldmap.m_spawn_points.push_back(std::move(sp)); |
108 | } else if (iter.get_key() == "level" ) { |
109 | auto& level = m_worldmap.add<LevelTile>(m_worldmap.m_levels_path, iter.as_mapping()); |
110 | load_level_information(level); |
111 | } else if (iter.get_key() == "special-tile" ) { |
112 | m_worldmap.add<SpecialTile>(iter.as_mapping()); |
113 | } else if (iter.get_key() == "sprite-change" ) { |
114 | m_worldmap.add<SpriteChange>(iter.as_mapping()); |
115 | } else if (iter.get_key() == "teleporter" ) { |
116 | m_worldmap.add<Teleporter>(iter.as_mapping()); |
117 | } else if (iter.get_key() == "decal" ) { |
118 | m_worldmap.add<Decal>(iter.as_mapping()); |
119 | } else if (iter.get_key() == "path" ) { |
120 | m_worldmap.add<PathGameObject>(iter.as_mapping()); |
121 | } else if (iter.get_key() == "ambient-light" ) { |
122 | const auto& sx = iter.get_sexp(); |
123 | if (sx.is_array() && sx.as_array().size() >= 3 && |
124 | sx.as_array()[1].is_real() && sx.as_array()[2].is_real() && sx.as_array()[3].is_real()) |
125 | { |
126 | // for backward compatibilty |
127 | std::vector<float> vColor; |
128 | bool hasColor = sector->get("ambient-light" , vColor); |
129 | if (vColor.size() < 3 || !hasColor) { |
130 | log_warning << "(ambient-light) requires a color as argument" << std::endl; |
131 | } else { |
132 | m_worldmap.add<AmbientLight>(Color(vColor)); |
133 | } |
134 | } else { |
135 | // modern format |
136 | m_worldmap.add<AmbientLight>(iter.as_mapping()); |
137 | } |
138 | } else if (iter.get_key() == "name" ) { |
139 | // skip |
140 | } else { |
141 | log_warning << "Unknown token '" << iter.get_key() << "' in worldmap" << std::endl; |
142 | } |
143 | } |
144 | } |
145 | |
146 | m_worldmap.flush_game_objects(); |
147 | |
148 | if (m_worldmap.get_solid_tilemaps().empty()) |
149 | throw std::runtime_error("No solid tilemap specified" ); |
150 | |
151 | m_worldmap.move_to_spawnpoint("main" ); |
152 | |
153 | } catch(std::exception& e) { |
154 | std::stringstream msg; |
155 | msg << "Problem when parsing worldmap '" << m_worldmap.m_map_filename << "': " << |
156 | e.what(); |
157 | throw std::runtime_error(msg.str()); |
158 | } |
159 | |
160 | m_worldmap.finish_construction(); |
161 | } |
162 | |
163 | void |
164 | WorldMapParser::load_level_information(LevelTile& level) |
165 | { |
166 | /** get special_tile's title */ |
167 | level.m_title = _("<no title>" ); |
168 | level.m_target_time = 0.0f; |
169 | |
170 | try { |
171 | std::string filename = m_worldmap.m_levels_path + level.get_level_filename(); |
172 | |
173 | if (m_worldmap.m_levels_path == "./" ) |
174 | filename = level.get_level_filename(); |
175 | |
176 | if (!PHYSFS_exists(filename.c_str())) |
177 | { |
178 | log_warning << "Level file '" << filename << "' does not exist. Skipping." << std::endl; |
179 | return; |
180 | } |
181 | if (physfsutil::is_directory(filename)) |
182 | { |
183 | log_warning << "Level file '" << filename << "' is a directory. Skipping." << std::endl; |
184 | return; |
185 | } |
186 | |
187 | register_translation_directory(filename); |
188 | auto doc = ReaderDocument::from_file(filename); |
189 | auto root = doc.get_root(); |
190 | if (root.get_name() != "supertux-level" ) { |
191 | return; |
192 | } else { |
193 | auto level_mapping = root.get_mapping(); |
194 | level_mapping.get("name" , level.m_title); |
195 | level_mapping.get("target-time" , level.m_target_time); |
196 | } |
197 | } catch(std::exception& e) { |
198 | log_warning << "Problem when reading level information: " << e.what() << std::endl; |
199 | return; |
200 | } |
201 | } |
202 | |
203 | } // namespace worldmap |
204 | |
205 | /* EOF */ |
206 | |