Commit f611cbbc authored by louiz’'s avatar louiz’

Switch the map to a isometric, again (staggered, not just isometric)

fix #2277
parent 0c67404d
......@@ -4,10 +4,12 @@
#include <world/layer.hpp>
#include <gui/sprites/archive_sprite.hpp>
#include <gui/sprites/pic_sprite.hpp>
#include <climits>
#include <cstdlib>
Camera::Camera(ClientWorld* world, GraphMap* map, sf::RenderWindow* win, Screen* screen):
x(0),
y(0),
y(56),
zoom(1),
focused(true),
movement_speed(0.023),
......@@ -263,64 +265,74 @@ void Camera::update(const Duration& dt)
void Camera::draw()
{
const sf::Vector2u win_size = this->win->getSize();
GraphTile* tile;
const sf::Vector2i mouse_pos = sf::Mouse::getPosition(*this->win);
const unsigned int cell_on_mouse = selected_cell;
unsigned int mouse_row = -1;
unsigned int mouse_col = -1;
uint layer_width = this->map->twidth;
uint layer_height = this->map->theight;
uint row_number = layer_height + layer_width - 1;
auto unit_it = this->world->units.begin();
if (cell_on_mouse != UINT_MAX)
{
mouse_row = cell_on_mouse / this->map->get_width_in_tiles();
mouse_col = cell_on_mouse % this->map->get_width_in_tiles();
}
uint row;
bool upper_part;
for (row = 0; row < row_number; ++row)
// Draw all tiles even the ones that are not visible for the camera.
GraphTile* tile;
// Iterate all the rows, from top to bottom
for (unsigned int row = 0; row < this->map->get_height_in_tiles(); row++)
{
upper_part = row < layer_height;
uint yoffset = -LEVEL_HEIGHT;
// Position where we will draw, in pixels
unsigned int y = row * (TILE_HEIGHT / 2);
unsigned int x;
// Draw the current row of all layers, from bottom to top
for (Layer* layer: this->map->layers)
{
yoffset += LEVEL_HEIGHT;
if (!layer->cells)
continue;
uint col = 0;
int start_col;
int end_col;
const int step = layer_width - 1;
if (upper_part)
{
start_col = row * layer_width;
end_col = 0;
}
else
{
start_col = (layer_height - 1) * layer_width + row - layer_width + 1;
end_col = layer_width - 1 + (row - layer_height + 1) * layer_width - 1;
}
if (start_col == 0)
end_col = -1;
for (int n = start_col; n > end_col; n -= step)
{
++col;
std::size_t gid = layer->cells[n];
tile = this->map->tiles[gid];
if (tile)
{
uint sx = ((96 * layer_width) / 2) - (row * 96/2) + col * 96;
if (!upper_part)
sx += 96 * (row - layer_height + 1);
const uint sy = (72 / 2) * row;
tile->sprite.setPosition(sx - this->x, sy - this->y - yoffset);
this->win->draw(tile->sprite);
}
}
}
for (Sprite* sprite: this->sprites)
{
sprite->draw(this, this->world, this->screen);
if (layer && layer->cells)
for (unsigned int col = 0; col < this->map->get_width_in_tiles(); col++)
{
x = col * TILE_WIDTH;
if (row % 2 != 0)
x += HALF_TILE_W;
std::size_t cell = col + (row * this->map->get_height_in_tiles());
std::size_t gid = layer->cells[cell];
tile = this->map->tiles[gid];
if (tile)
{
constexpr char tile_opacity = 255;
tile->sprite.setColor(sf::Color(255, 255, 255, tile_opacity));
if (cell_on_mouse != UINT_MAX &&
row == mouse_row && col == mouse_col)
tile->sprite.setColor(sf::Color(255, 0, 255, tile_opacity));
tile->sprite.setPosition(x - this->x, y - this->y);
this->win->draw(tile->sprite);
}
}
// The row for the next layer must be shifted from a few pixel to
// the top
y -= LEVEL_HEIGHT;
}
}
for (Sprite* sprite: this->sprites)
{
sprite->draw(this, this->world, this->screen);
}
this->draw_mouse_selection();
// Debug
const std::string cam_pos = "Camera position: " + std::to_string(this->x) + ", " + std::to_string(this->y);
this->screen->get_debug_hud().add_debug_line(cam_pos);
this->screen->get_debug_hud().add_debug_line("Map width in pixel: " + std::to_string(this->map->get_width_in_pixels()) + ", " + std::to_string(this->map->get_height_in_pixels()));
Position world_mouse_pos = this->camera_to_world_position(mouse_pos.x, mouse_pos.y);
this->screen->get_debug_hud().add_debug_line("Mouse world position: " +
std::to_string(world_mouse_pos.x.toLong()) + ", " +
std::to_string(world_mouse_pos.y.toLong()));
this->screen->get_debug_hud().add_debug_line("Cell under the mouse: " +
std::to_string(mouse_col) + ":" +
std::to_string(mouse_row),
sf::Color::Yellow);
}
void Camera::draw_mouse_selection()
......@@ -361,69 +373,202 @@ void Camera::fixup_camera_position()
this->y = this->map->get_height_in_pixels() - win_size.y;
}
/**
* Values used to make an angular rotation
* See http://en.wikipedia.org/wiki/Rotation_%28mathematics%29#Matrix_algebra
* or your school lessons.
*/
// sin(7π/4)
static const float sin_45 = 0.6496369390800625;
// cos(7π/4)
static const float cos_45 = 0.7602445970756301;
sf::Vector2u Camera::world_to_camera_position(const Position& pos) const
{
sf::Vector2u res;
// Do some proportionality, to convert from the size of the world to the
// size of the screen
// res.x = (pos.x.toLong() * TILE_WIDTH) / CELL_SIZE;
// res.y = (pos.y.toLong() * TILE_HEIGHT) / CELL_SIZE;
res.x = pos.x.toLong();
res.y = pos.y.toLong();
// Then a rotation of a 45° angle
log_error("Before rotate: " << res.x << ": " << res.y);
res.x = res.x * cos_45 - res.y * sin_45;
res.y = res.y * sin_45 + res.y * cos_45;
log_error("After rotate: " << res.x << ": " << res.y);
// A translation to the right, because the world and the screen do not have
// the same origin
static const unsigned int w = 96;
static const unsigned int y = 72;
res.x = (res.x * w) / CELL_SIZE;
res.y = (res.y * y) / CELL_SIZE;
// Then adjust y using the height of that position in the world
Fix16 height = this->world->get_position_height(pos) * 32;
res.y -= height.toLong();
Fix16 height = this->world->get_position_height(pos) * 24;
return res;
}
Position Camera::camera_to_world_position(const int x,
const int y) const
CellIndex Camera::get_cell_at_coord(const unsigned int x, const unsigned int y) const
{
Position res;
const uint cell_size = static_cast<const uint>(CELL_SIZE);
res.x = (x + this->x) * (CELL_SIZE / static_cast<const float>(TILE_WIDTH));
res.y = (y + this->y) * (CELL_SIZE / static_cast<const float>(TILE_HEIGHT));
uint offset = (cell_size - (res.y.toLong() % cell_size));
uint i = 0;
assert(offset <= 32767);
Fix16 height_of_bottom_cell = this->world->get_position_height(Position(res.x, res.y + static_cast<short>(offset)));
if (height_of_bottom_cell > ((offset) * (1.f/LAYER_HEIGHT)))
res.y += (height_of_bottom_cell * LAYER_HEIGHT).toLong();
const Position pos(x * CELL_SIZE / TILE_WIDTH,
y * CELL_SIZE / TILE_HEIGHT);
const Cell cell = this->world->get_cell_at_position(pos);
return this->map->cell_to_index(cell);
}
float get_edge_height(const float x, const float left_height, const float right_height)
{
if (left_height == right_height)
return left_height;
const float ratio = x / HALF_TILE_W;
if (left_height > right_height)
return left_height - (ratio * (left_height - right_height));
else
return left_height + (ratio * (right_height - left_height));
}
Position Camera::camera_to_world_position(const int x, const int y) const
{
// The empty space at the top of the world, due to tile size. In pixels
static const unsigned int top_offset = 56;
if (x + this->x <= 0 ||
y + this->y - top_offset <= 0)
return Position::zero;
static const float cell_size = static_cast<const float>(CELL_SIZE);
// Get the cell (at the floor level) at the position
const CellIndex cell_index = this->get_cell_at_coord(x + this->x,
y + this->y - top_offset);
// DEBUG only
selected_cell = cell_index;
if (UINT_MAX == cell_index)
return Position::zero;
unsigned int col;
unsigned int row;
std::tie(col, row) = this->map->index_to_cell(cell_index);
// If the world was completely flat, this would be the result position
Position res;
res.x = (x + this->x) * (cell_size / TILE_WIDTH);
res.y = (y + this->y - top_offset) * (cell_size / TILE_HEIGHT);
// Now we need to check if, due to the levels of the world, this position
// is hidden or not by a surrounding cell. If yes, the position is
// actually on this other cell.
// If we are on the left side of the cell, we may be hidden by the bottom
// or the left cells. If we are on the right side, the bottom or the right
// cells.
unsigned int xx = x + this->x;
if (row % 2)
xx += HALF_TILE_W;
// TODO this in a (World::?) function
TileHeights heights = this->map->get_cell_heights(col, row);
this->screen->get_debug_hud().add_debug_line("Current cell corner heights: " +
std::to_string(heights.corners.left) + ", " +
std::to_string(heights.corners.top) + ", " +
std::to_string(heights.corners.right) + ", " +
std::to_string(heights.corners.bottom));
const Fix16 cell_size_16 = static_cast<fix16_t>(CELL_SIZE);
// Relative x and y position on the base square of the grid-aligned tile
unsigned int rel_x = (x + this->x) - (TILE_WIDTH * col);
unsigned int rel_y = (y + this->y - top_offset) - ((TILE_HEIGHT / 2) * row);
if (row % 2)
rel_x -= HALF_TILE_W;
float adj_rel_x;
float edge_height;
float bot_edge_height;
// TODO fix the height. It should not be (bot + top)/2
float height = 0;
CellIndex adj_cell;
if (xx % TILE_WIDTH >= HALF_TILE_W)
{
offset += cell_size;
height_of_bottom_cell = this->world->get_position_height(Position(res.x, res.y + static_cast<short>(offset)));
if (height_of_bottom_cell > (offset * (1.f/LAYER_HEIGHT)))
res.y += (height_of_bottom_cell * LAYER_HEIGHT).toLong();
// RIGHT
adj_rel_x = rel_x - HALF_TILE_W;
edge_height = ::get_edge_height(adj_rel_x,
heights.corners.top, heights.corners.right);
bot_edge_height = ::get_edge_height(adj_rel_x,
heights.corners.bottom, heights.corners.right);
height = (edge_height + bot_edge_height) / 2;
// Get the bottom-right cell (adj_cell) and its heights
if (row % 2 == 0)
adj_cell = col + (row + 1) * this->map->get_width_in_tiles();
else
adj_cell = col + 1 + (row + 1) * this->map->get_width_in_tiles();
heights = this->map->get_cell_heights(adj_cell);
edge_height = ::get_edge_height(adj_rel_x,
heights.corners.left, heights.corners.top);
bot_edge_height = ::get_edge_height(adj_rel_x,
heights.corners.left, heights.corners.bottom);
if ((edge_height * LAYER_HEIGHT) + adj_rel_x * TILE_HW_RATIO > (TILE_HEIGHT - rel_y))
{
selected_cell = adj_cell;
height = (edge_height + bot_edge_height) / 2;
}
// Now do the “same” thing with the cell at the bottom
adj_cell = col + (row + 2) * this->map->get_width_in_tiles();
heights = this->map->get_cell_heights(adj_cell);
edge_height = ::get_edge_height(adj_rel_x,
heights.corners.top, heights.corners.right);
bot_edge_height = ::get_edge_height(adj_rel_x,
heights.corners.bottom, heights.corners.right);
if ((edge_height * LAYER_HEIGHT) - adj_rel_x * TILE_HW_RATIO > (TILE_HEIGHT - rel_y))
{
selected_cell = adj_cell;
height = (edge_height + bot_edge_height) / 2;
}
}
else
{
// LEFT
adj_rel_x = static_cast<float>(rel_x);
edge_height = ::get_edge_height(adj_rel_x,
heights.corners.left, heights.corners.top);
bot_edge_height = ::get_edge_height(adj_rel_x,
heights.corners.left, heights.corners.bottom);
height = (edge_height + bot_edge_height) / 2;
if (row % 2 == 0)
adj_cell = col - 1 + (row + 1) * this->map->get_width_in_tiles();
else
adj_cell = col + (row + 1) * this->map->get_width_in_tiles();
heights = this->map->get_cell_heights(adj_cell);
edge_height = ::get_edge_height(adj_rel_x,
heights.corners.top, heights.corners.right);
bot_edge_height = ::get_edge_height(adj_rel_x,
heights.corners.bottom, heights.corners.right);
if (edge_height * LAYER_HEIGHT + (HALF_TILE_W - adj_rel_x) * TILE_HW_RATIO > TILE_HEIGHT - rel_y)
{
selected_cell = adj_cell;
height = (edge_height + bot_edge_height) / 2;
}
// Now do the “same” thing with the cell at the bottom
adj_cell = col + (row + 2) * this->map->get_width_in_tiles();
heights = this->map->get_cell_heights(adj_cell);
edge_height = ::get_edge_height(adj_rel_x,
heights.corners.left, heights.corners.top);
bot_edge_height = ::get_edge_height(adj_rel_x,
heights.corners.left, heights.corners.bottom);
if ((edge_height * LAYER_HEIGHT) - (HALF_TILE_W - adj_rel_x) * TILE_HW_RATIO > (TILE_HEIGHT - rel_y))
{
Fix16 height_of_current_cell = this->world->get_position_height(Position(res.x, res.y));
res.y += (height_of_current_cell * LAYER_HEIGHT).toLong();
selected_cell = adj_cell;
height = (edge_height + bot_edge_height) / 2;
}
}
res.y += height / TILE_HW_RATIO * static_cast<float>(LAYER_HEIGHT);
this->screen->get_debug_hud().add_debug_line("World position: " + std::to_string(res.x.toLong()) + ":" + std::to_string(res.y.toLong()));
return res;
}
......@@ -455,9 +600,7 @@ const sf::Vector2u Camera::get_win_size() const
void Camera::graphical_tick()
{
for (Sprite*& sprite: this->sprites)
{
sprite->tick();
}
sprite->tick();
}
const sf::Vector2i Camera::get_mouse_position() const
......
......@@ -67,6 +67,11 @@ public:
void update(const Duration& dt);
sf::Vector2u world_to_camera_position(const Position&) const;
Position camera_to_world_position(const int, const int) const;
/**
* Return the cell at the given position. If the position is not contained
* by any cell, return UINT_MAX.
*/
CellIndex get_cell_at_coord(const unsigned int x, const unsigned int y) const;
/**
* Start a mouse selection, i.e. drawing a rectangle to select entities
* inside it. The position is a world-static position, not a camera
......@@ -113,6 +118,7 @@ public:
private:
Camera(const Camera&);
Camera& operator=(const Camera&);
/**
* Check if the camera is at a valid position. If not, fix it.
*/
......@@ -143,6 +149,16 @@ private:
MouseSelection mouse_selection;
Screen* screen;
std::list<Sprite*> sprites;
};
// debug
static CellIndex selected_cell;
/**
* Given a relative (to the cell) x and two corners heights, returns the
* height of the cell at the x position
*/
float get_edge_height();
#endif // __CAMERA__HPP__
......@@ -146,11 +146,11 @@ void GraphMap::draw_full_map(sf::RenderTarget& target)
continue ;
uint yoffset = level++ * LEVEL_HEIGHT;
for (uint y = 0;
y < this->theight;
y < this->height_in_tiles;
y++)
{
for (uint x = 0;
x < this->twidth;
x < this->width_in_tiles;
x++)
{
const uint gid = layer->cells[this->width * y + x];
......
......@@ -31,6 +31,7 @@ void Screen::draw()
this->camera.draw();
this->hud.draw(&this->camera);
this->draw_mouse_cursor();
this->debug_hud.draw(&this->camera);
}
void Screen::update(const Duration& dt)
......@@ -60,6 +61,9 @@ void Screen::set_cursor_type(const cursor::type type)
void Screen::draw_mouse_cursor()
{
const sf::Vector2i pos = sf::Mouse::getPosition(*this->win);
this->debug_hud.add_debug_line("Cursor position: " +
std::to_string(pos.x) + ", " +
std::to_string(pos.y));
if (this->on_left_click.cursor_callback != 0)
this->set_cursor_type(this->on_left_click.cursor_callback(static_cast<uint>(pos.x),
static_cast<uint>(pos.y),
......@@ -88,9 +92,9 @@ cursor::type Screen::draw_build_cursor(const uint x, const uint y, const std::si
log_error("id: " << id);
sf::Sprite sprite;
sprite.setTexture(*(this->building_textures[id]));
short xa, ya;
Position world_pos = this->camera.camera_to_world_position(x, y);
this->world->get_cell_at_position(world_pos, xa, ya);
short xa, ya;
std::tie(xa, ya) = this->world->get_cell_at_position(world_pos);
Fix16 height = this->world->get_position_height(world_pos);
float xpos = xa * TILE_WIDTH - this->camera.x;
float ypos = ya * TILE_HEIGHT - this->camera.y - static_cast<int>(height) * LAYER_HEIGHT - 32;
......@@ -108,3 +112,8 @@ cursor::type Screen::draw_move_cursor(const uint, const uint, const std::size_t)
{
return cursor::Move;
}
DebugHud& Screen::get_debug_hud()
{
return this->debug_hud;
}
......@@ -20,6 +20,7 @@
#include <gui/camera/camera.hpp>
#include <gui/hud/hud.hpp>
#include <gui/hud/debug_hud.hpp>
#include <world/client_world/client_world.hpp>
#include <gui/cursor.hpp>
#include <mod/client_mod.hpp>
......@@ -50,6 +51,7 @@ public:
return world;
}
void reset_left_click_action();
DebugHud& get_debug_hud();
private:
Screen(const Screen&);
......@@ -58,6 +60,8 @@ private:
Camera camera;
ClientWorld* world;
Hud hud;
DebugHud debug_hud;
/**
* The list of the cursors' images. We just draw them at the mouse
* position. The texture is changed whenever needed (for example when
......
......@@ -20,6 +20,7 @@ void PicpicSprite::draw(Camera* camera, const ClientWorld* world, const Screen*
{
this->draw_shadow(camera, world, screen);
const sf::Vector2u entpos = camera->world_to_camera_position(this->unit->pos);
const uint x = entpos.x - camera->x;
const uint y = entpos.y - camera->y;
......
......@@ -19,7 +19,8 @@ void UnitSprite::draw_shadow(Camera* camera, const ClientWorld* world, const Scr
const sf::Vector2u size = UnitSprite::shadow_texture.getSize();
sf::Sprite sprite(UnitSprite::shadow_texture);
sprite.setPosition(x - size.x/2, y - size.y/2);
const sf::Color color(255, 255, 255, 8);
const sf::Color color(255, 255, 255, 122);
sprite.setColor(color);
camera->draw(sprite);
}
......
......@@ -77,7 +77,7 @@ void CommandHandler::read_handler(const boost::system::error_code& error, const
c[bytes_transferred] = 0;
// find the . separator
size_t pos = 0;
std::size_t pos = 0;
while (c[pos] && c[pos] != '.')
pos++;
......
......@@ -161,7 +161,7 @@ bool ClientWorld::action_build(const unsigned int x, const unsigned y, const std
BuildEvent event;
event.actor = this->current_selection.get_entities().front()->id;
Position pos(x, y);
this->get_cell_at_position(pos, event.x, event.y);
std::tie(event.x, event.y) = this->get_cell_at_position(pos);
event.type_id = id;
this->generate_command("BUILD", event.to_string());
return true;
......
......@@ -26,7 +26,7 @@ Map::~Map()
std::vector<Layer*>::iterator it;
for (it = this->layers.begin(); it < this->layers.end(); ++it)
delete *it;
if (walking_map != 0)
if (walking_map)
delete[] walking_map;
}
......@@ -48,14 +48,15 @@ bool Map::load_from_file(const std::string& map_name)
const unsigned int tile_height = tree.get<unsigned int>("map.<xmlattr>.tileheight", 0);
const unsigned int tile_width = tree.get<unsigned int>("map.<xmlattr>.tilewidth", 0);
log_debug("Tile size: " << tile_width << ", " << tile_height);
if ((tile_width != TILE_WIDTH) || (tile_height != TILE_HEIGHT))
{
log_error("Map has a wrong tile size: " << tile_width << ":" << tile_height);
return false;
}
this->theight = tree.get<unsigned int>
this->height_in_tiles = tree.get<unsigned int>
("map.<xmlattr>.height", 0);
this->twidth = tree.get<unsigned int>
this->width_in_tiles = tree.get<unsigned int>
("map.<xmlattr>.width", 0);
BOOST_FOREACH(boost::property_tree::ptree::value_type &v,
tree.get_child("map"))
......@@ -66,6 +67,8 @@ bool Map::load_from_file(const std::string& map_name)
return false;
}
}
log_debug("Map size in px " << this->width << ", " << this->height);
log_debug("Map size, in tiles: " << this->width_in_tiles << ", " << this->height_in_tiles);
this->generate_walking_map();
return true;
}
......@@ -90,7 +93,7 @@ bool Map::read_layer(boost::property_tree::ptree& tree)
Layer* layer = this->layers[level];
layer->set_level(level);
layer->set_size(layer_width, layer_height);
log_error(layer_width);
if ((layer_width * TILE_WIDTH) >= this->width)
{
this->width = layer_width * TILE_WIDTH;
......@@ -98,7 +101,8 @@ bool Map::read_layer(boost::property_tree::ptree& tree)
}
if ((layer_height * TILE_HEIGHT) >= this->height)
{
this->height = layer_height * TILE_HEIGHT;
// Divide by two because of the tile shifting each two rows
this->height = layer_height * TILE_HEIGHT / 2;
this->height_in_tiles = layer_height;
}
std::string data;
......@@ -204,24 +208,33 @@ uint Map::get_width_in_tiles() const
void Map::generate_walking_map()
{
const uint map_size = this->get_width_in_tiles() * this->get_height_in_tiles();
this->walking_map = new ushort[map_size];
this->walking_map = new TileHeights[map_size];
int level;
for (uint i = 0; i < map_size; ++i)
{
level = this->get_max_level_for_cell(i);
if (level == -1)
{ // in case of a hole, we put a height of 15 in the four corners.
// 15 means it's not walkable at all.
this->walking_map[i] = ~0;
{ // in case of a hole, we put a height of 7 in the four corners.
// 7 means it's not walkable at all.