Commit 0c3a673f authored by louiz’'s avatar louiz’

Rewrite the drawing of the Camera sprites

Everything (entity sprites, effects, decoration, path sprites etc) is drawn
in an elegant way, and in the correct order.
parent 01ecb83e
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <gui/camera/camera.hpp> #include <gui/camera/camera.hpp>
#include <gui/screen/screen.hpp> #include <gui/screen/screen.hpp>
#include <gui/sprites/pic_sprite.hpp> #include <gui/sprites/pic_sprite.hpp>
#include <gui/sprites/orange_blob.hpp>
#include <gui/sprites/tourbillon_sprite.hpp> #include <gui/sprites/tourbillon_sprite.hpp>
#include <gui/sprites/bullet_sprite.hpp> #include <gui/sprites/bullet_sprite.hpp>
#include <gui/sprites/emp_sprite.hpp> #include <gui/sprites/emp_sprite.hpp>
...@@ -17,10 +18,17 @@ ...@@ -17,10 +18,17 @@
#include <climits> #include <climits>
#include <cstdlib> #include <cstdlib>
#include <gui/effects/explosion.hpp> #include <gui/effects/explosion.hpp>
#include <gui/camera/mobility_sprites.hpp>
#include <world/works/path_work.hpp>
#include <gui/sprites/decoration_sprite.hpp>
#include <world/abilities.hpp> #include <world/abilities.hpp>
#include <world/location.hpp> #include <world/location.hpp>
#include <world/health.hpp>
#include <algorithm>
#include <iostream> #include <iostream>
using namespace std::string_literals; using namespace std::string_literals;
...@@ -40,6 +48,22 @@ Camera::Camera(GameClient* game, Screen* screen): ...@@ -40,6 +48,22 @@ Camera::Camera(GameClient* game, Screen* screen):
fog(1920, 1080, this, &this->world()) fog(1920, 1080, this, &this->world())
{ {
this->tileset.load_from_file("test6.tmx"); this->tileset.load_from_file("test6.tmx");
if (!ParticleEffect::texture.loadFromFile(AssetsManager::full_name("images/particles.png")))
{
log_debug("Failed to load particle.png");
exit(1);
}
this->decoration_sprites.push_back(std::make_unique<DecorationSprite<49>>(Position{800, 800},
AssetsManager::full_name("images/test/tree.png"),
sf::Vector2<int>{256, 400}, 35ms));
this->decoration_sprites.push_back(std::make_unique<DecorationSprite<49>>(Position{900, 850},
AssetsManager::full_name("images/test/tree.png"),
sf::Vector2<int>{256, 400}, 35ms));
MobilitySprites::init();
OrangeBlob::init();
} }
Camera::~Camera() Camera::~Camera()
...@@ -264,18 +288,19 @@ void Camera::update(const utils::Duration& dt) ...@@ -264,18 +288,19 @@ void Camera::update(const utils::Duration& dt)
} }
this->fixup_camera_position(); this->fixup_camera_position();
} }
else for (auto& effect: this->effects)
{ effect->update(dt);
const Entity* entity = this->get_game_client()->get_selection().get_entities()[0]; for (auto& sprite: this->sprites)
Location* location = entity->get<Location>(); sprite->update(dt);
if (!location) for (auto& sprite: this->decoration_sprites)
return; sprite->update(dt);
auto coord = this->world_to_camera_position(location->position());
log_debug("centering on position: " << coord.x << ":" << coord.y); this->effects.erase(std::remove_if(this->effects.begin(), this->effects.end(),
this->center(coord); [](const std::unique_ptr<Effect>& effect)
log_debug("Camera position: " << this->x << ": " << this->y); {
} return effect->current_life() <= 0s;
log_debug("Camera position after fixup: " << this->x << ": " << this->y); }),
this->effects.end());
} }
void Camera::center(const sf::Vector2i& center) void Camera::center(const sf::Vector2i& center)
...@@ -296,85 +321,152 @@ void Camera::center(const sf::Vector2i& center) ...@@ -296,85 +321,152 @@ void Camera::center(const sf::Vector2i& center)
this->y = 0; this->y = 0;
} }
bool Camera::tile_before_sprite(const CellIndex cell, const WorldSprite* sprite,
const Cell sprite_cell) const
{
unsigned short x, y;
std::tie(x, y) = this->map().index_to_cell(cell);
unsigned short sprite_x, sprite_y;
std::tie(sprite_x, sprite_y) = sprite_cell;
CellIndex sprite_cell_index = this->map().cell_to_index(std::make_pair(sprite_x, sprite_y));
// Same tile
if (sprite_cell_index == cell)
return true;
if (sprite_y > y)
return true;
return this->map().is_cell_in_neighbour_lower_cells(sprite_cell_index, cell);
void Camera::draw() void Camera::draw()
{ {
const sf::Vector2i mouse_pos = this->get_mouse_position(); const sf::Vector2i mouse_pos = this->get_mouse_position();
const Entity* entity_under_mouse = this->get_entity_under_mouse(); const Entity* entity_under_mouse = this->get_entity_under_mouse();
// Sort the sprites by their vertical position std::vector<const WorldSprite*> sorted_sprites;
this->sprites.sort([](const auto& a, const auto& b) sorted_sprites.reserve(this->sprites.size() +
{ this->effects.size() +
return a->get_world_pos().y < b->get_world_pos().y; this->decoration_sprites.size());
});
// Draw all tiles even the ones that are not visible for the camera. for (auto& sprite: this->sprites)
GraphTile* tile;
// Iterate all the rows, from top to bottom
for (unsigned short row = 0; row < this->map().get_height_in_tiles(); row++)
{ {
// Position where we will draw, in pixels if (this->world().can_be_seen_by_team(sprite->get_world_pos(),
unsigned int y = row * (TILE_HEIGHT / 2); this->game->get_self_team()))
unsigned int x; sorted_sprites.push_back(sprite.get());
// Draw the current row of all layers, from bottom to top }
for (Layer* layer: this->map().layers) for (auto& effect: this->effects)
{ sorted_sprites.push_back(effect.get());
if (layer && layer->cells) for (auto& sprite: this->decoration_sprites)
for (unsigned short col = 0; col < this->map().get_width_in_tiles(); col++) sorted_sprites.push_back(sprite.get());
std::vector<std::unique_ptr<SimpleWorldSprite>> flag_sprites;
for (const std::shared_ptr<Entity>& entity: this->world().entities)
{
const PathWork* path_work = entity->get_current_work<PathWork>();
if (!path_work)
continue;
const PathTask* path_task = path_work->get_task<PathTask>();
if (!path_task)
continue;
const Path& path = path_task->get_path();
MobilitySprites::insert_flag_sprites(path, flag_sprites);
}
for (const std::unique_ptr<SimpleWorldSprite>& flag_sprite: flag_sprites)
sorted_sprites.push_back(flag_sprite.get());
std::sort(sorted_sprites.begin(),
sorted_sprites.end(),
[](const WorldSprite* a, const WorldSprite* b)
{
return a->get_world_pos().y < b->get_world_pos().y;
});
std::vector<Cell> sprite_cells;
sprite_cells.reserve(this->sprites.size() + this->effects.size());
for (const auto& sprite: sorted_sprites)
sprite_cells.push_back(this->world().get_cell_at_position(sprite->get_world_pos()));
std::vector<std::vector<CellIndex>> tiles_before_sprite;
// We have one more vector, in which we put cells that are to be drawn
// after all the tiles
tiles_before_sprite.resize(sorted_sprites.size() + 1);
unsigned short start_x = std::max(0, static_cast<int>((this->x / TILE_WIDTH)) - 1);
unsigned short start_y = std::max(0, static_cast<int>(this->y / (TILE_HEIGHT / 2)) - 3);
for (auto y = start_y; y < start_y + 30; y++)
for (auto x = start_x; x < start_x + 25; x++)
{
CellIndex cell = this->map().cell_to_index(std::make_pair(x, y));
for (std::size_t i = 0; i < sorted_sprites.size(); i++)
{
const WorldSprite* sprite = sorted_sprites[i];
const Cell sprite_cell = sprite_cells[i];
if (this->tile_before_sprite(cell, sprite, sprite_cell))
{ {
x = col * TILE_WIDTH; tiles_before_sprite[i].push_back(cell);
if (row % 2 != 0) goto break_outerloop;
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->tileset.tiles[gid].get();
if (tile)
{
constexpr char tile_opacity = 180;
tile->sprite.setColor(sf::Color(255, 255, 255, tile_opacity));
if (std::find(this->world().current_path.begin(), this->world().current_path.end(),
this->map().cell_to_index(std::make_tuple(col, row)))
!= this->world().current_path.end())
tile->sprite.setColor(sf::Color(0, 120, 255, 255));
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 // We con only get here if the cell was added in no vector at all
y -= LEVEL_HEIGHT; tiles_before_sprite[tiles_before_sprite.size()-1].push_back(cell);
} break_outerloop:
continue;
}
// Display all the sprites that are on this row for (std::size_t i = 0; i < tiles_before_sprite.size(); ++i)
for (const auto& sprite: this->sprites) {
for (const CellIndex index: tiles_before_sprite[i])
{ {
Position sprite_world_position = sprite->get_world_pos(); unsigned short col;
unsigned short x_cell; unsigned short row;
unsigned short y_cell; std::tie(col, row) = this->map().index_to_cell(index);
std::tie(x_cell, y_cell) = this->world().get_cell_at_position(sprite_world_position); auto x = col * TILE_WIDTH;
if (y_cell == row && if (row % 2 != 0)
this->world().can_be_seen_by_team(sprite_world_position, this->game->get_self_team())) x += HALF_TILE_W;
auto y = row * (TILE_HEIGHT / 2);
for (Layer* layer: this->map().layers)
{ {
const Entity* entity = sprite->get_entity(); if (layer && layer->cells)
const auto entpos = this->world_to_camera_position(sprite_world_position); {
std::size_t gid = layer->cells[index];
if (entity->is_manipulable() && GraphTile* tile = this->tileset.tiles[gid].get();
(this->mouse_selection.contains(this->get_mouse_position(), entpos))) if (tile)
this->draw_hover_indicator(entpos, 80); {
if (std::find(this->world().current_path.begin(),
if (this->get_game_client()->is_entity_selected(sprite->get_entity())) this->world().current_path.end(),
this->draw_selected_indicator(entpos, 80); index) != this->world().current_path.end())
tile->sprite.setColor(sf::Color::Red);
// The sprites draw themselves around the 0:0 coordinates. We else
// just pass a RenderStates object to them, translating the tile->sprite.setColor(sf::Color::White);
// whole drawing to the correct camera position. This way the tile->sprite.setPosition(x - this->x, y - this->y);
// sprites class does not need to know anything about the this->win().draw(tile->sprite);
// world or camera coordinates. }
sf::Transform transform; }
transform.translate(entpos.x - this->x, y -= LEVEL_HEIGHT;
entpos.y - this->y); }
sprite->draw(this->win(), transform); }
// Draw the WorldSprite that is above all these tiles
if (i < sorted_sprites.size())
{ // Unless we were at the last queue of tiles, which have no tile above them
const WorldSprite* sprite = sorted_sprites[i];
auto entity_sprite = dynamic_cast<const EntitySprite*>(sprite);
const auto camera_pos = this->world_to_camera_position(sprite->get_world_pos());
sf::Transform transform;
if (entity_sprite &&
entity_sprite->get_entity()->is_manipulable() &&
(this->mouse_selection.contains(this->get_mouse_position(), camera_pos)))
this->draw_hover_indicator(camera_pos, 80);
if (entity_sprite &&
this->get_game_client()->is_entity_selected(entity_sprite->get_entity()))
this->draw_selected_indicator(camera_pos, 80);
transform.translate(camera_pos.x - this->x,
camera_pos.y - this->y);
sprite->draw(this->win(), transform);
if (entity_sprite)
{
auto entity = entity_sprite->get_entity();
auto health = entity->get<Health>(); auto health = entity->get<Health>();
auto team = entity->get<Team>(); auto team = entity->get<Team>();
...@@ -388,10 +480,10 @@ void Camera::draw() ...@@ -388,10 +480,10 @@ void Camera::draw()
} }
} }
} }
this->win().setView(this->win().getDefaultView());
} }
this->draw_mouse_selection(); this->draw_mouse_selection();
this->draw(this->fog.get_sprite());
// Debug // Debug
const std::string cam_pos = "Camera position: " + std::to_string(this->x) + ", " + std::to_string(this->y); const std::string cam_pos = "Camera position: " + std::to_string(this->x) + ", " + std::to_string(this->y);
...@@ -400,8 +492,8 @@ void Camera::draw() ...@@ -400,8 +492,8 @@ void Camera::draw()
Position world_mouse_pos = this->camera_to_world_position(mouse_pos.x, mouse_pos.y); Position world_mouse_pos = this->camera_to_world_position(mouse_pos.x, mouse_pos.y);
this->game->get_debug_hud().add_debug_line("Mouse world position: " + this->game->get_debug_hud().add_debug_line("Mouse world position: " +
std::to_string(world_mouse_pos.x.to_double()) + ", " + std::to_string(world_mouse_pos.x.to_double()) + ", " +
std::to_string(world_mouse_pos.y.to_double())); std::to_string(world_mouse_pos.y.to_double()));
if (entity_under_mouse) if (entity_under_mouse)
this->game->get_debug_hud().add_debug_line("Entity under mouse: " + std::to_string(entity_under_mouse->get_id())); this->game->get_debug_hud().add_debug_line("Entity under mouse: " + std::to_string(entity_under_mouse->get_id()));
...@@ -410,7 +502,8 @@ void Camera::draw() ...@@ -410,7 +502,8 @@ void Camera::draw()
this->game->get_debug_hud().add_debug_line("Current world time: "s + std::to_string(this->get_game_client()->current_world_time().count())); this->game->get_debug_hud().add_debug_line("Current world time: "s + std::to_string(this->get_game_client()->current_world_time().count()));
this->draw(this->fog.get_sprite()); Cell cell_under_mouse = this->world().get_cell_at_position(world_mouse_pos);
this->game->get_debug_hud().add_debug_line("Cell under mouse: "s + std::to_string(std::get<0>(cell_under_mouse)) + ":" + std::to_string(std::get<1>(cell_under_mouse)));
} }
void Camera::draw_mouse_selection() void Camera::draw_mouse_selection()
...@@ -811,7 +904,7 @@ void Camera::on_new_entity(const Entity* entity) ...@@ -811,7 +904,7 @@ void Camera::on_new_entity(const Entity* entity)
else if (entity->get_type() == 2) else if (entity->get_type() == 2)
this->sprites.push_back(std::make_unique<EmpSprite>(entity)); this->sprites.push_back(std::make_unique<EmpSprite>(entity));
else if (entity->get_type() == 4) else if (entity->get_type() == 4)
this->sprites.push_back(std::make_unique<TourbillonSprite>(entity)); this->sprites.push_back(std::make_unique<OrangeBlob>(entity));
} }
......
...@@ -37,6 +37,7 @@ class Entity; ...@@ -37,6 +37,7 @@ class Entity;
class EntitySprite; class EntitySprite;
class Task; class Task;
class Effect; class Effect;
class WorldSprite;
class Camera: public ScreenElement class Camera: public ScreenElement
{ {
...@@ -199,11 +200,10 @@ private: ...@@ -199,11 +200,10 @@ private:
* The position of the cursor when the user started dragging the camera. * The position of the cursor when the user started dragging the camera.
*/ */
sf::Vector2i start_drag_position; sf::Vector2i start_drag_position;
/**
* A pointer to the world object used to display stuff on the screen.
*/
GameClient* game; GameClient* game;
std::list<std::unique_ptr<EntitySprite>> sprites; std::list<std::unique_ptr<EntitySprite>> sprites;
std::list<std::unique_ptr<WorldSprite>> decoration_sprites;
MouseSelection mouse_selection; MouseSelection mouse_selection;
Tileset tileset; Tileset tileset;
Fog fog; Fog fog;
...@@ -212,6 +212,12 @@ private: ...@@ -212,6 +212,12 @@ private:
public: public:
std::vector<std::unique_ptr<Effect>> effects; std::vector<std::unique_ptr<Effect>> effects;
private:
/**
* Returns true if the tile for the given cell must be drawn before the
* sprite (and thus returns false if it must be drawn after)
*/
bool tile_before_sprite(const CellIndex cell, const WorldSprite* sprite, const Cell sprite_cell) const;
/** /**
* Various accessors * Various accessors
*/ */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment