X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fworldmap%2Fworldmap.cpp;h=b34e881edd2b453623a32fc3b2e15751ae12a63d;hb=d84d73b701cc7fa2bd74f3490b9be1bf8b6f705a;hp=84423f391c8285a4228cdd06c5aafab7790fb84a;hpb=112f01454123c94f5627200c6819b219026f0af0;p=supertux.git diff --git a/src/worldmap/worldmap.cpp b/src/worldmap/worldmap.cpp index 84423f391..b34e881ed 100644 --- a/src/worldmap/worldmap.cpp +++ b/src/worldmap/worldmap.cpp @@ -33,8 +33,8 @@ #include "gettext.hpp" #include "log.hpp" #include "mainloop.hpp" +#include "shrinkfade.hpp" #include "video/surface.hpp" -#include "video/screen.hpp" #include "video/drawing_context.hpp" #include "sprite/sprite_manager.hpp" #include "audio/sound_manager.hpp" @@ -46,7 +46,6 @@ #include "sector.hpp" #include "worldmap.hpp" #include "resources.hpp" -#include "misc.hpp" #include "log.hpp" #include "world.hpp" #include "player_status.hpp" @@ -59,10 +58,9 @@ #include "control/joystickkeyboardcontroller.hpp" #include "object/background.hpp" #include "object/tilemap.hpp" -#include "script_manager.hpp" #include "options_menu.hpp" #include "scripting/squirrel_error.hpp" -#include "scripting/wrapper_util.hpp" +#include "scripting/squirrel_util.hpp" #include "worldmap/level.hpp" #include "worldmap/special_tile.hpp" #include "worldmap/tux.hpp" @@ -130,8 +128,8 @@ string_to_direction(const std::string& directory) //--------------------------------------------------------------------------- -WorldMap::WorldMap() - : tux(0), solids(0) +WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpoint) + : tux(0), solids(0), force_spawnpoint(force_spawnpoint) { tile_manager.reset(new TileManager("images/worldmap.strf")); @@ -140,7 +138,6 @@ WorldMap::WorldMap() name = ""; music = "music/salcon.ogg"; - intro_displayed = false; total_stats.reset(); @@ -151,14 +148,48 @@ WorldMap::WorldMap() worldmap_menu->add_submenu(_("Options"), get_options_menu()); worldmap_menu->add_hl(); worldmap_menu->add_entry(MNID_QUITWORLDMAP, _("Quit World")); + + load(filename); + + // create a new squirrel table for the worldmap + 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"); + + 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"); + + sq_addref(global_vm, &worldmap_table); + sq_pop(global_vm, 1); } WorldMap::~WorldMap() { + using namespace Scripting; + + for(ScriptList::iterator i = scripts.begin(); + i != scripts.end(); ++i) { + HSQOBJECT& object = *i; + sq_release(global_vm, &object); + } + sq_release(global_vm, &worldmap_table); + + sq_collectgarbage(global_vm); + if(current_ == this) current_ = NULL; - clear_objects(); + for(GameObjects::iterator i = game_objects.begin(); + i != game_objects.end(); ++i) { + GameObject* object = *i; + object->unref(); + } + for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) { delete *i; @@ -173,25 +204,38 @@ WorldMap::add_object(GameObject* object) solids = tilemap; } + object->ref(); game_objects.push_back(object); } void -WorldMap::clear_objects() +WorldMap::move_to_spawnpoint(const std::string& spawnpoint) { - for(GameObjects::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) - delete *i; - game_objects.clear(); - solids = 0; - tux = new Tux(this); - add_object(tux); + for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) { + SpawnPoint* sp = *i; + if(sp->name == spawnpoint) { + Vector p = sp->pos; + tux->set_tile_pos(p); + return; + } + } + log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl; + if (spawnpoint != "main") { + move_to_spawnpoint("main"); + } +} + +void +WorldMap::change(const std::string& filename, const std::string& force_spawnpoint) +{ + main_loop->exit_screen(); + main_loop->push_screen(new WorldMap(filename, force_spawnpoint)); } -// Don't forget to set map_filename before calling this void -WorldMap::load_map() +WorldMap::load(const std::string& filename) { + map_filename = filename; levels_path = FileSystem::dirname(map_filename); try { @@ -208,7 +252,6 @@ WorldMap::load_map() if(!sector) throw std::runtime_error("No sector sepcified in worldmap file."); - clear_objects(); lisp::ListIterator iter(sector); while(iter.next()) { if(iter.item() == "tilemap") { @@ -217,23 +260,27 @@ WorldMap::load_map() add_object(new Background(*(iter.lisp()))); } else if(iter.item() == "music") { iter.value()->get(music); - } else if(iter.item() == "intro-script") { - iter.value()->get(intro_script); + } else if(iter.item() == "init-script") { + iter.value()->get(init_script); } else if(iter.item() == "worldmap-spawnpoint") { SpawnPoint* sp = new SpawnPoint(iter.lisp()); spawn_points.push_back(sp); } else if(iter.item() == "level") { - Level* level = new Level(levels_path, iter.lisp()); + LevelTile* level = new LevelTile(levels_path, iter.lisp()); levels.push_back(level); - game_objects.push_back(level); + add_object(level); } else if(iter.item() == "special-tile") { SpecialTile* special_tile = new SpecialTile(iter.lisp()); special_tiles.push_back(special_tile); - game_objects.push_back(special_tile); - } else if(iter.item() == "spritechange") { + add_object(special_tile); + } else if(iter.item() == "sprite-change") { SpriteChange* sprite_change = new SpriteChange(iter.lisp()); sprite_changes.push_back(sprite_change); - game_objects.push_back(sprite_change); + add_object(sprite_change); + } else if(iter.item() == "teleporter") { + Teleporter* teleporter = new Teleporter(iter.lisp()); + teleporters.push_back(teleporter); + add_object(teleporter); } else if(iter.item() == "name") { // skip } else { @@ -243,16 +290,7 @@ WorldMap::load_map() if(solids == 0) throw std::runtime_error("No solid tilemap specified"); - // search for main spawnpoint - for(SpawnPoints::iterator i = spawn_points.begin(); - i != spawn_points.end(); ++i) { - SpawnPoint* sp = *i; - if(sp->name == "main") { - Vector p = sp->pos; - tux->set_tile_pos(p); - break; - } - } + move_to_spawnpoint("main"); } catch(std::exception& e) { std::stringstream msg; @@ -263,7 +301,7 @@ WorldMap::load_map() } void -WorldMap::get_level_title(Level& level) +WorldMap::get_level_title(LevelTile& level) { /** get special_tile's title */ level.title = ""; @@ -286,8 +324,8 @@ WorldMap::get_level_title(Level& level) void WorldMap::calculate_total_stats() { total_stats.reset(); - for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) { - Level* level = *i; + for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { + LevelTile* level = *i; if (level->solved) { total_stats += level->statistics; } @@ -329,7 +367,7 @@ WorldMap::get_next_tile(Vector pos, Direction direction) } bool -WorldMap::path_ok(Direction direction, Vector old_pos, Vector* new_pos) +WorldMap::path_ok(Direction direction, const Vector& old_pos, Vector* new_pos) { *new_pos = get_next_tile(old_pos, direction); @@ -359,25 +397,24 @@ WorldMap::path_ok(Direction direction, Vector old_pos, Vector* new_pos) && at(*new_pos)->getData() & Tile::WORLDMAP_NORTH); case D_NONE: - assert(!"path_ok() can't work if direction is NONE"); + assert(!"path_ok() can't walk if direction is NONE"); } return false; } } void -WorldMap::finished_level(const std::string& filename) +WorldMap::finished_level(Level* gamelevel) { - // TODO calculate level from filename? - (void) filename; - Level* level = at_level(); + // TODO use Level* parameter here? + LevelTile* level = at_level(); bool old_level_state = level->solved; level->solved = true; level->sprite->set_action("solved"); // deal with statistics - level->statistics.merge(global_stats); + level->statistics.merge(gamelevel->stats); calculate_total_stats(); save_state(); @@ -416,10 +453,8 @@ WorldMap::finished_level(const std::string& filename) if (level->extro_script != "") { try { - HSQUIRRELVM vm = ScriptManager::instance->create_thread(); - std::istringstream in(level->extro_script); - Scripting::compile_and_run(vm, in, "worldmap,extro_script"); + run_script(in, "worldmap:extro_script"); } catch(std::exception& e) { log_fatal << "Couldn't run level-extro-script: " << e.what() << std::endl; } @@ -449,9 +484,8 @@ WorldMap::update(float delta) } // update GameObjects - for(GameObjects::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) { - GameObject* object = *i; + for(size_t i = 0; i < game_objects.size(); ++i) { + GameObject* object = game_objects[i]; object->update(delta); } @@ -460,7 +494,7 @@ WorldMap::update(float delta) i != game_objects.end(); ) { GameObject* object = *i; if(!object->is_valid()) { - delete object; + object->unref(); i = game_objects.erase(i); } else { ++i; @@ -490,40 +524,37 @@ WorldMap::update(float delta) enter_level = true; if(main_controller->pressed(Controller::PAUSE_MENU)) on_escape_press(); - + + // check for teleporters + Teleporter* teleporter = at_teleporter(tux->get_tile_pos()); + if (teleporter && (teleporter->automatic || (enter_level && (!tux->is_moving())))) { + enter_level = false; + if (teleporter->worldmap != "") { + 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"); + tux->back_direction = D_NONE; + move_to_spawnpoint(teleporter->spawnpoint); + } + } + if (enter_level && !tux->is_moving()) { - /* Check special tile action */ - SpecialTile* special_tile = at_special_tile(); - if(special_tile) - { - if (special_tile->teleport_dest != Vector(-1,-1)) - { - // TODO: an animation, camera scrolling or a fading would be a nice touch - sound_manager->play("sounds/warp.wav"); - tux->back_direction = D_NONE; - tux->set_tile_pos(special_tile->teleport_dest); - SDL_Delay(1000); - } - } - /* Check level action */ - Level* level = at_level(); + LevelTile* level = at_level(); if (!level) { 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()) { - // do a shriking fade to the level - shrink_fade(Vector((level->pos.x*32 + 16 + offset.x), - (level->pos.y*32 + 16 + offset.y)), 500); - try { - GameSession *session = - new GameSession(levels_path + level->name, - ST_GL_LOAD_LEVEL_FILE, &level->statistics); - main_loop->push_screen(session); + 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->name; + main_loop->push_screen(new GameSession(levelfile, &level->statistics), + new ShrinkFade(shrinkpos, 0.5)); } catch(std::exception& e) { log_fatal << "Couldn't load level: " << e.what() << std::endl; } @@ -541,11 +572,11 @@ WorldMap::at(Vector p) return solids->get_tile((int) p.x, (int) p.y); } -Level* +LevelTile* WorldMap::at_level() { - for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) { - Level* level = *i; + for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { + LevelTile* level = *i; if (level->pos == tux->get_tile_pos()) return level; } @@ -567,8 +598,26 @@ WorldMap::at_special_tile() } SpriteChange* -WorldMap::at_sprite_change() +WorldMap::at_sprite_change(const Vector& pos) { + for(SpriteChanges::iterator i = sprite_changes.begin(); + i != sprite_changes.end(); ++i) { + SpriteChange* sprite_change = *i; + if(sprite_change->pos == pos) + return sprite_change; + } + + return NULL; +} + +Teleporter* +WorldMap::at_teleporter(const Vector& pos) +{ + for(std::vector::iterator i = teleporters.begin(); i != teleporters.end(); ++i) { + Teleporter* teleporter = *i; + if(teleporter->pos == pos) return teleporter; + } + return NULL; } @@ -597,8 +646,8 @@ WorldMap::draw_status(DrawingContext& context) player_status->draw(context); if (!tux->is_moving()) { - for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) { - Level* level = *i; + for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { + LevelTile* level = *i; if (level->pos == tux->get_tile_pos()) { if(level->title == "") @@ -609,6 +658,19 @@ WorldMap::draw_status(DrawingContext& context) SCREEN_HEIGHT - white_text->get_height() - 30), CENTER_ALLIGN, LAYER_FOREGROUND1); + // if level is solved, draw level picture behind stats + /* + 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(); + } + } + */ + level->statistics.draw_worldmap_info(context); break; } @@ -628,6 +690,14 @@ WorldMap::draw_status(DrawingContext& context) break; } } + + // 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, CENTER_ALLIGN, LAYER_FOREGROUND1); + } + } /* Display a passive message in the map, if needed */ @@ -647,6 +717,42 @@ WorldMap::setup() current_ = this; load_state(); + + // if force_spawnpoint was set, move Tux there, then clear force_spawnpoint + if (force_spawnpoint != "") { + move_to_spawnpoint(force_spawnpoint); + force_spawnpoint = ""; + } + + tux->setup(); + + // register worldmap_table as worldmap in 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))) + throw SquirrelError(global_vm, "Couldn't set worldmap in roottable"); + sq_pop(global_vm, 1); + + if(init_script != "") { + std::istringstream in(init_script); + run_script(in, "WorldMap::init"); + } +} + +void +WorldMap::leave() +{ + // remove worldmap_table from roottable + using namespace Scripting; + + sq_pushroottable(global_vm); + sq_pushstring(global_vm, "worldmap", -1); + if(SQ_FAILED(sq_deleteslot(global_vm, -2, SQFalse))) + throw SquirrelError(global_vm, "Couldn't unset worldmap in roottable"); + sq_pop(global_vm, 1); } static void store_float(HSQUIRRELVM vm, const char* name, float val) @@ -746,7 +852,9 @@ static bool read_bool(HSQUIRRELVM vm, const char* name) void WorldMap::save_state() { - HSQUIRRELVM vm = ScriptManager::instance->get_vm(); + using namespace Scripting; + + HSQUIRRELVM vm = global_vm; int oldtop = sq_gettop(vm); try { @@ -791,8 +899,8 @@ WorldMap::save_state() sq_pushstring(vm, "levels", -1); sq_newtable(vm); - for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) { - Level* level = *i; + for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { + LevelTile* level = *i; if (level->solved) { sq_pushstring(vm, level->name.c_str(), -1); @@ -820,7 +928,9 @@ WorldMap::save_state() void WorldMap::load_state() { - HSQUIRRELVM vm = ScriptManager::instance->get_vm(); + using namespace Scripting; + + HSQUIRRELVM vm = global_vm; int oldtop = sq_gettop(vm); try { @@ -859,8 +969,8 @@ WorldMap::load_state() if(SQ_FAILED(sq_get(vm, -2))) throw Scripting::SquirrelError(vm, "Couldn't get levels"); - for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) { - Level* level = *i; + for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { + LevelTile* level = *i; sq_pushstring(vm, level->name.c_str(), -1); if(SQ_SUCCEEDED(sq_get(vm, -2))) { level->solved = read_bool(vm, "solved"); @@ -887,8 +997,8 @@ size_t WorldMap::solved_level_count() { size_t count = 0; - for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) { - Level* level = *i; + for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { + LevelTile* level = *i; if(level->solved) count++; @@ -896,13 +1006,39 @@ WorldMap::solved_level_count() return count; } - -void -WorldMap::loadmap(const std::string& filename) + +HSQUIRRELVM +WorldMap::run_script(std::istream& in, const std::string& sourcename) { - savegame_file = ""; - map_filename = filename; - load_map(); -} + using namespace Scripting; + + // garbage collect thread list + for(ScriptList::iterator i = scripts.begin(); + i != scripts.end(); ) { + HSQOBJECT& object = *i; + HSQUIRRELVM vm = object_to_vm(object); + + if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) { + sq_release(global_vm, &object); + i = scripts.erase(i); + continue; + } + + ++i; + } + HSQOBJECT object = create_thread(global_vm); + scripts.push_back(object); + + HSQUIRRELVM vm = object_to_vm(object); + + // set worldmap_table as roottable for the thread + sq_pushobject(vm, worldmap_table); + sq_setroottable(vm); + + compile_and_run(vm, in, sourcename); + + return vm; +} + } // namespace WorldMapNS