camera.cpp 26 KB
Newer Older
1
#include <logging/logging.hpp>
2
#include <gui/camera/camera.hpp>
3
#include <gui/screen/screen.hpp>
4
#include <gui/sprites/pic_sprite.hpp>
louiz’'s avatar
louiz’ committed
5
#include <gui/sprites/tourbillon_sprite.hpp>
louiz’'s avatar
louiz’ committed
6
#include <gui/sprites/bullet_sprite.hpp>
louiz’'s avatar
louiz’ committed
7
#include <gui/sprites/emp_sprite.hpp>
8
#include <world/world.hpp>
9
#include <world/layer.hpp>
10
#include <world/entity.hpp>
louiz’'s avatar
louiz’ committed
11
#include <world/team.hpp>
12
#include <gui/utils.hpp>
louiz’'s avatar
louiz’ committed
13
#include <game/game_client.hpp>
14 15
#include <climits>
#include <cstdlib>
16

louiz’'s avatar
louiz’ committed
17
#include <world/abilities.hpp>
louiz’'s avatar
louiz’ committed
18 19
#include <world/location.hpp>

louiz’'s avatar
louiz’ committed
20 21
#include <iostream>

22 23
using namespace std::string_literals;

louiz’'s avatar
louiz’ committed
24 25
Camera::Camera(GameClient* game, Screen* screen):
  ScreenElement(screen),
26
  x(0),
27
  y(56),
28
  zoom(1),
29
  focused(true),
30
  movement_speed(1000),
31 32
  previous_position(0, 0),
  start_drag_position(0, 0),
louiz’'s avatar
louiz’ committed
33
  game(game),
louiz’'s avatar
louiz’ committed
34
  mouse_selection(),
35 36
  tileset(),
  fog(1920, 1080, this, &this->world())
37
{
louiz’'s avatar
louiz’ committed
38
  this->tileset.load_from_file("test6.tmx");
39 40 41 42 43 44
}

Camera::~Camera()
{
}

louiz’'s avatar
louiz’ committed
45 46 47 48 49
World& Camera::world() const
{
  return this->game->get_world();
}

louiz’'s avatar
louiz’ committed
50
Map& Camera::map() const
louiz’'s avatar
louiz’ committed
51
{
louiz’'s avatar
louiz’ committed
52
  return this->world().get_map();
louiz’'s avatar
louiz’ committed
53 54 55 56 57 58 59 60 61 62 63 64
}

sf::RenderWindow& Camera::win()
{
  return this->screen->window();
}

sf::RenderWindow& Camera::win() const
{
  return this->screen->window();
}

65
bool Camera::handle_event(const sf::Event& event)
66
{
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  if (event.type == sf::Event::LostFocus)
    this->focused = false;
  else if (event.type == sf::Event::GainedFocus)
    this->focused = true;
  else if (event.type == sf::Event::MouseButtonPressed)
    {
      switch (event.mouseButton.button)
        {
        case sf::Mouse::Left:
          this->handle_left_click(event);
          break;
        case sf::Mouse::Middle:
          this->handle_middle_click(event);
          break;
        case sf::Mouse::Right:
          this->handle_right_click(event);
          break;
        default:
          log_debug("Mouse button not implemented.");
        }
    }
  else if (event.type == sf::Event::MouseButtonReleased)
    {
      switch (event.mouseButton.button)
        {
        case sf::Mouse::Left:
          this->handle_left_release(event);
          break;
        case sf::Mouse::Middle:
          this->handle_middle_release(event);
          break;
        default:
          log_debug("Mouse button not implemented.");
        }
    }
  this->fixup_camera_position();
  if (this->mouse_selection.ongoing == true)
    return true;
  return false;
}

louiz’'s avatar
louiz’ committed
108
void Camera::handle_middle_click(const sf::Event&)
109 110 111 112 113
{
}

void Camera::handle_right_click(const sf::Event& event)
{
114
  const Position pos = this->screen_to_world_position(event.mouseButton.x,
115
                                                      event.mouseButton.y);
louiz’'s avatar
louiz’ committed
116 117
  bool queue = sf::Keyboard::isKeyPressed(sf::Keyboard::LShift);
  log_debug("queue: " << queue);
118

119
  // A right click when there's an action associated with the left click
120
  // resets the default action of the right click.
121
  if (this->screen->get_left_click().callback)
122
    this->screen->reset_left_click();
123 124

  // else // otherwise it always does the move action.
louiz’'s avatar
louiz’ committed
125 126 127 128
    {
      std::vector<EntityId> ids;
      for (const auto& entity: this->game->get_selection().get_entities())
        ids.push_back(entity->get_id());
129 130 131 132 133 134
      if (ids.empty())
        return ;
      const Entity* entity_under_mouse = this->get_entity_under_mouse();
      if (entity_under_mouse)
        this->game->action_follow(ids, entity_under_mouse->get_id(), queue);
      else
louiz’'s avatar
louiz’ committed
135 136
        this->game->action_move(ids, pos, queue);
    }
137 138 139 140
}

void Camera::handle_left_click(const sf::Event& event)
{
louiz’'s avatar
louiz’ committed
141
  // TODO do nothing if the click is not in the camera zone.
142
  if (!this->screen->get_left_click().callback)
louiz’'s avatar
louiz’ committed
143 144 145 146 147 148 149 150
    {
      const sf::Vector2i pos(event.mouseButton.x + this->x,
                             event.mouseButton.y + this->y);

      this->mouse_selection.start(pos);
    }
  else
    {
151
      const Position pos = this->screen_to_world_position(event.mouseButton.x,
louiz’'s avatar
louiz’ committed
152
                                                          event.mouseButton.y);
153 154
      if (this->screen->get_left_click().callback(pos) == true)
        this->screen->reset_left_click();
louiz’'s avatar
louiz’ committed
155
    }
156 157
}

louiz’'s avatar
louiz’ committed
158
void Camera::handle_left_release(const sf::Event&)
159 160 161 162 163 164
{
  if (sf::Keyboard::isKeyPressed(sf::Keyboard::RShift) ||
      sf::Keyboard::isKeyPressed(sf::Keyboard::LShift))
    this->add_mouse_selection_to_selection();
  else
    this->set_mouse_selection_to_selection();
165
  this->mouse_selection.end();
166 167 168 169
}

void Camera::set_mouse_selection_to_selection()
{
170 171
  sf::Vector2i mouse_pos = this->get_mouse_position();

louiz’'s avatar
louiz’ committed
172 173 174 175
  // Only the manipulable entities are considered
  std::vector<const Entity*> entities_in_mouse_selection;

  for (const auto& entity: this->world().entities)
176
    {
louiz’'s avatar
louiz’ committed
177 178 179 180 181 182 183 184 185
      Location* location = entity->get<Location>();
      Team* team = entity->get<Team>();
      if (!team || !location || !entity->is_manipulable())
        continue;
      if (this->mouse_selection.contains(mouse_pos,
                                         this->world_to_camera_position(location->position())))
        entities_in_mouse_selection.push_back(entity.get());
    }

186 187 188
  if (entities_in_mouse_selection.empty())
    return ;

louiz’'s avatar
louiz’ committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
  auto end_own_entities = std::partition(entities_in_mouse_selection.begin(),
                                         entities_in_mouse_selection.end(),
                                         [this](const Entity* entity) -> bool
                                         {
                                           Team* team = entity->get<Team>();
                                           if (team->get() == this->game->get_self_team())
                                             return true;
                                           return false;
                                         });
  if (end_own_entities == entities_in_mouse_selection.begin())
    { // 0 of the selected entities are the player's
      this->game->get_selection().assign(entities_in_mouse_selection.begin(),
                                         entities_in_mouse_selection.end());
    }
  else
    { // Only select entities owned by the player
      this->game->get_selection().assign(entities_in_mouse_selection.begin(),
                                         end_own_entities);
207 208 209 210 211
    }
}

void Camera::add_mouse_selection_to_selection()
{
212
  sf::Vector2i mouse_pos = this->get_mouse_position();
louiz’'s avatar
louiz’ committed
213 214

  for (const auto& entity: this->world().entities)
215
    {
louiz’'s avatar
louiz’ committed
216
      Location* location = entity->get<Location>();
louiz’'s avatar
louiz’ committed
217
      if (!location || !entity->is_manipulable())
louiz’'s avatar
louiz’ committed
218
        continue;
219
      if (this->mouse_selection.contains(mouse_pos,
220
                                         this->world_to_camera_position(location->position())))
221
        {
louiz’'s avatar
louiz’ committed
222 223
          if (this->game->is_entity_selected(entity.get()) == false)
            this->game->select_entity(entity.get());
224
        }
225 226 227 228
    }
  this->mouse_selection.end();
}

louiz’'s avatar
louiz’ committed
229
void Camera::handle_middle_release(const sf::Event&)
230
{
231 232
}

233
void Camera::update(const utils::Duration& dt)
234
{
235 236 237
  if (this->get_game_client()->get_selection().is_empty())
    {
      sf::Vector2i pos = this->screen->get_mouse_position();
238

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
      const sf::Vector2u win_size = this->get_win_size();
      if (this->focused)
        {
          if ((pos.x > 0) && (static_cast<uint>(pos.x) > (win_size.x - 2)))
            pos.x = (win_size.x - 2);
          if (pos.x < 1)
            pos.x = 1;
          if ((pos.y > 0) && (static_cast<uint>(pos.y) > (win_size.y - 2)))
            pos.y = (win_size.y - 2);
          if (pos.y < 1)
            pos.y = 1;
          // TODO
          // sf::Mouse::setPosition(pos, *this->win);
          if (pos.x < 20)
            this->x -= this->movement_speed * utils::sec(dt).count();
          if (pos.y < 20)
            this->y -= this->movement_speed * utils::sec(dt).count();
          if ((pos.y > 0) && static_cast<uint>(pos.y) > (win_size.y - 20))
            this->y += this->movement_speed * utils::sec(dt).count();
          if ((pos.x > 0) && static_cast<uint>(pos.x) > (win_size.x - 20))
            this->x += this->movement_speed * utils::sec(dt).count();
        }
      this->fixup_camera_position();
    }
  else
264
    {
265 266 267 268 269 270 271 272
      const Entity* entity = this->get_game_client()->get_selection().get_entities()[0];
      Location* location = entity->get<Location>();
      if (!location)
        return;
      auto coord = this->world_to_camera_position(location->position());
      log_debug("centering on position: " << coord.x << ":" << coord.y);
      this->center(coord);
      log_debug("Camera position: " << this->x << ": " << this->y);
273
    }
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  log_debug("Camera position after fixup: " << this->x << ": " << this->y);
}

void Camera::center(const sf::Vector2i& center)
{
  auto size = this->win().getSize();
  size.x /= 2;
  size.y /= 2;
  log_debug("width :"  << size.x);
  log_debug("height:"  << size.y);
  if (size.x < center.x)
    this->x = center.x - size.x;
  else
    this->x = 0;

  if (size.y < center.y)
    this->y = center.y - size.y;
  else
    this->y = 0;
293
}
louiz’'s avatar
louiz’ committed
294

louiz’'s avatar
louiz’ committed
295
void Camera::draw()
louiz’'s avatar
louiz’ committed
296
{
297
  const sf::Vector2i mouse_pos = this->get_mouse_position();
298
  const Entity* entity_under_mouse = this->get_entity_under_mouse();
299

louiz’'s avatar
louiz’ committed
300 301 302 303 304 305
  // Sort the sprites by their vertical position
  this->sprites.sort([](const auto& a, const auto& b)
                     {
                       return a->get_world_pos().y < b->get_world_pos().y;
                     });

306 307 308
  // Draw all tiles even the ones that are not visible for the camera.
  GraphTile* tile;
  // Iterate all the rows, from top to bottom
louiz’'s avatar
louiz’ committed
309
  for (unsigned short row = 0; row < this->map().get_height_in_tiles(); row++)
louiz’'s avatar
louiz’ committed
310
    {
311 312 313 314
      // 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
louiz’'s avatar
louiz’ committed
315
      for (Layer* layer: this->map().layers)
louiz’'s avatar
louiz’ committed
316
        {
317
          if (layer && layer->cells)
louiz’'s avatar
louiz’ committed
318
            for (unsigned short col = 0; col < this->map().get_width_in_tiles(); col++)
319 320 321 322
              {
                x = col * TILE_WIDTH;
                if (row % 2 != 0)
                  x += HALF_TILE_W;
louiz’'s avatar
louiz’ committed
323
                std::size_t cell = col + (row * this->map().get_height_in_tiles());
324
                std::size_t gid = layer->cells[cell];
louiz’'s avatar
louiz’ committed
325
                tile = this->tileset.tiles[gid].get();
326 327
                if (tile)
                  {
328
                    constexpr char tile_opacity = 180;
329
                    tile->sprite.setColor(sf::Color(255, 255, 255, tile_opacity));
louiz’'s avatar
louiz’ committed
330 331 332 333
                    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));
334 335

                    tile->sprite.setPosition(x - this->x, y - this->y);
louiz’'s avatar
louiz’ committed
336
                    this->win().draw(tile->sprite);
337 338 339 340 341
                  }
              }
          // The row for the next layer must be shifted from a few pixel to
          // the top
          y -= LEVEL_HEIGHT;
louiz’'s avatar
louiz’ committed
342
        }
louiz’'s avatar
louiz’ committed
343 344 345
      // Display all the sprites that are on this row
      for (const auto& sprite: this->sprites)
        {
346
          Position sprite_world_position = sprite->get_world_pos();
louiz’'s avatar
louiz’ committed
347 348
          unsigned short x;
          unsigned short y;
349 350 351
          std::tie(x, y) = this->world().get_cell_at_position(sprite_world_position);
          if (y == row &&
              this->world().can_be_seen_by_team(sprite_world_position, this->game->get_self_team()))
352
            {
louiz’'s avatar
louiz’ committed
353 354 355 356
              const Entity* entity = sprite->get_entity();
              Team* team = entity->get<Team>();

              if (entity->is_manipulable() &&
357 358 359
                  this->mouse_selection.contains(this->get_mouse_position(),
                                                 this->world_to_camera_position(sprite_world_position))
                  )
louiz’'s avatar
louiz’ committed
360 361 362 363 364 365
                {
                  this->draw_hover_indicator(
                      this->world_to_camera_position(sprite_world_position),
                      80);
                }

366 367 368 369
              if (this->get_game_client()->is_entity_selected(sprite->get_entity()))
                this->draw_selected_indicator(this->world_to_camera_position(sprite_world_position), 80);
              sprite->draw(this->game);
            }
louiz’'s avatar
louiz’ committed
370
        }
louiz’'s avatar
louiz’ committed
371
    }
372

373
  this->draw_mouse_selection();
374 375 376

  // Debug
  const std::string cam_pos = "Camera position: " + std::to_string(this->x) + ", " + std::to_string(this->y);
louiz’'s avatar
louiz’ committed
377 378
  this->game->get_debug_hud().add_debug_line(cam_pos);
  this->game->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()));
379 380

  Position world_mouse_pos = this->camera_to_world_position(mouse_pos.x, mouse_pos.y);
louiz’'s avatar
louiz’ committed
381
  this->game->get_debug_hud().add_debug_line("Mouse world position: " +
louiz’'s avatar
louiz’ committed
382 383
                                               std::to_string(world_mouse_pos.x.to_double()) + ", " +
                                               std::to_string(world_mouse_pos.y.to_double()));
384

385 386 387 388 389
  if (entity_under_mouse)
    this->game->get_debug_hud().add_debug_line("Entity under mouse: " + std::to_string(entity_under_mouse->get_id()));
  else
    this->game->get_debug_hud().add_debug_line("No entity under mouse");

390 391
  this->game->get_debug_hud().add_debug_line("Current world time: "s + std::to_string(this->get_game_client()->current_world_time().count()));

392
  this->draw(this->fog.get_sprite());
393 394 395 396 397 398
}

void Camera::draw_mouse_selection()
{
  if (this->mouse_selection.ongoing == false)
    return ;
399
  sf::Vector2i mouse_pos = this->get_mouse_position();
400 401 402
  sf::RectangleShape rect(sf::Vector2f(::abs(mouse_pos.x - this->mouse_selection.start_pos.x),
                                       ::abs(mouse_pos.y- this->mouse_selection.start_pos.y)));
  rect.setOutlineThickness(1);
403 404
  rect.setFillColor(sf::Color(13, 0, 152, 40));
  rect.setOutlineColor(sf::Color(13, 0, 152, 0x36));
405 406 407 408 409 410 411 412 413 414
  sf::Vector2f rect_pos;
  if (this->mouse_selection.start_pos.x < mouse_pos.x)
    rect_pos.x = this->mouse_selection.start_pos.x - this->x;
  else
    rect_pos.x = mouse_pos.x - this->x;
  if (this->mouse_selection.start_pos.y < mouse_pos.y)
    rect_pos.y = this->mouse_selection.start_pos.y - this->y;
  else
    rect_pos.y = mouse_pos.y - this->y;
  rect.setPosition(rect_pos);
louiz’'s avatar
louiz’ committed
415
  this->win().draw(rect);
416 417
}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
void Camera::draw_hover_indicator(const sf::Vector2i& center, const unsigned int width)
{
  const float radius = static_cast<float>(width) / 2.0;
  sf::CircleShape circle(radius, 6);
  circle.setOutlineColor(sf::Color::Red);
  circle.setFillColor({0, 0, 0, 0});
  circle.setOutlineThickness(2);
  circle.scale(1.0f, 0.666f);
  circle.setPosition(static_cast<float>(center.x) - radius - this->x,
                     static_cast<float>(center.y) - radius * 0.666f - this->y);
  this->draw(circle);
}

void Camera::draw_selected_indicator(const sf::Vector2i& center, const unsigned int width)
{
  const float radius = static_cast<float>(width) / 2.0;
  sf::CircleShape circle(radius);
  circle.setOutlineColor(sf::Color::Blue);
  circle.setFillColor({0, 0, 0, 0});
  circle.setOutlineThickness(2);
  circle.scale(1.0f, 0.666f);
  circle.setPosition(static_cast<float>(center.x) - radius - this->x,
                     static_cast<float>(center.y) - radius * 0.666f - this->y);
  this->draw(circle);
}

444 445
void Camera::fixup_camera_position()
{
louiz’'s avatar
louiz’ committed
446
  const sf::Vector2u win_size = this->win().getSize();
447 448
  if (this->x < 0)
    this->x = 0;
louiz’'s avatar
louiz’ committed
449 450
  else if (this->x > (this->map().get_width_in_pixels() - win_size.x))
    this->x = this->map().get_width_in_pixels() - win_size.x;
451 452
  if (this->y < 0)
    this->y = 0;
louiz’'s avatar
louiz’ committed
453 454
  else if (this->y > (this->map().get_height_in_pixels() - win_size.y))
    this->y = this->map().get_height_in_pixels() - win_size.y;
455 456
}

457 458 459 460 461 462 463 464
sf::Vector2i Camera::world_to_screen_position(const Position& pos) const
{
  auto res = this->world_to_camera_position(pos);
  res.x -= this->x;
  res.y -= this->y;
  return res;
}

465
sf::Vector2i Camera::world_to_camera_position(const Position& pos) const
466
{
467
  sf::Vector2i res;
468

louiz’'s avatar
louiz’ committed
469 470
  res.x = pos.x.to_int();
  res.y = pos.y.to_int();
471

louiz’'s avatar
louiz’ committed
472 473
  res.x = (res.x * TILE_WIDTH) / CELL_SIZE;
  res.y = (res.y * TILE_HEIGHT) / CELL_SIZE;
474 475

  // Then adjust y using the height of that position in the world
louiz’'s avatar
louiz’ committed
476
  Fix16 height = this->world().get_position_height(pos) * 24;
477

louiz’'s avatar
louiz’ committed
478 479
  res.y += TILE_TOP_OFFSET - height.to_int();

480 481 482
  return res;
}

483
CellIndex Camera::get_cell_at_coord(const unsigned int x, const unsigned int y) const
484
{
485 486 487
  const Position pos(x * CELL_SIZE / TILE_WIDTH,
                     y * CELL_SIZE / TILE_HEIGHT);

louiz’'s avatar
louiz’ committed
488 489
  const Cell cell = this->world().get_cell_at_position(pos);
  return this->map().cell_to_index(cell);
490 491 492 493 494 495 496 497 498 499
}

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));
500
  else
501 502 503
    return left_height + (ratio * (right_height - left_height));
}

504 505 506 507 508 509 510 511
sf::Vector2i Camera::get_mouse_position() const
{
  auto res = this->screen->get_mouse_position();
  res.x += this->x;
  res.y += this->y;
  return res;
}

512
Position Camera::camera_to_world_position(const int x, const int y) const
513 514 515 516 517
{
  return this->screen_to_world_position(x - this->x, y - this->y);
}

Position Camera::screen_to_world_position(const int x, const int y) const
518 519
{
  // The empty space at the top of the world, due to tile size. In pixels
louiz’'s avatar
louiz’ committed
520
  static const unsigned int top_offset = TILE_TOP_OFFSET;
521 522 523 524 525 526 527 528 529 530 531 532 533 534

  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;

louiz’'s avatar
louiz’ committed
535 536
  if (cell_index == InvalidCellIndex)
    return Position::invalid;
537 538 539

  unsigned int col;
  unsigned int row;
louiz’'s avatar
louiz’ committed
540
  std::tie(col, row) = this->map().index_to_cell(cell_index);
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

  // 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
louiz’'s avatar
louiz’ committed
559
  TileHeights heights = this->map().get_cell_heights(col, row);
560

louiz’'s avatar
louiz’ committed
561
  this->game->get_debug_hud().add_debug_line("Current cell corner heights: " +
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
                                               std::to_string(heights.corners.left) + ", " +
                                               std::to_string(heights.corners.top) + ", " +
                                               std::to_string(heights.corners.right) + ", " +
                                               std::to_string(heights.corners.bottom));

  // 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)
581
    {
582 583 584 585 586 587 588 589 590 591 592
      // 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)
louiz’'s avatar
louiz’ committed
593
        adj_cell = col + (row + 1) * this->map().get_width_in_tiles();
594
      else
louiz’'s avatar
louiz’ committed
595 596
        adj_cell = col + 1 + (row + 1) * this->map().get_width_in_tiles();
      heights = this->map().get_cell_heights(adj_cell);
597 598 599 600 601 602 603 604 605 606 607 608 609

      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
louiz’'s avatar
louiz’ committed
610 611
      adj_cell = col + (row + 2) * this->map().get_width_in_tiles();
      heights = this->map().get_cell_heights(adj_cell);
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635

      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)
louiz’'s avatar
louiz’ committed
636
          adj_cell = col - 1 + (row + 1) * this->map().get_width_in_tiles();
637
        else
louiz’'s avatar
louiz’ committed
638 639
          adj_cell = col + (row + 1) * this->map().get_width_in_tiles();
      heights = this->map().get_cell_heights(adj_cell);
640 641 642 643 644 645 646 647 648 649 650 651 652

      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
louiz’'s avatar
louiz’ committed
653 654
      adj_cell = col + (row + 2) * this->map().get_width_in_tiles();
      heights = this->map().get_cell_heights(adj_cell);
655 656 657 658 659 660 661

      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))
662
        {
663 664
          selected_cell = adj_cell;
          height = (edge_height + bot_edge_height) / 2;
665 666
        }
    }
667 668

  res.y += height / TILE_HW_RATIO * static_cast<float>(LAYER_HEIGHT);
louiz’'s avatar
louiz’ committed
669
  this->game->get_debug_hud().add_debug_line("World position: " + std::to_string(res.x.to_double()) + ":" + std::to_string(res.y.to_double()));
670

671 672 673 674 675 676
  return res;
}

bool Camera::is_mouse_selection_ongoing() const
{
  return this->mouse_selection.ongoing;
louiz’'s avatar
louiz’ committed
677
}
678

679 680 681 682 683 684 685 686 687 688 689 690
const Entity* Camera::get_entity_under_mouse() const
{
  auto mouse_pos = this->get_mouse_position();
  for (auto it = this->sprites.crbegin(); it != this->sprites.crend(); ++it)
    {
      EntitySprite* sprite = it->get();
      if (sprite->is_mouse_over(this) == true)
        return sprite->get_entity();
    }
  return nullptr;
}

691
void Camera::draw(const sf::Drawable& drawable, const sf::RenderStates& states)
692
{
693
  this->win().draw(drawable, states);
694 695
}

696 697 698 699 700 701 702 703 704 705 706 707 708
void Camera::draw_energy_bar(sf::Vector2f screen_position, const EnergyBar& bar_specs,
                             const std::size_t max_val, int current_val)
{
  sf::RectangleShape rect;
  rect.setSize(bar_specs.size);
  rect.setOutlineColor(sf::Color::Black);
  rect.setOutlineThickness(1);
  rect.setFillColor({25, 25, 25, 200});
  screen_position.x -= bar_specs.size.x/2;
  rect.setPosition(screen_position);

  this->draw(rect);

louiz’'s avatar
louiz’ committed
709
  rect.setOutlineThickness(1);
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
  float grad_width = bar_specs.size.x / (max_val / bar_specs.little_graduation);

  sf::Color color = mix(bar_specs.min_color, bar_specs.max_color,
                        static_cast<float>(current_val) / max_val);

  rect.setSize({grad_width, bar_specs.size.y});
  while (current_val > 0)
    {
      if (current_val >= bar_specs.little_graduation)
        rect.setFillColor(color);
      else
        rect.setFillColor(color * sf::Color(255, 255, 255, 100));
      rect.setPosition(screen_position);
      this->draw(rect);

      current_val -= bar_specs.little_graduation;
      screen_position.x += grad_width;
    }
}

louiz’'s avatar
louiz’ committed
730
void Camera::on_new_entity(const Entity* entity)
731
{
louiz’'s avatar
louiz’ committed
732 733 734 735
  if (entity->get_type() == 0)
    this->sprites.push_back(std::make_unique<PicpicSprite>(entity));
  else if (entity->get_type() == 1)
    this->sprites.push_back(std::make_unique<BulletSprite>(entity));
louiz’'s avatar
louiz’ committed
736 737
  else if (entity->get_type() == 2)
    this->sprites.push_back(std::make_unique<EmpSprite>(entity));
louiz’'s avatar
louiz’ committed
738 739 740
  else if (entity->get_type() == 4)
    this->sprites.push_back(std::make_unique<TourbillonSprite>(entity));

741 742
}

743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
void Camera::on_entity_deleted(const Entity* entity)
{
  // Look for an EntitySprite using this entity pointer, and remove it
  auto it = std::find_if(this->sprites.begin(),
                         this->sprites.end(),
                         [entity](const auto& s)
                         {
                           return s->get_entity() == entity;
                         });
  if (it != this->sprites.end())
    {
      log_debug("removing sprite.");
      this->sprites.erase(it);
    }
}

759 760
const sf::Vector2u Camera::get_win_size() const
{
louiz’'s avatar
louiz’ committed
761
  return this->win().getSize();
762
}
763 764 765

void Camera::graphical_tick()
{
766
  for (auto& sprite: this->sprites)
767
    sprite->tick();
768
  this->fog.invalidate();
769
}
770

771 772 773 774
const GameClient* Camera::get_game_client() const
{
  return this->game;
}