From: Christoph Sommer Date: Tue, 3 Apr 2007 21:12:55 +0000 (+0000) Subject: Allowed multiple solid Tilemaps on a Worldmap X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=f9eba980c51c60137ec197182d140e0f76a8d7b2;p=supertux.git Allowed multiple solid Tilemaps on a Worldmap SVN-Revision: 4961 --- diff --git a/src/worldmap/tux.cpp b/src/worldmap/tux.cpp index 8c4339feb..0aa3c21a8 100644 --- a/src/worldmap/tux.cpp +++ b/src/worldmap/tux.cpp @@ -145,12 +145,12 @@ Tux::tryStartWalking() } bool -Tux::canWalk(const Tile* tile, Direction dir) +Tux::canWalk(int tile_data, Direction dir) { - return ((tile->getData() & Tile::WORLDMAP_NORTH && dir == D_NORTH) || - (tile->getData() & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) || - (tile->getData() & Tile::WORLDMAP_EAST && dir == D_EAST) || - (tile->getData() & Tile::WORLDMAP_WEST && dir == D_WEST)); + return ((tile_data & Tile::WORLDMAP_NORTH && dir == D_NORTH) || + (tile_data & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) || + (tile_data & Tile::WORLDMAP_EAST && dir == D_EAST) || + (tile_data & Tile::WORLDMAP_WEST && dir == D_WEST)); } void @@ -205,7 +205,7 @@ Tux::tryContinueWalking(float elapsed_time) // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message if ((worldmap->at_level()) - || (worldmap->at(tile_pos)->getData() & Tile::WORLDMAP_STOP) + || (worldmap->tile_data_at(tile_pos) & Tile::WORLDMAP_STOP) || (special_tile && !special_tile->passive_message && special_tile->script == "") || (teleporter)) { @@ -217,21 +217,21 @@ Tux::tryContinueWalking(float elapsed_time) } // if user wants to change direction, try changing, else guess the direction in which to walk next - const Tile* tile = worldmap->at(tile_pos); + const int tile_data = worldmap->tile_data_at(tile_pos); if (direction != input_direction) { - if(canWalk(tile, input_direction)) { + if(canWalk(tile_data, input_direction)) { direction = input_direction; back_direction = reverse_dir(direction); } } else { Direction dir = D_NONE; - if (tile->getData() & Tile::WORLDMAP_NORTH && back_direction != D_NORTH) + if (tile_data & Tile::WORLDMAP_NORTH && back_direction != D_NORTH) dir = D_NORTH; - else if (tile->getData() & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH) + else if (tile_data & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH) dir = D_SOUTH; - else if (tile->getData() & Tile::WORLDMAP_EAST && back_direction != D_EAST) + else if (tile_data & Tile::WORLDMAP_EAST && back_direction != D_EAST) dir = D_EAST; - else if (tile->getData() & Tile::WORLDMAP_WEST && back_direction != D_WEST) + else if (tile_data & Tile::WORLDMAP_WEST && back_direction != D_WEST) dir = D_WEST; if (dir == D_NONE) { diff --git a/src/worldmap/tux.hpp b/src/worldmap/tux.hpp index 67ec76d4f..4c36042d4 100644 --- a/src/worldmap/tux.hpp +++ b/src/worldmap/tux.hpp @@ -50,7 +50,7 @@ private: void stop(); - bool canWalk(const Tile* tile, Direction dir); /**< check if we can leave "tile" in direction "dir" */ + bool canWalk(int tile_data, Direction dir); /**< check if we can leave a tile (with given "tile_data") in direction "dir" */ void updateInputDirection(); /**< if controller was pressed, update input_direction */ void tryStartWalking(); /**< try starting to walk in input_direction */ void tryContinueWalking(float elapsed_time); /**< try to continue walking in current direction */ diff --git a/src/worldmap/worldmap.cpp b/src/worldmap/worldmap.cpp index 2a6b2552a..0477ed8a6 100644 --- a/src/worldmap/worldmap.cpp +++ b/src/worldmap/worldmap.cpp @@ -134,7 +134,7 @@ string_to_direction(const std::string& directory) //--------------------------------------------------------------------------- WorldMap::WorldMap(const std::string& filename, const std::string& force_spawnpoint) - : tux(0), solids(0), ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), force_spawnpoint(force_spawnpoint), in_level(false) + : 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")); @@ -210,7 +210,7 @@ WorldMap::add_object(GameObject* object) { TileMap* tilemap = dynamic_cast (object); if(tilemap != 0 && tilemap->is_solid()) { - solids = tilemap; + solid_tilemaps.push_back(tilemap); } object->ref(); @@ -335,7 +335,7 @@ WorldMap::load(const std::string& filename) log_warning << "Unknown token '" << iter.item() << "' in worldmap" << std::endl; } } - if(solids == 0) + if(solid_tilemaps.size() == 0) throw std::runtime_error("No solid tilemap specified"); move_to_spawnpoint("main"); @@ -419,30 +419,32 @@ WorldMap::path_ok(Direction direction, const Vector& old_pos, Vector* new_pos) { *new_pos = get_next_tile(old_pos, direction); - if (!(new_pos->x >= 0 && new_pos->x < solids->get_width() - && new_pos->y >= 0 && new_pos->y < solids->get_height())) + 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; } 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 (at(old_pos)->getData() & Tile::WORLDMAP_WEST - && at(*new_pos)->getData() & Tile::WORLDMAP_EAST); + return (old_tile_data & Tile::WORLDMAP_WEST + && new_tile_data & Tile::WORLDMAP_EAST); case D_EAST: - return (at(old_pos)->getData() & Tile::WORLDMAP_EAST - && at(*new_pos)->getData() & Tile::WORLDMAP_WEST); + return (old_tile_data & Tile::WORLDMAP_EAST + && new_tile_data & Tile::WORLDMAP_WEST); case D_NORTH: - return (at(old_pos)->getData() & Tile::WORLDMAP_NORTH - && at(*new_pos)->getData() & Tile::WORLDMAP_SOUTH); + return (old_tile_data & Tile::WORLDMAP_NORTH + && new_tile_data & Tile::WORLDMAP_SOUTH); case D_SOUTH: - return (at(old_pos)->getData() & Tile::WORLDMAP_SOUTH - && at(*new_pos)->getData() & Tile::WORLDMAP_NORTH); + 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"); @@ -472,9 +474,7 @@ WorldMap::finished_level(Level* gamelevel) // FIXME: Mostly a hack Direction dir = D_NONE; - const Tile* tile = at(tux->get_tile_pos()); - - int dirdata = tile->getData() & Tile::WORLDMAP_DIR_MASK; + int dirdata = available_directions_at(tux->get_tile_pos()); // first, test for crossroads if (dirdata == Tile::WORLDMAP_CNSE || dirdata == Tile::WORLDMAP_CNSW || @@ -552,6 +552,17 @@ 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) + { + TileMap* tm = dynamic_cast(*i); + 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; @@ -562,15 +573,15 @@ WorldMap::update(float delta) if (camera_offset.y < 0) camera_offset.y = 0; - if (camera_offset.x > (int)solids->get_width()*32 - SCREEN_WIDTH) - camera_offset.x = (int)solids->get_width()*32 - SCREEN_WIDTH; - if (camera_offset.y > (int)solids->get_height()*32 - SCREEN_HEIGHT) - camera_offset.y = (int)solids->get_height()*32 - SCREEN_HEIGHT; + 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; - if (int(solids->get_width()*32) < SCREEN_WIDTH) - camera_offset.x = solids->get_width()*16.0 - SCREEN_WIDTH/2.0; - if (int(solids->get_height()*32) < SCREEN_HEIGHT) - camera_offset.y = solids->get_height()*16.0 - SCREEN_HEIGHT/2.0; + 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; // handle input bool enter_level = false; @@ -610,8 +621,8 @@ WorldMap::update(float delta) LevelTile* level = at_level(); if (!level) { //Respawn if player on a tile with no level and nowhere to go. - const Tile* tile = at(tux->get_tile_pos()); - if(!( tile->getData() & ( Tile::WORLDMAP_NORTH | Tile::WORLDMAP_SOUTH | Tile::WORLDMAP_WEST | Tile::WORLDMAP_EAST ))){ + 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; @@ -644,10 +655,25 @@ WorldMap::update(float delta) } } -const Tile* -WorldMap::at(Vector p) +int +WorldMap::tile_data_at(Vector p) +{ + int dirs = 0; + + 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(); + dirs |= dirdata; + } + + return dirs; +} + +int +WorldMap::available_directions_at(Vector p) { - return solids->get_tile((int) p.x, (int) p.y); + return tile_data_at(p) & Tile::WORLDMAP_DIR_MASK; } LevelTile* @@ -702,7 +728,7 @@ WorldMap::at_teleporter(const Vector& pos) void WorldMap::draw(DrawingContext& context) { - if (int(solids->get_width()*32) < SCREEN_WIDTH || int(solids->get_height()*32) < SCREEN_HEIGHT) + 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); @@ -716,6 +742,27 @@ WorldMap::draw(DrawingContext& context) 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; + 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); + } + } +*/ + draw_status(context); context.pop_transform(); } @@ -1127,4 +1174,26 @@ WorldMap::run_script(std::istream& in, const std::string& sourcename) return vm; } +float +WorldMap::get_width() const +{ + float width = 0; + 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(); + } + return width; +} + +float +WorldMap::get_height() const +{ + float height = 0; + 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 diff --git a/src/worldmap/worldmap.hpp b/src/worldmap/worldmap.hpp index e988a6935..bfcf96099 100644 --- a/src/worldmap/worldmap.hpp +++ b/src/worldmap/worldmap.hpp @@ -84,7 +84,7 @@ private: typedef std::vector GameObjects; GameObjects game_objects; - TileMap* solids; + std::list solid_tilemaps; std::auto_ptr tile_manager; @@ -139,7 +139,18 @@ public: virtual void draw(DrawingContext& context); Vector get_next_tile(Vector pos, Direction direction); - const Tile* at(Vector pos); + + /** + * gets a bitfield of Tile::WORLDMAP_NORTH | Tile::WORLDMAP_WEST | ... values, + * which indicates the directions Tux can move to when at the given position. + */ + int available_directions_at(Vector pos); + + /** + * returns a bitfield representing the union of all Tile::WORLDMAP_XXX values + * of all solid tiles at the given position + */ + int tile_data_at(Vector pos); size_t level_count(); size_t solved_level_count(); @@ -189,6 +200,16 @@ public: */ void move_to_spawnpoint(const std::string& spawnpoint); + /** + * returns the width (in tiles) of a worldmap + */ + float get_width() const; + + /** + * returns the height (in tiles) of a worldmap + */ + float get_height() const; + private: void get_level_title(LevelTile& level); void draw_status(DrawingContext& context);