X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fobject%2Ftilemap.cpp;h=91360920364e78958e52a2494128f1dd674d1af7;hb=7be55d4d3cfdeea2c68405c4b2a1999b8e246331;hp=b54c5deed2ec57a31bbf9c63097fd8fb19dfdcaa;hpb=1ff837fd2c166214bfec2b573424ddfc03cce8c1;p=supertux.git diff --git a/src/object/tilemap.cpp b/src/object/tilemap.cpp index b54c5deed..913609203 100644 --- a/src/object/tilemap.cpp +++ b/src/object/tilemap.cpp @@ -1,12 +1,10 @@ -// $Id$ -// // SuperTux // Copyright (C) 2006 Matthias Braun // -// 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 @@ -14,85 +12,155 @@ // 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. - -#include - -#include -#include -#include -#include -#include - -#include "tilemap.hpp" -#include "video/drawing_context.hpp" -#include "level.hpp" -#include "tile.hpp" -#include "resources.hpp" -#include "tile_manager.hpp" -#include "lisp/lisp.hpp" -#include "lisp/writer.hpp" -#include "object_factory.hpp" -#include "main.hpp" -#include "log.hpp" - -TileMap::TileMap() - : solid(false), speed(1), width(0), height(0), z_pos(0), - drawing_effect(NO_EFFECT) +// along with this program. If not, see . + +#include + +#include "object/tilemap.hpp" +#include "scripting/squirrel_util.hpp" +#include "scripting/tilemap.hpp" +#include "supertux/globals.hpp" +#include "supertux/object_factory.hpp" +#include "supertux/tile_manager.hpp" +#include "supertux/tile_set.hpp" +#include "util/reader.hpp" + +TileMap::TileMap(const TileSet *new_tileset) : + tileset(new_tileset), + tiles(), + real_solid(false), + effective_solid(false), + speed_x(1), + speed_y(1), + width(0), + height(0), + z_pos(0), + offset(Vector(0,0)), + movement(0,0), + drawing_effect(NO_EFFECT), + alpha(1.0), + current_alpha(1.0), + remaining_fade_time(0), + path(), + walker(), + draw_target(DrawingContext::NORMAL) { - tilemanager = tile_manager; - - if(solid) - flags |= FLAG_SOLID; } -TileMap::TileMap(const lisp::Lisp& reader, TileManager* new_tile_manager) - : solid(false), speed(1), width(-1), height(-1), z_pos(0), - drawing_effect(NO_EFFECT) +TileMap::TileMap(const Reader& reader) : + tileset(), + tiles(), + real_solid(false), + effective_solid(false), + speed_x(1), + speed_y(1), + width(-1), + height(-1), + z_pos(0), + offset(Vector(0,0)), + movement(Vector(0,0)), + drawing_effect(NO_EFFECT), + alpha(1.0), + current_alpha(1.0), + remaining_fade_time(0), + path(), + walker(), + draw_target(DrawingContext::NORMAL) { - tilemanager = new_tile_manager; - if(tilemanager == 0) - tilemanager = tile_manager; + tileset = current_tileset; + assert(tileset != NULL); + + reader.get("name", name); + reader.get("solid", real_solid); + reader.get("speed", speed_x); + reader.get("speed-y", speed_y); - reader.get("z-pos", z_pos); - reader.get("solid", solid); - reader.get("speed", speed); + z_pos = reader_get_layer (reader, /* default = */ 0); - if(solid && speed != 1) { + if(real_solid && ((speed_x != 1) || (speed_y != 1))) { log_warning << "Speed of solid tilemap is not 1. fixing" << std::endl; - speed = 1; + speed_x = 1; + speed_y = 1; + } + + const lisp::Lisp* pathLisp = reader.get_lisp("path"); + if (pathLisp) { + path.reset(new Path()); + path->read(*pathLisp); + walker.reset(new PathWalker(path.get(), /*running*/false)); + Vector v = path->get_base(); + set_offset(v); + } + + std::string draw_target_s = "normal"; + reader.get("draw-target", draw_target_s); + if (draw_target_s == "normal") draw_target = DrawingContext::NORMAL; + if (draw_target_s == "lightmap") draw_target = DrawingContext::LIGHTMAP; + + if (reader.get("alpha", alpha)) { + current_alpha = alpha; } - if(solid) - flags |= FLAG_SOLID; - + + /* Initialize effective_solid based on real_solid and current_alpha. */ + effective_solid = real_solid; + update_effective_solid (); + reader.get("width", width); reader.get("height", height); if(width < 0 || height < 0) throw std::runtime_error("Invalid/No width/height specified in tilemap."); - if(!reader.get_vector("tiles", tiles)) + if(!reader.get("tiles", tiles)) throw std::runtime_error("No tiles in tilemap."); if(int(tiles.size()) != width*height) { throw std::runtime_error("wrong number of tiles in tilemap."); } - // make sure all tiles are loaded - for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i) - tilemanager->get(*i); + bool empty = true; + + // make sure all tiles used on the tilemap are loaded and tilemap isn't empty + for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i) { + if(*i != 0) { + empty = false; + } + + tileset->get(*i); + } + + if(empty) + { + log_info << "Tilemap '" << name << "', z-pos '" << z_pos << "' is empty." << std::endl; + } } -TileMap::TileMap(int z_pos, bool solid, size_t width, size_t height) - : solid(solid), speed(1), width(0), height(0), z_pos(z_pos), - drawing_effect(NO_EFFECT) +TileMap::TileMap(const TileSet *new_tileset, std::string name_, int z_pos_, + bool solid, size_t width_, size_t height_) : + tileset(new_tileset), + tiles(), + real_solid(solid), + effective_solid(solid), + speed_x(1), + speed_y(1), + width(0), + height(0), + z_pos(z_pos_), + offset(Vector(0,0)), + movement(Vector(0,0)), + drawing_effect(NO_EFFECT), + alpha(1.0), + current_alpha(1.0), + remaining_fade_time(0), + path(), + walker(), + draw_target(DrawingContext::NORMAL) { - tilemanager = tile_manager; - - resize(width, height); + this->name = name_; - if(solid) - flags |= FLAG_SOLID; + if (this->z_pos > (LAYER_GUI - 100)) + this->z_pos = LAYER_GUI - 100; + + resize(width_, height_); } TileMap::~TileMap() @@ -100,68 +168,120 @@ TileMap::~TileMap() } void -TileMap::write(lisp::Writer& writer) +TileMap::update(float elapsed_time) { - writer.start_list("tilemap"); - - writer.write_int("z-pos", z_pos); - - writer.write_bool("solid", solid); - writer.write_float("speed", speed); - writer.write_int("width", width); - writer.write_int("height", height); - writer.write_int_vector("tiles", tiles); - - writer.end_list("tilemap"); -} + // handle tilemap fading + if (current_alpha != alpha) { + remaining_fade_time = std::max(0.0f, remaining_fade_time - elapsed_time); + if (remaining_fade_time == 0.0f) { + current_alpha = alpha; + } else { + float amt = (alpha - current_alpha) / (remaining_fade_time / elapsed_time); + if (amt > 0) current_alpha = std::min(current_alpha + amt, alpha); + if (amt < 0) current_alpha = std::max(current_alpha + amt, alpha); + } + update_effective_solid (); + } -void -TileMap::update(float ) -{ + movement = Vector(0,0); + // if we have a path to follow, follow it + if (walker.get()) { + Vector v = walker->advance(elapsed_time); + movement = v - get_offset(); + set_offset(v); + } } void TileMap::draw(DrawingContext& context) { + // skip draw if current opacity is 0.0 + if (current_alpha == 0.0) return; + context.push_transform(); + if(draw_target != DrawingContext::NORMAL) { + context.push_target(); + context.set_target(draw_target); + } + + if(drawing_effect != 0) context.set_drawing_effect(drawing_effect); + if(current_alpha != 1.0) context.set_alpha(current_alpha); - if(drawing_effect != 0) - context.set_drawing_effect(drawing_effect); + /* Force the translation to be an integer so that the tiles appear sharper. + * For consistency (i.e., to avoid 1-pixel gaps), this needs to be done even + * for solid tilemaps that are guaranteed to have speed 1. + * FIXME Force integer translation for all graphics, not just tilemaps. */ float trans_x = roundf(context.get_translation().x); float trans_y = roundf(context.get_translation().y); - context.set_translation(Vector(trans_x * speed, trans_y * speed)); - - /** if we don't round here, we'll have a 1 pixel gap on screen sometimes. - * I have no idea why */ - float start_x = roundf(context.get_translation().x); - if(start_x < 0) - start_x = 0; - float start_y = roundf(context.get_translation().y); - if(start_y < 0) - start_y = 0; - float end_x = std::min(start_x + SCREEN_WIDTH, float(width * 32)); - float end_y = std::min(start_y + SCREEN_HEIGHT, float(height * 32)); - start_x -= int(start_x) % 32; - start_y -= int(start_y) % 32; - int tsx = int(start_x / 32); // tilestartindex x - int tsy = int(start_y / 32); // tilestartindex y + context.set_translation(Vector(int(trans_x * speed_x), + int(trans_y * speed_y))); + + Rectf draw_rect = Rectf(context.get_translation(), + context.get_translation() + Vector(SCREEN_WIDTH, SCREEN_HEIGHT)); + Rect t_draw_rect = get_tiles_overlapping(draw_rect); + Vector start = get_tile_position(t_draw_rect.left, t_draw_rect.top); Vector pos; int tx, ty; - for(pos.x = start_x, tx = tsx; pos.x < end_x; pos.x += 32, ++tx) { - for(pos.y = start_y, ty = tsy; pos.y < end_y; pos.y += 32, ++ty) { - const Tile* tile = tilemanager->get(tiles[ty*width + tx]); + + for(pos.x = start.x, tx = t_draw_rect.left; tx < t_draw_rect.right; pos.x += 32, ++tx) { + for(pos.y = start.y, ty = t_draw_rect.top; ty < t_draw_rect.bottom; pos.y += 32, ++ty) { + int index = ty*width + tx; + assert (index >= 0); + assert (index < (width * height)); + + if (tiles[index] == 0) continue; + const Tile* tile = tileset->get(tiles[index]); assert(tile != 0); tile->draw(context, pos, z_pos); - } - } + } /* for (pos y) */ + } /* for (pos x) */ + if(draw_target != DrawingContext::NORMAL) { + context.pop_target(); + } context.pop_transform(); } void +TileMap::goto_node(int node_no) +{ + if (!walker.get()) return; + walker->goto_node(node_no); +} + +void +TileMap::start_moving() +{ + if (!walker.get()) return; + walker->start_moving(); +} + +void +TileMap::stop_moving() +{ + if (!walker.get()) return; + walker->stop_moving(); +} + +void +TileMap::expose(HSQUIRRELVM vm, SQInteger table_idx) +{ + if (name.empty()) return; + scripting::TileMap* _this = new scripting::TileMap(this); + expose_object(vm, table_idx, _this, name, true); +} + +void +TileMap::unexpose(HSQUIRRELVM vm, SQInteger table_idx) +{ + if (name.empty()) return; + scripting::unexpose_object(vm, table_idx, name); +} + +void TileMap::set(int newwidth, int newheight, const std::vector&newt, - int new_z_pos, bool newsolid) + int new_z_pos, bool newsolid) { if(int(newt.size()) != newwidth * newheight) throw std::runtime_error("Wrong tilecount count."); @@ -172,18 +292,20 @@ TileMap::set(int newwidth, int newheight, const std::vector&newt, tiles.resize(newt.size()); tiles = newt; - z_pos = new_z_pos; - solid = newsolid; - if(solid) - flags |= FLAG_SOLID; + if (new_z_pos > (LAYER_GUI - 100)) + z_pos = LAYER_GUI - 100; + else + z_pos = new_z_pos; + real_solid = newsolid; + update_effective_solid (); // make sure all tiles are loaded for(Tiles::iterator i = tiles.begin(); i != tiles.end(); ++i) - tilemanager->get(*i); + tileset->get(*i); } void -TileMap::resize(int new_width, int new_height) +TileMap::resize(int new_width, int new_height, int fill_id) { if(new_width < width) { // remap tiles for new width @@ -193,18 +315,18 @@ TileMap::resize(int new_width, int new_height) } } } - - tiles.resize(new_width * new_height); - + + tiles.resize(new_width * new_height, fill_id); + if(new_width > width) { // remap tiles for(int y = std::min(height, new_height)-1; y >= 0; --y) { for(int x = new_width-1; x >= 0; --x) { if(x >= width) { - tiles[y * new_width + x] = 0; + tiles[y * new_width + x] = fill_id; continue; } - + tiles[y * new_width + x] = tiles[y * width + x]; } } @@ -214,21 +336,56 @@ TileMap::resize(int new_width, int new_height) width = new_width; } -const Tile* -TileMap::get_tile(int x, int y) const +Rect +TileMap::get_tiles_overlapping(const Rectf &rect) const +{ + Rectf rect2 = rect; + rect2.move(-offset); + + int t_left = std::max(0 , int(floorf(rect2.get_left () / 32))); + int t_right = std::min(width , int(ceilf (rect2.get_right () / 32))); + int t_top = std::max(0 , int(floorf(rect2.get_top () / 32))); + int t_bottom = std::min(height, int(ceilf (rect2.get_bottom() / 32))); + return Rect(t_left, t_top, t_right, t_bottom); +} + +void +TileMap::set_solid(bool solid) +{ + this->real_solid = solid; + update_effective_solid (); +} + +uint32_t +TileMap::get_tile_id(int x, int y) const { if(x < 0 || x >= width || y < 0 || y >= height) { //log_warning << "tile outside tilemap requested" << std::endl; - return tilemanager->get(0); + return 0; } - return tilemanager->get(tiles[y*width + x]); + return tiles[y*width + x]; +} + +const Tile* +TileMap::get_tile(int x, int y) const +{ + uint32_t id = get_tile_id(x, y); + return tileset->get(id); +} + +uint32_t +TileMap::get_tile_id_at(const Vector& pos) const +{ + Vector xy = (pos - offset) / 32; + return get_tile_id(int(xy.x), int(xy.y)); } const Tile* TileMap::get_tile_at(const Vector& pos) const { - return get_tile(int(pos.x)/32, int(pos.y)/32); + uint32_t id = get_tile_id_at(pos); + return tileset->get(id); } void @@ -241,16 +398,57 @@ TileMap::change(int x, int y, uint32_t newtile) void TileMap::change_at(const Vector& pos, uint32_t newtile) { - change(int(pos.x)/32, int(pos.y)/32, newtile); + Vector xy = (pos - offset) / 32; + change(int(xy.x), int(xy.y), newtile); } void TileMap::change_all(uint32_t oldtile, uint32_t newtile) { - for (size_t x = 0; x < get_width(); x++) + for (size_t x = 0; x < get_width(); x++) { for (size_t y = 0; y < get_height(); y++) { - if (get_tile(x,y)->getID() == oldtile) change(x,y,newtile); + if (get_tile_id(x,y) != oldtile) + continue; + + change(x,y,newtile); } + } +} + +void +TileMap::fade(float alpha_, float seconds) +{ + this->alpha = alpha_; + this->remaining_fade_time = seconds; +} + +void +TileMap::set_alpha(float alpha_) +{ + this->alpha = alpha_; + this->current_alpha = alpha; + this->remaining_fade_time = 0; + update_effective_solid (); +} + +float +TileMap::get_alpha() +{ + return this->current_alpha; +} + +/* + * Private methods + */ +void +TileMap::update_effective_solid (void) +{ + if (!real_solid) + effective_solid = false; + else if (effective_solid && (current_alpha < .25)) + effective_solid = false; + else if (!effective_solid && (current_alpha >= .75)) + effective_solid = true; } -IMPLEMENT_FACTORY(TileMap, "tilemap"); +/* EOF */