1 | // SuperTux |
2 | // Copyright (C) 2006 Matthias Braun <matze@braunis.de> |
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 "supertux/level.hpp" |
18 | |
19 | #include "badguy/goldbomb.hpp" |
20 | #include "object/bonus_block.hpp" |
21 | #include "object/coin.hpp" |
22 | #include "physfs/util.hpp" |
23 | #include "supertux/sector.hpp" |
24 | #include "trigger/secretarea_trigger.hpp" |
25 | #include "util/file_system.hpp" |
26 | #include "util/log.hpp" |
27 | #include "util/writer.hpp" |
28 | |
29 | #include <physfs.h> |
30 | |
31 | Level* Level::s_current = nullptr; |
32 | |
33 | Level::Level(bool worldmap) : |
34 | m_is_worldmap(worldmap), |
35 | m_name("noname" ), |
36 | m_author("SuperTux Player" ), |
37 | m_contact(), |
38 | m_license(), |
39 | m_filename(), |
40 | m_sectors(), |
41 | m_stats(), |
42 | m_target_time(), |
43 | m_tileset("images/tiles.strf" ) |
44 | { |
45 | s_current = this; |
46 | } |
47 | |
48 | Level::~Level() |
49 | { |
50 | m_sectors.clear(); |
51 | } |
52 | |
53 | void |
54 | Level::save(std::ostream& stream) |
55 | { |
56 | Writer writer(stream); |
57 | save(writer); |
58 | } |
59 | |
60 | void |
61 | Level::save(const std::string& filepath, bool retry) |
62 | { |
63 | //FIXME: It tests for directory in supertux/data, but saves into .supertux2. |
64 | try { |
65 | { // make sure the level directory exists |
66 | std::string dirname = FileSystem::dirname(filepath); |
67 | if (!PHYSFS_exists(dirname.c_str())) |
68 | { |
69 | if (!PHYSFS_mkdir(dirname.c_str())) |
70 | { |
71 | std::ostringstream msg; |
72 | msg << "Couldn't create directory for level '" |
73 | << dirname << "': " <<PHYSFS_getLastErrorCode(); |
74 | throw std::runtime_error(msg.str()); |
75 | } |
76 | } |
77 | |
78 | if (!physfsutil::is_directory(dirname)) |
79 | { |
80 | std::ostringstream msg; |
81 | msg << "Level path '" << dirname << "' is not a directory" ; |
82 | throw std::runtime_error(msg.str()); |
83 | } |
84 | } |
85 | |
86 | Writer writer(filepath); |
87 | save(writer); |
88 | log_warning << "Level saved as " << filepath << "." << std::endl; |
89 | } catch(std::exception& e) { |
90 | if (retry) { |
91 | std::stringstream msg; |
92 | msg << "Problem when saving level '" << filepath << "': " << e.what(); |
93 | throw std::runtime_error(msg.str()); |
94 | } else { |
95 | log_warning << "Failed to save the level, retrying..." << std::endl; |
96 | { // create the level directory again |
97 | std::string dirname = FileSystem::dirname(filepath); |
98 | if (!PHYSFS_mkdir(dirname.c_str())) |
99 | { |
100 | std::ostringstream msg; |
101 | msg << "Couldn't create directory for level '" |
102 | << dirname << "': " <<PHYSFS_getLastErrorCode(); |
103 | throw std::runtime_error(msg.str()); |
104 | } |
105 | } |
106 | save(filepath, true); |
107 | } |
108 | } |
109 | } |
110 | |
111 | void |
112 | Level::save(Writer& writer) |
113 | { |
114 | writer.start_list("supertux-level" ); |
115 | // Starts writing to supertux level file. Keep this at the very beginning. |
116 | |
117 | writer.write("version" , 3); |
118 | writer.write("name" , m_name, true); |
119 | writer.write("author" , m_author, false); |
120 | if (!m_contact.empty()) { |
121 | writer.write("contact" , m_contact, false); |
122 | } |
123 | if (!m_license.empty()) { |
124 | writer.write("license" , m_license, false); |
125 | } |
126 | if (m_target_time != 0.0f){ |
127 | writer.write("target-time" , m_target_time); |
128 | } |
129 | |
130 | for (auto& sector : m_sectors) { |
131 | sector->save(writer); |
132 | } |
133 | |
134 | if (m_tileset != "images/tiles.strf" ) |
135 | writer.write("tileset" , m_tileset, false); |
136 | |
137 | // Ends writing to supertux level file. Keep this at the very end. |
138 | writer.end_list("supertux-level" ); |
139 | } |
140 | |
141 | void |
142 | Level::add_sector(std::unique_ptr<Sector> sector) |
143 | { |
144 | Sector* test = get_sector(sector->get_name()); |
145 | if (test != nullptr) { |
146 | throw std::runtime_error("Trying to add 2 sectors with same name" ); |
147 | } else { |
148 | m_sectors.push_back(std::move(sector)); |
149 | } |
150 | } |
151 | |
152 | Sector* |
153 | Level::get_sector(const std::string& name_) const |
154 | { |
155 | for (auto const& sector : m_sectors) { |
156 | if (sector->get_name() == name_) { |
157 | return sector.get(); |
158 | } |
159 | } |
160 | return nullptr; |
161 | } |
162 | |
163 | size_t |
164 | Level::get_sector_count() const |
165 | { |
166 | return m_sectors.size(); |
167 | } |
168 | |
169 | Sector* |
170 | Level::get_sector(size_t num) const |
171 | { |
172 | return m_sectors.at(num).get(); |
173 | } |
174 | |
175 | int |
176 | Level::get_total_coins() const |
177 | { |
178 | int total_coins = 0; |
179 | for (auto const& sector : m_sectors) { |
180 | for (const auto& o: sector->get_objects()) { |
181 | auto coin = dynamic_cast<Coin*>(o.get()); |
182 | if (coin) |
183 | { |
184 | total_coins++; |
185 | continue; |
186 | } |
187 | auto block = dynamic_cast<BonusBlock*>(o.get()); |
188 | if (block) |
189 | { |
190 | if (block->get_contents() == BonusBlock::Content::COIN) |
191 | { |
192 | total_coins += block->get_hit_counter(); |
193 | continue; |
194 | } else if (block->get_contents() == BonusBlock::Content::RAIN || |
195 | block->get_contents() == BonusBlock::Content::EXPLODE) |
196 | { |
197 | total_coins += 10; |
198 | continue; |
199 | } |
200 | } |
201 | auto goldbomb = dynamic_cast<GoldBomb*>(o.get()); |
202 | if (goldbomb) |
203 | total_coins += 10; |
204 | } |
205 | } |
206 | return total_coins; |
207 | } |
208 | |
209 | int |
210 | Level::get_total_badguys() const |
211 | { |
212 | int total_badguys = 0; |
213 | for (auto const& sector : m_sectors) { |
214 | total_badguys += sector->get_object_count<BadGuy>([] (const BadGuy& badguy) { |
215 | return badguy.m_countMe; |
216 | }); |
217 | } |
218 | return total_badguys; |
219 | } |
220 | |
221 | int |
222 | Level::get_total_secrets() const |
223 | { |
224 | int total_secrets = 0; |
225 | for (auto const& sector : m_sectors) { |
226 | total_secrets += sector->get_object_count<SecretAreaTrigger>(); |
227 | } |
228 | return total_secrets; |
229 | } |
230 | |
231 | void |
232 | Level::reactivate() |
233 | { |
234 | s_current = this; |
235 | } |
236 | |
237 | /* EOF */ |
238 | |