2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
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.
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.
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/>.
19 #include "lisp/parser.hpp"
20 #include "lisp/writer.hpp"
21 #include "physfs/ifile_streambuf.hpp"
22 #include "scripting/serialize.hpp"
23 #include "scripting/squirrel_util.hpp"
24 #include "supertux/gameconfig.hpp"
25 #include "supertux/globals.hpp"
26 #include "supertux/player_status.hpp"
27 #include "supertux/screen_fade.hpp"
28 #include "supertux/screen_manager.hpp"
29 #include "supertux/world.hpp"
30 #include "supertux/world_state.hpp"
31 #include "util/file_system.hpp"
32 #include "util/reader.hpp"
33 #include "util/string_util.hpp"
34 #include "worldmap/worldmap.hpp"
36 std::unique_ptr<World>
37 World::load(const std::string& directory)
39 std::unique_ptr<World> world(new World);
41 world->load_(directory);
43 { // generate savegame filename
44 std::string worlddirname = FileSystem::basename(directory);
45 std::ostringstream stream;
46 stream << "profile" << g_config->profile << "/" << worlddirname << ".stsg";
47 std::string slotfile = stream.str();
48 world->m_savegame_filename = stream.str();
51 return std::move(world);
57 m_worldmap_filename(),
58 m_savegame_filename(),
62 m_world_state(new WorldState),
63 m_hide_from_contribs(false),
66 sq_resetobject(&m_world_thread);
71 sq_release(scripting::global_vm, &m_world_thread);
75 World::load_(const std::string& directory)
77 m_basedir = directory;
78 m_worldmap_filename = m_basedir + "/worldmap.stwm";
81 const lisp::Lisp* root = parser.parse(m_basedir + "/info");
83 const lisp::Lisp* info = root->get_lisp("supertux-world");
85 info = root->get_lisp("supertux-level-subset");
87 throw std::runtime_error("File is not a world or levelsubset file");
89 m_hide_from_contribs = false;
92 info->get("title", m_title);
93 info->get("description", m_description);
94 info->get("levelset", m_is_levelset);
95 info->get("hide-from-contribs", m_hide_from_contribs);
97 // Level info file doesn't define any levels, so read the
98 // directory to see what we can find
100 char** files = PHYSFS_enumerateFiles(m_basedir.c_str());
103 log_warning << "Couldn't read subset dir '" << m_basedir << "'" << std::endl;
107 for(const char* const* filename = files; *filename != 0; ++filename)
109 if(StringUtil::has_suffix(*filename, ".stl"))
111 m_levels.push_back(*filename);
114 PHYSFS_freeList(files);
116 std::sort(m_levels.begin(), m_levels.end(), StringUtil::numeric_less);
122 // create new squirrel table for persistent game state
123 HSQUIRRELVM vm = scripting::global_vm;
125 sq_pushroottable(vm);
126 sq_pushstring(vm, "state", -1);
128 if(SQ_FAILED(sq_createslot(vm, -3)))
130 throw scripting::SquirrelError(vm, "Couldn't create state table");
138 std::string filename = m_basedir + "/world.nut";
141 IFileStreambuf ins(filename);
142 std::istream in(&ins);
144 sq_release(scripting::global_vm, &m_world_thread);
145 m_world_thread = scripting::create_thread(scripting::global_vm);
146 scripting::compile_and_run(scripting::object_to_vm(m_world_thread), in, filename);
148 catch(const std::exception& )
150 // fallback: try to load worldmap worldmap.stwm
151 g_screen_manager->push_screen(std::unique_ptr<Screen>(
152 new worldmap::WorldMap(m_worldmap_filename,
153 get_player_status())));
161 m_world_state->save(m_savegame_filename);
167 m_world_state->load(m_savegame_filename);
171 World::get_level_filename(unsigned int i) const
173 return FileSystem::join(m_basedir, m_levels[i]);
177 World::get_num_levels() const
179 return static_cast<int>(m_levels.size());
183 World::get_num_solved_levels() const
185 int num_solved_levels = 0;
187 HSQUIRRELVM vm = scripting::global_vm;
188 int oldtop = sq_gettop(vm);
190 sq_pushroottable(vm);
191 sq_pushstring(vm, "state", -1);
192 if(SQ_FAILED(sq_get(vm, -2)))
194 log_warning << "failed to get 'state' table" << std::endl;
198 sq_pushstring(vm, "worlds", -1);
199 if(SQ_FAILED(sq_get(vm, -2)))
201 log_warning << "failed to get 'state.worlds' table" << std::endl;
205 sq_pushstring(vm, m_worldmap_filename.c_str(), -1);
206 if(SQ_FAILED(sq_get(vm, -2)))
208 log_warning << "failed to get state.worlds['" << m_worldmap_filename << "']" << std::endl;
212 sq_pushstring(vm, "levels", -1);
213 if(SQ_FAILED(sq_get(vm, -2)))
215 log_warning << "failed to get state.worlds['" << m_worldmap_filename << "'].levels" << std::endl;
219 for(auto level : m_levels)
221 sq_pushstring(vm, level.c_str(), -1);
222 if(SQ_FAILED(sq_get(vm, -2)))
224 log_warning << "failed to get state.worlds['" << m_worldmap_filename << "'].levels['"
225 << level << "']" << std::endl;
229 bool solved = scripting::read_bool(vm, "solved");
232 num_solved_levels += 1;
242 sq_settop(vm, oldtop);
244 return num_solved_levels;
248 World::get_basedir() const
254 World::get_title() const