X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fworldmap%2Fworldmap.cpp;h=7ee9c5ee161dd8e4e0cefcf6572b51bf47134c8f;hb=8a627e73d824b5a14249cfe066dc2fdc643ce28d;hp=d834fac1a3d7e2bf0fddee9c3156a8db04321aec;hpb=9b58f72e1c6900540c0ee00a800ed57d2c1f4974;p=supertux.git diff --git a/src/worldmap/worldmap.cpp b/src/worldmap/worldmap.cpp index d834fac1a..7ee9c5ee1 100644 --- a/src/worldmap/worldmap.cpp +++ b/src/worldmap/worldmap.cpp @@ -58,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" @@ -129,14 +128,14 @@ string_to_direction(const std::string& directory) //--------------------------------------------------------------------------- -WorldMap::WorldMap(const std::string& filename) - : 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")); - + tux = new Tux(this); add_object(tux); - + name = ""; music = "music/salcon.ogg"; @@ -151,16 +150,45 @@ WorldMap::WorldMap(const std::string& filename) 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; for(GameObjects::iterator i = game_objects.begin(); - i != game_objects.end(); ++i) - delete *i; + i != game_objects.end(); ++i) { + GameObject* object = *i; + object->unref(); + } for(SpawnPoints::iterator i = spawn_points.begin(); i != spawn_points.end(); ++i) { @@ -176,10 +204,35 @@ WorldMap::add_object(GameObject* object) solids = tilemap; } + object->ref(); game_objects.push_back(object); } void +WorldMap::move_to_spawnpoint(const std::string& spawnpoint) +{ + 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)); +} + +void WorldMap::load(const std::string& filename) { map_filename = filename; @@ -194,11 +247,11 @@ WorldMap::load(const std::string& filename) throw std::runtime_error("file isn't a supertux-level file."); lisp->get("name", name); - + const lisp::Lisp* sector = lisp->get_lisp("sector"); if(!sector) throw std::runtime_error("No sector sepcified in worldmap file."); - + lisp::ListIterator iter(sector); while(iter.next()) { if(iter.item() == "tilemap") { @@ -207,21 +260,27 @@ WorldMap::load(const std::string& filename) add_object(new Background(*(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()); spawn_points.push_back(sp); } else if(iter.item() == "level") { 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); + 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 { @@ -231,16 +290,7 @@ WorldMap::load(const std::string& filename) 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; @@ -258,12 +308,12 @@ WorldMap::get_level_title(LevelTile& level) try { lisp::Parser parser; - std::auto_ptr root (parser.parse(levels_path + level.name)); + std::auto_ptr 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("name", level.title); } catch(std::exception& e) { log_warning << "Problem when reading leveltitle: " << e.what() << std::endl; @@ -375,7 +425,7 @@ WorldMap::finished_level(Level* gamelevel) // Try to detect the next direction to which we should walk // FIXME: Mostly a hack Direction dir = D_NONE; - + const Tile* tile = at(tux->get_tile_pos()); // first, test for crossroads @@ -403,10 +453,8 @@ WorldMap::finished_level(Level* gamelevel) 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; } @@ -423,7 +471,7 @@ WorldMap::update(float delta) if(menu == worldmap_menu.get()) { switch (worldmap_menu->check()) { - case MNID_RETURNWORLDMAP: // Return to game + case MNID_RETURNWORLDMAP: // Return to game Menu::set_current(0); break; case MNID_QUITWORLDMAP: // Quit Worldmap @@ -436,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); } @@ -447,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; @@ -477,23 +524,23 @@ 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 */ LevelTile* level = at_level(); if (!level) { @@ -505,7 +552,7 @@ WorldMap::update(float delta) 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->name; + std::string levelfile = levels_path + level->get_name(); main_loop->push_screen(new GameSession(levelfile, &level->statistics), new ShrinkFade(shrinkpos, 0.5)); } catch(std::exception& e) { @@ -544,7 +591,7 @@ WorldMap::at_special_tile() i != special_tiles.end(); ++i) { SpecialTile* special_tile = *i; if (special_tile->pos == tux->get_tile_pos()) - return special_tile; + return special_tile; } return NULL; @@ -563,18 +610,29 @@ WorldMap::at_sprite_change(const Vector& pos) 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; +} + void WorldMap::draw(DrawingContext& context) { 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); } - + draw_status(context); context.pop_transform(); } @@ -584,13 +642,13 @@ WorldMap::draw_status(DrawingContext& context) { context.push_transform(); context.set_translation(Vector(0, 0)); - + player_status->draw(context); if (!tux->is_moving()) { for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { LevelTile* level = *i; - + if (level->pos == tux->get_tile_pos()) { if(level->title == "") get_level_title(*level); @@ -599,7 +657,20 @@ WorldMap::draw_status(DrawingContext& context) Vector(SCREEN_WIDTH/2, 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; } @@ -608,22 +679,30 @@ WorldMap::draw_status(DrawingContext& context) for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) { SpecialTile* special_tile = *i; - + 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, + context.draw_text(gold_text, special_tile->map_message, Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60), CENTER_ALLIGN, LAYER_FOREGROUND1); 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 */ if(passive_message_timer.started()) - context.draw_text(gold_text, passive_message, + context.draw_text(gold_text, passive_message, Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60), CENTER_ALLIGN, LAYER_FOREGROUND1); @@ -638,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) @@ -682,7 +797,7 @@ static float read_float(HSQUIRRELVM vm, const char* name) msg << "Couldn't get float value for '" << name << "' from table"; throw Scripting::SquirrelError(vm, msg.str()); } - + float result; if(SQ_FAILED(sq_getfloat(vm, -1, &result))) { std::ostringstream msg; @@ -702,7 +817,7 @@ static std::string read_string(HSQUIRRELVM vm, const char* name) msg << "Couldn't get string value for '" << name << "' from table"; throw Scripting::SquirrelError(vm, msg.str()); } - + const char* result; if(SQ_FAILED(sq_getstring(vm, -1, &result))) { std::ostringstream msg; @@ -721,8 +836,8 @@ static bool read_bool(HSQUIRRELVM vm, const char* name) std::ostringstream msg; msg << "Couldn't get bool value for '" << name << "' from table"; throw Scripting::SquirrelError(vm, msg.str()); - } - + } + SQBool result; if(SQ_FAILED(sq_getbool(vm, -1, &result))) { std::ostringstream msg; @@ -737,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 { @@ -759,7 +876,7 @@ WorldMap::save_state() if(SQ_FAILED(sq_get(vm, -2))) 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); @@ -771,32 +888,32 @@ WorldMap::save_state() // store tux sq_pushstring(vm, "tux", -1); sq_newtable(vm); - + store_float(vm, "x", tux->get_tile_pos().x); store_float(vm, "y", tux->get_tile_pos().y); store_string(vm, "back", direction_to_string(tux->back_direction)); sq_createslot(vm, -3); - + // levels... sq_pushstring(vm, "levels", -1); sq_newtable(vm); for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { LevelTile* level = *i; - + if (level->solved) { - sq_pushstring(vm, level->name.c_str(), -1); + sq_pushstring(vm, level->get_name().c_str(), -1); sq_newtable(vm); - store_bool(vm, "solved", true); + store_bool(vm, "solved", true); // TODO write statistics // i->statistics.write(writer); sq_createslot(vm, -3); } } - + sq_createslot(vm, -3); // push world into worlds table @@ -811,9 +928,11 @@ 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 { // get state table sq_pushroottable(vm); @@ -852,7 +971,7 @@ WorldMap::load_state() for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { LevelTile* level = *i; - sq_pushstring(vm, level->name.c_str(), -1); + 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"); @@ -880,12 +999,46 @@ WorldMap::solved_level_count() size_t count = 0; for(LevelTiles::iterator i = levels.begin(); i != levels.end(); ++i) { LevelTile* level = *i; - + if(level->solved) count++; } return count; } - + +HSQUIRRELVM +WorldMap::run_script(std::istream& in, const std::string& sourcename) +{ + 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