X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fworldmap%2Fworldmap.cpp;h=f6be69f316dbc244daf5d07c89e5a11c1f57b951;hb=82bb12ef9e35fb9af8fab2de99ad853f57391d33;hp=e25c1628bf8e6fb1d19415fd52f14507128da4eb;hpb=691335a74a0717e839f440c6151f1517cf89548d;p=supertux.git diff --git a/src/worldmap/worldmap.cpp b/src/worldmap/worldmap.cpp index e25c1628b..f6be69f31 100644 --- a/src/worldmap/worldmap.cpp +++ b/src/worldmap/worldmap.cpp @@ -1,13 +1,11 @@ -// $Id$ -// // SuperTux - A Jump'n Run -// Copyright (C) 2004 Ingo Ruhnke +// Copyright (C) 2004 Ingo Ruhnke // Copyright (C) 2006 Christoph Sommer // -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,130 +13,105 @@ // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// along with this program. If not, see . + +#include "worldmap/worldmap.hpp" + #include -#include +#include #include -#include -#include -#include +#include +#include #include +#include #include -#include - -#include "worldmap.hpp" +#include -#include "gettext.hpp" -#include "log.hpp" -#include "mainloop.hpp" -#include "shrinkfade.hpp" -#include "video/surface.hpp" -#include "video/drawing_context.hpp" -#include "sprite/sprite.hpp" -#include "sprite/sprite_manager.hpp" #include "audio/sound_manager.hpp" -#include "lisp/parser.hpp" -#include "lisp/lisp.hpp" -#include "lisp/list_iterator.hpp" -#include "lisp/writer.hpp" -#include "game_session.hpp" -#include "sector.hpp" -#include "worldmap.hpp" -#include "resources.hpp" -#include "log.hpp" -#include "world.hpp" -#include "player_status.hpp" -#include "textscroller.hpp" -#include "main.hpp" -#include "spawn_point.hpp" -#include "file_system.hpp" +#include "control/input_manager.hpp" #include "gui/menu.hpp" +#include "gui/menu_manager.hpp" #include "gui/mousecursor.hpp" -#include "control/joystickkeyboardcontroller.hpp" +#include "lisp/lisp.hpp" +#include "lisp/list_iterator.hpp" +#include "lisp/parser.hpp" #include "object/background.hpp" +#include "object/decal.hpp" #include "object/tilemap.hpp" -#include "options_menu.hpp" +#include "physfs/ifile_streambuf.hpp" +#include "scripting/scripting.hpp" #include "scripting/squirrel_error.hpp" #include "scripting/squirrel_util.hpp" +#include "sprite/sprite.hpp" +#include "sprite/sprite_manager.hpp" +#include "supertux/game_session.hpp" +#include "supertux/gameconfig.hpp" +#include "supertux/globals.hpp" +#include "supertux/menu/menu_storage.hpp" +#include "supertux/menu/options_menu.hpp" +#include "supertux/menu/worldmap_menu.hpp" +#include "supertux/player_status.hpp" +#include "supertux/resources.hpp" +#include "supertux/savegame.hpp" +#include "supertux/screen_manager.hpp" +#include "supertux/sector.hpp" +#include "supertux/shrinkfade.hpp" +#include "supertux/spawn_point.hpp" +#include "supertux/textscroller.hpp" +#include "supertux/tile_manager.hpp" +#include "supertux/tile_set.hpp" +#include "supertux/world.hpp" +#include "util/file_system.hpp" +#include "util/gettext.hpp" +#include "util/log.hpp" +#include "util/reader.hpp" +#include "video/drawing_context.hpp" +#include "video/surface.hpp" #include "worldmap/level.hpp" #include "worldmap/special_tile.hpp" -#include "worldmap/tux.hpp" #include "worldmap/sprite_change.hpp" +#include "worldmap/tux.hpp" +#include "worldmap/worldmap.hpp" -namespace WorldMapNS { +static const float CAMERA_PAN_SPEED = 5.0; -enum WorldMapMenuIDs { - MNID_RETURNWORLDMAP, - MNID_QUITWORLDMAP -}; +namespace worldmap { WorldMap* WorldMap::current_ = NULL; -Direction reverse_dir(Direction direction) -{ - switch(direction) - { - case D_WEST: - return D_EAST; - case D_EAST: - return D_WEST; - case D_NORTH: - return D_SOUTH; - case D_SOUTH: - return D_NORTH; - case D_NONE: - return D_NONE; - } - return D_NONE; -} - -std::string -direction_to_string(Direction direction) +WorldMap::WorldMap(const std::string& filename, Savegame& savegame, const std::string& force_spawnpoint_) : + tux(), + m_savegame(savegame), + tileset(NULL), + free_tileset(false), + camera_offset(), + name(), + music(), + init_script(), + game_objects(), + solid_tilemaps(), + passive_message_timer(), + passive_message(), + map_filename(), + levels_path(), + special_tiles(), + levels(), + sprite_changes(), + spawn_points(), + teleporters(), + total_stats(), + worldmap_table(), + scripts(), + ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), + force_spawnpoint(force_spawnpoint_), + in_level(false), + pan_pos(), + panning(false), + last_position(), + last_target_time() { - switch(direction) - { - case D_WEST: - return "west"; - case D_EAST: - return "east"; - case D_NORTH: - return "north"; - case D_SOUTH: - return "south"; - default: - return "none"; - } -} - -Direction -string_to_direction(const std::string& directory) -{ - if (directory == "west") - return D_WEST; - else if (directory == "east") - return D_EAST; - else if (directory == "north") - return D_NORTH; - else if (directory == "south") - return D_SOUTH; - else if (directory == "none") - return D_NONE; - else { - log_warning << "unknown direction: \"" << directory << "\"" << std::endl; - return D_NONE; - } -} - -//--------------------------------------------------------------------------- - -WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpoint) - : tux(0), ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), force_spawnpoint(force_spawnpoint), in_level(false) -{ - tile_manager.reset(new TileManager("images/worldmap.strf")); - - tux = new Tux(this); + tux = std::make_shared(this); add_object(tux); name = ""; @@ -146,45 +119,39 @@ WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpo total_stats.reset(); - worldmap_menu.reset(new Menu()); - worldmap_menu->add_label(_("Pause")); - worldmap_menu->add_hl(); - worldmap_menu->add_entry(MNID_RETURNWORLDMAP, _("Continue")); - worldmap_menu->add_submenu(_("Options"), get_options_menu()); - worldmap_menu->add_hl(); - worldmap_menu->add_entry(MNID_QUITWORLDMAP, _("Quit World")); - // create a new squirrel table for the worldmap - using namespace Scripting; + using namespace scripting; sq_collectgarbage(global_vm); sq_newtable(global_vm); sq_pushroottable(global_vm); if(SQ_FAILED(sq_setdelegate(global_vm, -2))) - throw Scripting::SquirrelError(global_vm, "Couldn't set worldmap_table delegate"); + throw scripting::SquirrelError(global_vm, "Couldn't set worldmap_table delegate"); sq_resetobject(&worldmap_table); if(SQ_FAILED(sq_getstackobj(global_vm, -1, &worldmap_table))) - throw Scripting::SquirrelError(global_vm, "Couldn't get table from stack"); + throw scripting::SquirrelError(global_vm, "Couldn't get table from stack"); sq_addref(global_vm, &worldmap_table); sq_pop(global_vm, 1); - sound_manager->preload("sounds/warp.wav"); - + SoundManager::current()->preload("sounds/warp.wav"); + // load worldmap objects load(filename); } WorldMap::~WorldMap() { - using namespace Scripting; + using namespace scripting; + + if(free_tileset) + delete tileset; for(GameObjects::iterator i = game_objects.begin(); i != game_objects.end(); ++i) { - GameObject* object = *i; + GameObjectPtr& object = *i; try_unexpose(object); - object->unref(); } for(SpawnPoints::iterator i = spawn_points.begin(); @@ -206,40 +173,39 @@ WorldMap::~WorldMap() } void -WorldMap::add_object(GameObject* object) +WorldMap::add_object(GameObjectPtr object) { - TileMap* tilemap = dynamic_cast (object); + TileMap* tilemap = dynamic_cast(object.get()); if(tilemap != 0 && tilemap->is_solid()) { solid_tilemaps.push_back(tilemap); } - object->ref(); try_expose(object); game_objects.push_back(object); } void -WorldMap::try_expose(GameObject* object) +WorldMap::try_expose(const GameObjectPtr& object) { - ScriptInterface* interface = dynamic_cast (object); - if(interface != NULL) { - HSQUIRRELVM vm = Scripting::global_vm; + ScriptInterface* object_ = dynamic_cast(object.get()); + if(object_ != NULL) { + HSQUIRRELVM vm = scripting::global_vm; sq_pushobject(vm, worldmap_table); - interface->expose(vm, -1); + object_->expose(vm, -1); sq_pop(vm, 1); } } void -WorldMap::try_unexpose(GameObject* object) +WorldMap::try_unexpose(const GameObjectPtr& object) { - ScriptInterface* interface = dynamic_cast (object); - if(interface != NULL) { - HSQUIRRELVM vm = Scripting::global_vm; + ScriptInterface* object_ = dynamic_cast(object.get()); + if(object_ != NULL) { + HSQUIRRELVM vm = scripting::global_vm; SQInteger oldtop = sq_gettop(vm); sq_pushobject(vm, worldmap_table); try { - interface->unexpose(vm, -1); + object_->unexpose(vm, -1); } catch(std::exception& e) { log_warning << "Couldn't unregister object: " << e.what() << std::endl; } @@ -248,7 +214,7 @@ WorldMap::try_unexpose(GameObject* object) } void -WorldMap::move_to_spawnpoint(const std::string& spawnpoint) +WorldMap::move_to_spawnpoint(const std::string& spawnpoint, bool pan) { for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) { SpawnPoint* sp = *i; @@ -256,6 +222,11 @@ WorldMap::move_to_spawnpoint(const std::string& spawnpoint) Vector p = sp->pos; tux->set_tile_pos(p); tux->set_direction(sp->auto_dir); + if(pan) { + panning = true; + pan_pos = get_camera_pos_for_tux(); + clamp_camera_position(pan_pos); + } return; } } @@ -266,10 +237,10 @@ WorldMap::move_to_spawnpoint(const std::string& spawnpoint) } void -WorldMap::change(const std::string& filename, const std::string& force_spawnpoint) +WorldMap::change(const std::string& filename, const std::string& force_spawnpoint_) { - main_loop->exit_screen(); - main_loop->push_screen(new WorldMap(filename, force_spawnpoint)); + ScreenManager::current()->pop_screen(); + ScreenManager::current()->push_screen(std::unique_ptr(new WorldMap(filename, m_savegame, force_spawnpoint_))); } void @@ -282,48 +253,70 @@ WorldMap::load(const std::string& filename) lisp::Parser parser; const lisp::Lisp* root = parser.parse(map_filename); - const lisp::Lisp* lisp = root->get_lisp("supertux-level"); - if(!lisp) + const lisp::Lisp* level_ = root->get_lisp("supertux-level"); + if(level_ == NULL) throw std::runtime_error("file isn't a supertux-level file."); - lisp->get("name", name); + level_->get("name", name); - const lisp::Lisp* sector = lisp->get_lisp("sector"); + const lisp::Lisp* sector = level_->get_lisp("sector"); if(!sector) - throw std::runtime_error("No sector sepcified in worldmap file."); + throw std::runtime_error("No sector specified in worldmap file."); + + const lisp::Lisp* tilesets_lisp = level_->get_lisp("tilesets"); + if(tilesets_lisp != NULL) { + tileset = TileManager::current()->parse_tileset_definition(*tilesets_lisp).release(); + free_tileset = true; + } + std::string tileset_name; + if(level_->get("tileset", tileset_name)) { + if(tileset != NULL) { + log_warning << "multiple tilesets specified in level_" << std::endl; + } else { + tileset = TileManager::current()->get_tileset(tileset_name); + } + } + /* load default tileset */ + if(tileset == NULL) { + tileset = TileManager::current()->get_tileset("images/worldmap.strf"); + } + current_tileset = tileset; lisp::ListIterator iter(sector); while(iter.next()) { if(iter.item() == "tilemap") { - add_object(new TileMap(*(iter.lisp()), tile_manager.get())); + add_object(std::make_shared(*(iter.lisp()))); } else if(iter.item() == "background") { - add_object(new Background(*(iter.lisp()))); + add_object(std::make_shared(*(iter.lisp()))); } else if(iter.item() == "music") { iter.value()->get(music); } else if(iter.item() == "init-script") { iter.value()->get(init_script); } else if(iter.item() == "worldmap-spawnpoint") { - SpawnPoint* sp = new SpawnPoint(iter.lisp()); + SpawnPoint* sp = new SpawnPoint(*iter.lisp()); spawn_points.push_back(sp); } else if(iter.item() == "level") { - LevelTile* level = new LevelTile(levels_path, iter.lisp()); - levels.push_back(level); + auto level = std::make_shared(levels_path, *iter.lisp()); + levels.push_back(level.get()); add_object(level); } else if(iter.item() == "special-tile") { - SpecialTile* special_tile = new SpecialTile(iter.lisp()); - special_tiles.push_back(special_tile); + auto special_tile = std::make_shared(*iter.lisp()); + special_tiles.push_back(special_tile.get()); add_object(special_tile); } else if(iter.item() == "sprite-change") { - SpriteChange* sprite_change = new SpriteChange(iter.lisp()); - sprite_changes.push_back(sprite_change); + auto sprite_change = std::make_shared(*iter.lisp()); + sprite_changes.push_back(sprite_change.get()); add_object(sprite_change); } else if(iter.item() == "teleporter") { - Teleporter* teleporter = new Teleporter(iter.lisp()); - teleporters.push_back(teleporter); + auto teleporter = std::make_shared(*iter.lisp()); + teleporters.push_back(teleporter.get()); add_object(teleporter); + } else if(iter.item() == "decal") { + auto decal = std::make_shared(*iter.lisp()); + add_object(decal); } else if(iter.item() == "ambient-light") { std::vector vColor; - sector->get_vector( "ambient-light", vColor ); + sector->get( "ambient-light", vColor ); if(vColor.size() < 3) { log_warning << "(ambient-light) requires a color as argument" << std::endl; } else { @@ -335,7 +328,9 @@ WorldMap::load(const std::string& filename) log_warning << "Unknown token '" << iter.item() << "' in worldmap" << std::endl; } } - if(solid_tilemaps.size() == 0) + current_tileset = NULL; + + if(solid_tilemaps.empty()) throw std::runtime_error("No solid tilemap specified"); move_to_spawnpoint("main"); @@ -369,6 +364,33 @@ WorldMap::get_level_title(LevelTile& level) } } +void +WorldMap::get_level_target_time(LevelTile& level) +{ + if(last_position == tux->get_tile_pos()) { + level.target_time = last_target_time; + return; + } + + try { + lisp::Parser parser; + const lisp::Lisp* root = parser.parse(levels_path + level.get_name()); + + const lisp::Lisp* level_lisp = root->get_lisp("supertux-level"); + if(!level_lisp) + return; + + level_lisp->get("target-time", level.target_time); + + last_position = level.pos; + last_target_time = level.target_time; + + } catch(std::exception& e) { + log_warning << "Problem when reading level target time: " << e.what() << std::endl; + return; + } +} + void WorldMap::calculate_total_stats() { total_stats.zero(); @@ -384,11 +406,11 @@ void WorldMap::on_escape_press() { // Show or hide the menu - if(!Menu::current()) { - Menu::set_current(worldmap_menu.get()); + if(!MenuManager::instance().is_active()) { + MenuManager::instance().set_menu(MenuStorage::WORLDMAP_MENU); tux->set_direction(D_NONE); // stop tux movement when menu is called } else { - Menu::set_current(NULL); + MenuManager::instance().clear_menu_stack(); } } @@ -421,36 +443,36 @@ WorldMap::path_ok(Direction direction, const Vector& old_pos, Vector* new_pos) if (!(new_pos->x >= 0 && new_pos->x < get_width() && new_pos->y >= 0 && new_pos->y < get_height())) - { // New position is outsite the tilemap - return false; - } + { // New position is outsite the tilemap + return false; + } else - { // Check if the tile allows us to go to new_pos - int old_tile_data = tile_data_at(old_pos); - int new_tile_data = tile_data_at(*new_pos); - switch(direction) - { - case D_WEST: - return (old_tile_data & Tile::WORLDMAP_WEST - && new_tile_data & Tile::WORLDMAP_EAST); - - case D_EAST: - return (old_tile_data & Tile::WORLDMAP_EAST - && new_tile_data & Tile::WORLDMAP_WEST); - - case D_NORTH: - return (old_tile_data & Tile::WORLDMAP_NORTH - && new_tile_data & Tile::WORLDMAP_SOUTH); - - case D_SOUTH: - return (old_tile_data & Tile::WORLDMAP_SOUTH - && new_tile_data & Tile::WORLDMAP_NORTH); - - case D_NONE: - assert(!"path_ok() can't walk if direction is NONE"); - } - return false; + { // Check if the tile allows us to go to new_pos + int old_tile_data = tile_data_at(old_pos); + int new_tile_data = tile_data_at(*new_pos); + switch(direction) + { + case D_WEST: + return (old_tile_data & Tile::WORLDMAP_WEST + && new_tile_data & Tile::WORLDMAP_EAST); + + case D_EAST: + return (old_tile_data & Tile::WORLDMAP_EAST + && new_tile_data & Tile::WORLDMAP_WEST); + + case D_NORTH: + return (old_tile_data & Tile::WORLDMAP_NORTH + && new_tile_data & Tile::WORLDMAP_SOUTH); + + case D_SOUTH: + return (old_tile_data & Tile::WORLDMAP_SOUTH + && new_tile_data & Tile::WORLDMAP_NORTH); + + case D_NONE: + assert(!"path_ok() can't walk if direction is NONE"); } + return false; + } } void @@ -459,6 +481,10 @@ WorldMap::finished_level(Level* gamelevel) // TODO use Level* parameter here? LevelTile* level = at_level(); + if(level == NULL) { + return; + } + bool old_level_state = level->solved; level->solved = true; level->sprite->set_action("solved"); @@ -466,6 +492,12 @@ WorldMap::finished_level(Level* gamelevel) // deal with statistics level->statistics.merge(gamelevel->stats); calculate_total_stats(); + get_level_target_time(*level); + if(level->statistics.completed(level->statistics, level->target_time)) { + level->perfect = true; + if(level->sprite->has_action("perfect")) + level->sprite->set_action("perfect"); + } save_state(); @@ -477,22 +509,22 @@ WorldMap::finished_level(Level* gamelevel) int dirdata = available_directions_at(tux->get_tile_pos()); // first, test for crossroads if (dirdata == Tile::WORLDMAP_CNSE || - dirdata == Tile::WORLDMAP_CNSW || - dirdata == Tile::WORLDMAP_CNEW || - dirdata == Tile::WORLDMAP_CSEW || - dirdata == Tile::WORLDMAP_CNSEW) + dirdata == Tile::WORLDMAP_CNSW || + dirdata == Tile::WORLDMAP_CNEW || + dirdata == Tile::WORLDMAP_CSEW || + dirdata == Tile::WORLDMAP_CNSEW) dir = D_NONE; else if (dirdata & Tile::WORLDMAP_NORTH - && tux->back_direction != D_NORTH) + && tux->back_direction != D_NORTH) dir = D_NORTH; else if (dirdata & Tile::WORLDMAP_SOUTH - && tux->back_direction != D_SOUTH) + && tux->back_direction != D_SOUTH) dir = D_SOUTH; else if (dirdata & Tile::WORLDMAP_EAST - && tux->back_direction != D_EAST) + && tux->back_direction != D_EAST) dir = D_EAST; else if (dirdata & Tile::WORLDMAP_WEST - && tux->back_direction != D_WEST) + && tux->back_direction != D_WEST) dir = D_WEST; if (dir != D_NONE) { @@ -505,47 +537,57 @@ WorldMap::finished_level(Level* gamelevel) std::istringstream in(level->extro_script); run_script(in, "worldmap:extro_script"); } catch(std::exception& e) { - log_fatal << "Couldn't run level-extro-script: " << e.what() << std::endl; + log_warning << "Couldn't run level-extro-script: " << e.what() << std::endl; } } } +Vector +WorldMap::get_camera_pos_for_tux() { + Vector camera_offset_; + Vector tux_pos = tux->get_pos(); + camera_offset_.x = tux_pos.x - SCREEN_WIDTH/2; + camera_offset_.y = tux_pos.y - SCREEN_HEIGHT/2; + return camera_offset_; +} + +void +WorldMap::clamp_camera_position(Vector& c) { + if (c.x < 0) + c.x = 0; + if (c.y < 0) + c.y = 0; + + if (c.x > (int)get_width()*32 - SCREEN_WIDTH) + c.x = (int)get_width()*32 - SCREEN_WIDTH; + if (c.y > (int)get_height()*32 - SCREEN_HEIGHT) + c.y = (int)get_height()*32 - SCREEN_HEIGHT; + + if (int(get_width()*32) < SCREEN_WIDTH) + c.x = get_width()*16.0 - SCREEN_WIDTH/2.0; + if (int(get_height()*32) < SCREEN_HEIGHT) + c.y = get_height()*16.0 - SCREEN_HEIGHT/2.0; +} + void WorldMap::update(float delta) { - if(!in_level) { - Menu* menu = Menu::current(); - if(menu != NULL) { - menu->update(); - - if(menu == worldmap_menu.get()) { - switch (worldmap_menu->check()) - { - case MNID_RETURNWORLDMAP: // Return to game - Menu::set_current(0); - break; - case MNID_QUITWORLDMAP: // Quit Worldmap - main_loop->exit_screen(); - break; - } - } - - return; - } - + if (!in_level && !MenuManager::instance().is_active()) + { // update GameObjects for(size_t i = 0; i < game_objects.size(); ++i) { - GameObject* object = game_objects[i]; - object->update(delta); + GameObjectPtr& object = game_objects[i]; + if(!panning || object != tux) { + object->update(delta); + } } // remove old GameObjects for(GameObjects::iterator i = game_objects.begin(); i != game_objects.end(); ) { - GameObject* object = *i; + GameObjectPtr& object = *i; if(!object->is_valid()) { try_unexpose(object); - object->unref(); i = game_objects.erase(i); } else { ++i; @@ -555,45 +597,63 @@ WorldMap::update(float delta) /* update solid_tilemaps list */ //FIXME: this could be more efficient solid_tilemaps.clear(); - for(std::vector::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) + for(auto i = game_objects.begin(); i != game_objects.end(); ++i) { - TileMap* tm = dynamic_cast(*i); + TileMap* tm = dynamic_cast(i->get()); if (!tm) continue; if (tm->is_solid()) solid_tilemaps.push_back(tm); } - // position "camera" - Vector tux_pos = tux->get_pos(); - camera_offset.x = tux_pos.x - SCREEN_WIDTH/2; - camera_offset.y = tux_pos.y - SCREEN_HEIGHT/2; + Vector requested_pos; - if (camera_offset.x < 0) - camera_offset.x = 0; - if (camera_offset.y < 0) - camera_offset.y = 0; + // position "camera" + if(!panning) { + camera_offset = get_camera_pos_for_tux(); + } else { + Vector delta__ = pan_pos - camera_offset; + float mag = delta__.norm(); + if(mag > CAMERA_PAN_SPEED) { + delta__ *= CAMERA_PAN_SPEED/mag; + } + camera_offset += delta__; + if(camera_offset == pan_pos) { + panning = false; + } + } - if (camera_offset.x > (int)get_width()*32 - SCREEN_WIDTH) - camera_offset.x = (int)get_width()*32 - SCREEN_WIDTH; - if (camera_offset.y > (int)get_height()*32 - SCREEN_HEIGHT) - camera_offset.y = (int)get_height()*32 - SCREEN_HEIGHT; + requested_pos = camera_offset; + clamp_camera_position(camera_offset); - if (int(get_width()*32) < SCREEN_WIDTH) - camera_offset.x = get_width()*16.0 - SCREEN_WIDTH/2.0; - if (int(get_height()*32) < SCREEN_HEIGHT) - camera_offset.y = get_height()*16.0 - SCREEN_HEIGHT/2.0; + if(panning) { + if(requested_pos.x != camera_offset.x) { + pan_pos.x = camera_offset.x; + } + if(requested_pos.y != camera_offset.y) { + pan_pos.y = camera_offset.y; + } + } // handle input + Controller *controller = InputManager::current()->get_controller(); bool enter_level = false; - if(main_controller->pressed(Controller::ACTION) - || main_controller->pressed(Controller::JUMP) - || main_controller->pressed(Controller::MENU_SELECT)) { + if(controller->pressed(Controller::ACTION) + || controller->pressed(Controller::JUMP) + || controller->pressed(Controller::MENU_SELECT)) { /* some people define UP and JUMP on the same key... */ - if(!main_controller->pressed(Controller::UP)) - enter_level = true; - } - if(main_controller->pressed(Controller::PAUSE_MENU)) + if(!controller->pressed(Controller::UP)) + enter_level = true; + } + if(controller->pressed(Controller::START) || + controller->pressed(Controller::ESCAPE)) + { on_escape_press(); + } + + if(controller->pressed(Controller::CHEAT_MENU) && + g_config->developer_mode) + { + MenuManager::instance().set_menu(MenuStorage::WORLDMAP_CHEAT_MENU); + } // check for teleporters Teleporter* teleporter = at_teleporter(tux->get_tile_pos()); @@ -603,9 +663,9 @@ WorldMap::update(float delta) change(teleporter->worldmap, teleporter->spawnpoint); } else { // TODO: an animation, camera scrolling or a fading would be a nice touch - sound_manager->play("sounds/warp.wav"); + SoundManager::current()->play("sounds/warp.wav"); tux->back_direction = D_NONE; - move_to_spawnpoint(teleporter->spawnpoint); + move_to_spawnpoint(teleporter->spawnpoint, true); } } @@ -613,45 +673,47 @@ WorldMap::update(float delta) LevelTile* level = at_level(); if (level && (level->auto_play) && (!level->solved) && (!tux->is_moving())) { enter_level = true; + // automatically mark these levels as solved in case player aborts + level->solved = true; } if (enter_level && !tux->is_moving()) - { - /* Check level action */ - LevelTile* level = at_level(); - if (!level) { - //Respawn if player on a tile with no level and nowhere to go. - int tile_data = tile_data_at(tux->get_tile_pos()); - if(!( tile_data & ( Tile::WORLDMAP_NORTH | Tile::WORLDMAP_SOUTH | Tile::WORLDMAP_WEST | Tile::WORLDMAP_EAST ))){ - log_warning << "Player at illegal position " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << " respawning." << std::endl; - move_to_spawnpoint("main"); - return; - } - log_warning << "No level to enter at: " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl; + { + /* Check level action */ + LevelTile* level_ = at_level(); + if (!level_) { + //Respawn if player on a tile with no level and nowhere to go. + int tile_data = tile_data_at(tux->get_tile_pos()); + if(!( tile_data & ( Tile::WORLDMAP_NORTH | Tile::WORLDMAP_SOUTH | Tile::WORLDMAP_WEST | Tile::WORLDMAP_EAST ))){ + log_warning << "Player at illegal position " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << " respawning." << std::endl; + move_to_spawnpoint("main"); return; } + log_warning << "No level to enter at: " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl; + return; + } - if (level->pos == tux->get_tile_pos()) { - try { - Vector shrinkpos = Vector(level->pos.x*32 + 16 - camera_offset.x, - level->pos.y*32 + 16 - camera_offset.y); - std::string levelfile = levels_path + level->get_name(); + if (level_->pos == tux->get_tile_pos()) { + try { + Vector shrinkpos = Vector(level_->pos.x*32 + 16 - camera_offset.x, + level_->pos.y*32 + 8 - camera_offset.y); + std::string levelfile = levels_path + level_->get_name(); - // update state and savegame - save_state(); + // update state and savegame + save_state(); - main_loop->push_screen(new GameSession(levelfile, &level->statistics), - new ShrinkFade(shrinkpos, 0.5)); - in_level = true; - } catch(std::exception& e) { - log_fatal << "Couldn't load level: " << e.what() << std::endl; - } + ScreenManager::current()->push_screen(std::unique_ptr(new GameSession(levelfile, m_savegame, &level_->statistics)), + std::unique_ptr(new ShrinkFade(shrinkpos, 1.0f))); + in_level = true; + } catch(std::exception& e) { + log_fatal << "Couldn't load level: " << e.what() << std::endl; } } + } else - { - // tux->set_direction(input_direction); - } + { + // tux->set_direction(input_direction); + } } } @@ -660,7 +722,7 @@ WorldMap::tile_data_at(Vector p) { int dirs = 0; - for(std::list::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) { + for(std::list::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); ++i) { TileMap* tilemap = *i; const Tile* tile = tilemap->get_tile((int)p.x, (int)p.y); int dirdata = tile->getData(); @@ -730,38 +792,40 @@ WorldMap::draw(DrawingContext& context) { if (int(get_width()*32) < SCREEN_WIDTH || int(get_height()*32) < SCREEN_HEIGHT) context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT), - Color(0.0f, 0.0f, 0.0f, 1.0f), LAYER_BACKGROUND0); + Color(0.0f, 0.0f, 0.0f, 1.0f), LAYER_BACKGROUND0); context.set_ambient_color( ambient_light ); context.push_transform(); context.set_translation(camera_offset); - for(GameObjects::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) { - GameObject* object = *i; - object->draw(context); + for(auto i = game_objects.begin(); i != game_objects.end(); ++i) + { + GameObjectPtr& object = *i; + if(!panning || object != tux) { + object->draw(context); + } } -/* + /* // FIXME: make this a runtime switch similar to draw_collrects/show_collrects? // draw visual indication of possible walk directions - static int flipme = 0; + static int flipme = 0; if (flipme++ & 0x04) for (int x = 0; x < get_width(); x++) { - for (int y = 0; y < get_height(); y++) { - int data = tile_data_at(Vector(x,y)); - int px = x * 32; - int py = y * 32; - const int W = 4; - if (data & Tile::WORLDMAP_NORTH) context.draw_filled_rect(Rect(px + 16-W, py , px + 16+W, py + 16-W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); - if (data & Tile::WORLDMAP_SOUTH) context.draw_filled_rect(Rect(px + 16-W, py + 16+W, px + 16+W, py + 32 ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); - if (data & Tile::WORLDMAP_EAST) context.draw_filled_rect(Rect(px + 16+W, py + 16-W, px + 32 , py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); - if (data & Tile::WORLDMAP_WEST) context.draw_filled_rect(Rect(px , py + 16-W, px + 16-W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); - if (data & Tile::WORLDMAP_DIR_MASK) context.draw_filled_rect(Rect(px + 16-W, py + 16-W, px + 16+W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); - if (data & Tile::WORLDMAP_STOP) context.draw_filled_rect(Rect(px + 4 , py + 4 , px + 28 , py + 28 ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); - } + for (int y = 0; y < get_height(); y++) { + int data = tile_data_at(Vector(x,y)); + int px = x * 32; + int py = y * 32; + const int W = 4; + if (data & Tile::WORLDMAP_NORTH) context.draw_filled_rect(Rect(px + 16-W, py , px + 16+W, py + 16-W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); + if (data & Tile::WORLDMAP_SOUTH) context.draw_filled_rect(Rect(px + 16-W, py + 16+W, px + 16+W, py + 32 ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); + if (data & Tile::WORLDMAP_EAST) context.draw_filled_rect(Rect(px + 16+W, py + 16-W, px + 32 , py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); + if (data & Tile::WORLDMAP_WEST) context.draw_filled_rect(Rect(px , py + 16-W, px + 16-W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); + if (data & Tile::WORLDMAP_DIR_MASK) context.draw_filled_rect(Rect(px + 16-W, py + 16-W, px + 16+W, py + 16+W), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); + if (data & Tile::WORLDMAP_STOP) context.draw_filled_rect(Rect(px + 4 , py + 4 , px + 28 , py + 28 ), Color(0.2f, 0.2f, 0.2f, 0.7f), LAYER_FOREGROUND1 + 1000); + } } -*/ + */ draw_status(context); context.pop_transform(); @@ -773,7 +837,7 @@ WorldMap::draw_status(DrawingContext& context) context.push_transform(); context.set_translation(Vector(0, 0)); - player_status->draw(context); + m_savegame.get_player_status()->draw(context); if (!tux->is_moving()) { for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { @@ -783,25 +847,27 @@ WorldMap::draw_status(DrawingContext& context) if(level->title == "") get_level_title(*level); - context.draw_text(white_text, level->title, + context.draw_text(Resources::normal_font, level->title, Vector(SCREEN_WIDTH/2, - SCREEN_HEIGHT - white_text->get_height() - 30), - ALIGN_CENTER, LAYER_FOREGROUND1); + SCREEN_HEIGHT - Resources::normal_font->get_height() - 10), + ALIGN_CENTER, LAYER_HUD, WorldMap::level_title_color); // if level is solved, draw level picture behind stats /* - if (level->solved) { + if (level->solved) { if (const Surface* picture = level->get_picture()) { - Vector pos = Vector(SCREEN_WIDTH - picture->get_width(), SCREEN_HEIGHT - picture->get_height()); - context.push_transform(); - context.set_alpha(0.5); - context.draw_surface(picture, pos, LAYER_FOREGROUND1-1); - context.pop_transform(); + Vector pos = Vector(SCREEN_WIDTH - picture->get_width(), SCREEN_HEIGHT - picture->get_height()); + context.push_transform(); + context.set_alpha(0.5); + context.draw_surface(picture, pos, LAYER_FOREGROUND1-1); + context.pop_transform(); + } } - } */ - level->statistics.draw_worldmap_info(context); + if (level->target_time == 0.0f) + get_level_target_time(*level); + level->statistics.draw_worldmap_info(context, level->target_time); break; } } @@ -813,10 +879,10 @@ WorldMap::draw_status(DrawingContext& context) if (special_tile->pos == tux->get_tile_pos()) { /* Display an in-map message in the map, if any as been selected */ if(!special_tile->map_message.empty() && !special_tile->passive_message) - context.draw_text(gold_text, special_tile->map_message, - Vector(SCREEN_WIDTH/2, - SCREEN_HEIGHT - white_text->get_height() - 60), - ALIGN_CENTER, LAYER_FOREGROUND1); + context.draw_text(Resources::normal_font, special_tile->map_message, + Vector(SCREEN_WIDTH/2, + SCREEN_HEIGHT - Resources::normal_font->get_height() - 60), + ALIGN_CENTER, LAYER_FOREGROUND1, WorldMap::message_color); break; } } @@ -824,17 +890,17 @@ WorldMap::draw_status(DrawingContext& context) // display teleporter messages Teleporter* teleporter = at_teleporter(tux->get_tile_pos()); if (teleporter && (teleporter->message != "")) { - Vector pos = Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 30); - context.draw_text(white_text, teleporter->message, pos, ALIGN_CENTER, LAYER_FOREGROUND1); + Vector pos = Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - Resources::normal_font->get_height() - 30); + context.draw_text(Resources::normal_font, teleporter->message, pos, ALIGN_CENTER, LAYER_FOREGROUND1, WorldMap::teleporter_message_color); } } /* Display a passive message in the map, if needed */ if(passive_message_timer.started()) - context.draw_text(gold_text, passive_message, - Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60), - ALIGN_CENTER, LAYER_FOREGROUND1); + context.draw_text(Resources::normal_font, passive_message, + Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - Resources::normal_font->get_height() - 60), + ALIGN_CENTER, LAYER_FOREGROUND1, WorldMap::message_color); context.pop_transform(); } @@ -842,8 +908,8 @@ WorldMap::draw_status(DrawingContext& context) void WorldMap::setup() { - sound_manager->play_music(music); - Menu::set_current(NULL); + SoundManager::current()->play_music(music); + MenuManager::instance().clear_menu_stack(); current_ = this; load_state(); @@ -857,15 +923,24 @@ WorldMap::setup() tux->setup(); // register worldmap_table as worldmap in scripting - using namespace Scripting; + using namespace scripting; sq_pushroottable(global_vm); sq_pushstring(global_vm, "worldmap", -1); sq_pushobject(global_vm, worldmap_table); - if(SQ_FAILED(sq_createslot(global_vm, -3))) + if(SQ_FAILED(sq_newslot(global_vm, -3, SQFalse))) throw SquirrelError(global_vm, "Couldn't set worldmap in roottable"); sq_pop(global_vm, 1); + //Run default.nut just before init script + try { + IFileStreambuf ins(levels_path + "default.nut"); + std::istream in(&ins); + run_script(in, "WorldMap::default.nut"); + } catch(std::exception& ) { + // doesn't exist or erroneous; do nothing + } + if(init_script != "") { std::istringstream in(init_script); run_script(in, "WorldMap::init"); @@ -875,7 +950,7 @@ WorldMap::setup() void WorldMap::leave() { - using namespace Scripting; + using namespace scripting; // save state of world and player save_state(); @@ -889,9 +964,19 @@ WorldMap::leave() } void +WorldMap::set_levels_solved(bool solved, bool perfect) +{ + for(auto& level : levels) + { + level->set_solved(solved); + level->set_perfect(perfect); + } +} + +void WorldMap::save_state() { - using namespace Scripting; + using namespace scripting; HSQUIRRELVM vm = global_vm; int oldtop = sq_gettop(vm); @@ -901,24 +986,25 @@ WorldMap::save_state() sq_pushroottable(vm); sq_pushstring(vm, "state", -1); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't get state table"); + throw scripting::SquirrelError(vm, "Couldn't get state table"); // get or create worlds table sq_pushstring(vm, "worlds", -1); if(SQ_FAILED(sq_get(vm, -2))) { sq_pushstring(vm, "worlds", -1); sq_newtable(vm); - if(SQ_FAILED(sq_createslot(vm, -3))) - throw Scripting::SquirrelError(vm, "Couldn't create state.worlds"); + if(SQ_FAILED(sq_newslot(vm, -3, SQFalse))) + throw scripting::SquirrelError(vm, "Couldn't create state.worlds"); sq_pushstring(vm, "worlds", -1); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't create.get state.worlds"); + throw scripting::SquirrelError(vm, "Couldn't create.get state.worlds"); } sq_pushstring(vm, map_filename.c_str(), map_filename.length()); if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse))) - sq_pop(vm, 1); + { + } // construct new table for this worldmap sq_pushstring(vm, map_filename.c_str(), map_filename.length()); @@ -932,7 +1018,7 @@ WorldMap::save_state() store_float(vm, "y", tux->get_tile_pos().y); store_string(vm, "back", direction_to_string(tux->back_direction)); - sq_createslot(vm, -3); + sq_newslot(vm, -3, SQFalse); // levels... sq_pushstring(vm, "levels", -1); @@ -941,36 +1027,45 @@ WorldMap::save_state() for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { LevelTile* level = *i; - sq_pushstring(vm, level->get_name().c_str(), -1); - sq_newtable(vm); + sq_pushstring(vm, level->get_name().c_str(), -1); + sq_newtable(vm); - store_bool(vm, "solved", level->solved); - level->statistics.serialize_to_squirrel(vm); + store_bool(vm, "solved", level->solved); + store_bool(vm, "perfect", level->perfect); + level->statistics.serialize_to_squirrel(vm); - sq_createslot(vm, -3); + if(SQ_FAILED(sq_newslot(vm, -3, SQFalse))) + { + throw std::runtime_error("failed to create '" + name + "' table entry"); + } } - sq_createslot(vm, -3); + if(SQ_FAILED(sq_newslot(vm, -3, SQFalse))) + { + throw std::runtime_error("failed to create '" + name + "' table entry"); + } // overall statistics... total_stats.serialize_to_squirrel(vm); // push world into worlds table - sq_createslot(vm, -3); + if(SQ_FAILED(sq_newslot(vm, -3, SQFalse))) + { + throw std::runtime_error("failed to create '" + name + "' table entry"); + } } catch(std::exception& ) { sq_settop(vm, oldtop); } sq_settop(vm, oldtop); - if(World::current() != NULL) - World::current()->save_state(); + m_savegame.save(); } void WorldMap::load_state() { - using namespace Scripting; + using namespace scripting; HSQUIRRELVM vm = global_vm; int oldtop = sq_gettop(vm); @@ -980,22 +1075,22 @@ WorldMap::load_state() sq_pushroottable(vm); sq_pushstring(vm, "state", -1); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't get state table"); + throw scripting::SquirrelError(vm, "Couldn't get state table"); // get worlds table sq_pushstring(vm, "worlds", -1); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't get state.worlds"); + throw scripting::SquirrelError(vm, "Couldn't get state.worlds"); // get table for our world sq_pushstring(vm, map_filename.c_str(), map_filename.length()); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't get state.worlds.mapfilename"); + throw scripting::SquirrelError(vm, "Couldn't get state.worlds.mapfilename"); // load tux sq_pushstring(vm, "tux", -1); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't get tux"); + throw scripting::SquirrelError(vm, "Couldn't get tux"); Vector p; p.x = read_float(vm, "x"); @@ -1009,14 +1104,18 @@ WorldMap::load_state() // load levels sq_pushstring(vm, "levels", -1); if(SQ_FAILED(sq_get(vm, -2))) - throw Scripting::SquirrelError(vm, "Couldn't get levels"); + throw scripting::SquirrelError(vm, "Couldn't get levels"); for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { LevelTile* level = *i; sq_pushstring(vm, level->get_name().c_str(), -1); if(SQ_SUCCEEDED(sq_get(vm, -2))) { level->solved = read_bool(vm, "solved"); - level->sprite->set_action(level->solved ? "solved" : "default"); + level->perfect = read_bool(vm, "perfect"); + if(!level->solved) + level->sprite->set_action("default"); + else + level->sprite->set_action((level->sprite->has_action("perfect") && level->perfect) ? "perfect" : "solved"); level->statistics.unserialize_from_squirrel(vm); sq_pop(vm, 1); } @@ -1059,7 +1158,7 @@ WorldMap::solved_level_count() HSQUIRRELVM WorldMap::run_script(std::istream& in, const std::string& sourcename) { - using namespace Scripting; + using namespace scripting; // garbage collect thread list for(ScriptList::iterator i = scripts.begin(); @@ -1094,7 +1193,7 @@ float WorldMap::get_width() const { float width = 0; - for(std::list::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) { + for(std::list::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); ++i) { TileMap* solids = *i; if (solids->get_width() > width) width = solids->get_width(); } @@ -1105,11 +1204,13 @@ float WorldMap::get_height() const { float height = 0; - for(std::list::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) { + for(std::list::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); ++i) { TileMap* solids = *i; if (solids->get_height() > height) height = solids->get_height(); } return height; } -} // namespace WorldMapNS +} // namespace worldmap + +/* EOF */