Commit a901f1ec authored by louiz’'s avatar louiz’

Implement the cast of abilities

With an example of Blink
parent b6db67cd
......@@ -53,6 +53,31 @@ void Game::move_callback(Message* message)
srl.turn());
}
void Game::cast_callback(Message* message)
{
log_debug("Game::cast_callback");
auto srl = message->parse_body_to_protobuf_object<ser::order::Cast>();
if (!srl.IsInitialized())
{
log_error("Invalid data received for cast: " << srl.InitializationErrorString());
return ;
}
std::vector<EntityId> ids;
for (const auto& id: srl.entity_id())
ids.push_back(id);
Position pos;
pos.x.raw() = srl.pos().x();
pos.y.raw() = srl.pos().y();
uint32_t type = srl.type();
this->turn_handler.insert_action(std::bind(&World::do_cast, &this->world, ids, pos,
static_cast<AbilityType>(type), srl.queue()),
srl.turn());
}
void Game::do_new_entity(const EntityType type, const Position& pos, const uint16_t team)
{
log_debug("Game::do_new_entity");
......
......@@ -21,6 +21,7 @@ public:
void new_entity_callback(Message* msg);
void move_callback(Message* msg);
void cast_callback(Message* msg);
virtual void do_new_entity(const EntityType type, const Position& pos,
const uint16_t team);
......
......@@ -29,6 +29,8 @@ GameClient::GameClient(const std::shared_ptr<Screen>& screen):
std::bind(&GameClient::turn_callback, this, std::placeholders::_1));
this->install_callback("MOVE",
std::bind(&Game::move_callback, this, std::placeholders::_1));
this->install_callback("CAST",
std::bind(&Game::cast_callback, this, std::placeholders::_1));
this->screen->add_element(&this->camera, 0);
this->screen->add_element(&this->hud, 1);
......@@ -226,10 +228,11 @@ void GameClient::path_callback(Message* message)
{
}
bool GameClient::action_move(std::vector<EntityId> ids, const Position& pos, const bool queue)
bool GameClient::action_move(const std::vector<EntityId>& ids, const Position& pos, const bool queue)
{
ser::request::Move srl;
srl.set_queue(queue);
if (queue)
srl.set_queue(queue);
srl.mutable_pos()->set_x(pos.x.raw());
srl.mutable_pos()->set_y(pos.y.raw());
for (const EntityId id: ids)
......@@ -238,6 +241,21 @@ bool GameClient::action_move(std::vector<EntityId> ids, const Position& pos, con
return true;
}
bool GameClient::action_cast(const std::vector<EntityId>& ids, const Position& pos,
const AbilityType& type, const bool queue)
{
ser::request::Cast srl;
if (queue)
srl.set_queue(queue);
srl.mutable_pos()->set_x(pos.x.raw());
srl.mutable_pos()->set_y(pos.y.raw());
for (const EntityId id: ids)
srl.add_entity_id(id);
srl.set_type(static_cast<uint32_t>(type));
this->send_message("CAST", srl);
return true;
}
void GameClient::select_entity(const Entity* entity)
{
this->current_selection.add_to_selection(entity);
......
......@@ -78,10 +78,21 @@ public:
void graphical_tick();
/**
* Give the order to all selected and movable units to move to the given
* world coordinates.
* Send, to the server, a request to move the given entities to the given
* destination. Queue indicates whether or not this work should be queued.
*/
bool action_move(const std::vector<EntityId>& ids, const Position& pos,
const bool queue);
/**
* Send, to the server, a request to make the list of entities cast the
* given spell. There are three versions, each for each type of possible
* target (Position, Entity, nothing).
*/
bool action_move(std::vector<EntityId> ids, const Position& pos,
bool action_cast(const std::vector<EntityId>& ids, const Position& pos,
const AbilityType& type, const bool queue);
bool action_cast(const std::vector<EntityId>& ids, const EntityId target,
const AbilityType& type, const bool queue);
bool action_cast(const std::vector<EntityId>& ids, const AbilityType& type,
const bool queue);
/**
* Give the order to all selected and movable units to move to the given
......
......@@ -205,11 +205,49 @@ void GameServer::on_move_request(Message* message)
for (const auto& id: srl.entity_id())
ids.push_back(id);
Position pos;
pos.x.raw() = srl.mutable_pos()->x();
pos.y.raw() = srl.mutable_pos()->y();
pos.x.raw() = srl.pos().x();
pos.y.raw() = srl.pos().y();
this->send_move_order(ids, pos, srl.queue());
}
void GameServer::on_cast_request(Message* message)
{
auto srl = message->parse_body_to_protobuf_object<ser::request::Cast>();
if (!srl.IsInitialized())
{
log_error("Invalid data received for Cast request: " << srl.InitializationErrorString());
return ;
}
std::vector<EntityId> ids;
for (const auto& id: srl.entity_id())
ids.push_back(id);
if (srl.has_pos())
{
Position pos;
pos.x.raw() = srl.pos().x();
pos.y.raw() = srl.pos().y();
this->send_cast_order(ids, pos, srl.type(), srl.queue());
}
}
void GameServer::send_cast_order(const std::vector<EntityId>& ids, const Position& pos,
const uint32_t type, const bool queue)
{
ser::order::Cast srl;
srl.set_turn(this->turn_handler.get_current_turn() + 2);
if (queue)
srl.set_queue(queue);
for (const EntityId id: ids)
srl.add_entity_id(id);
srl.set_type(type);
srl.mutable_pos()->set_x(pos.x.raw());
srl.mutable_pos()->set_y(pos.y.raw());
this->send_order_to_all("CAST", srl);
this->turn_handler.insert_action(std::bind(&World::do_cast, &this->world, ids, pos,
static_cast<AbilityType>(type), queue),
srl.turn());
}
void GameServer::send_new_entity_order(const EntityType type, const Position& pos,
const uint16_t team)
{
......
......@@ -68,6 +68,16 @@ public:
void send_new_entity_order(const EntityType type, const Position& pos, const uint16_t team);
void send_move_order(const std::vector<EntityId> ids, const Position& pos, const bool queue);
/**
* Send an Order to each client, to make each of these entities cast the
* given ability.
*/
void send_cast_order(const std::vector<EntityId>& ids, const Position& pos,
const uint32_t type, const bool queue);
void send_cast_order(const std::vector<EntityId>& ids, const EntityId pos,
const uint32_t type, const bool queue);
void send_cast_order(const std::vector<EntityId>& ids,
const uint32_t type, const bool queue);
/**
* This will create the initial game
* state.
......@@ -87,6 +97,10 @@ public:
* send that path action to all clients.
*/
void on_move_request(Message*);
/**
* Called whenever we receive a CAST message from one client.
*/
void on_cast_request(Message*);
/**
* Called whenever we receive a BUILD message from one client.
* Check if the position is valid and the building can be built (money, the unit actually has this ability, etc).
......
......@@ -26,4 +26,5 @@ void RemoteGameClient::on_connection_closed()
void RemoteGameClient::install_callbacks()
{
this->install_callback("MOVE", std::bind(&GameServer::on_move_request, this->server, std::placeholders::_1));
this->install_callback("CAST", std::bind(&GameServer::on_cast_request, this->server, std::placeholders::_1));
}
......@@ -33,7 +33,6 @@ void TurnHandler::tick()
{
if (this->is_next_turn_ready() == false)
{
log_debug("Next turn is not validated");
this->pause();
return ;
}
......
......@@ -94,50 +94,12 @@ bool Camera::handle_event(const sf::Event& event)
log_debug("Mouse button not implemented.");
}
}
else if (event.type == sf::Event::KeyPressed)
this->handle_keypress(event);
this->fixup_camera_position();
if (this->mouse_selection.ongoing == true)
return true;
return false;
}
void Camera::activate_ability(const std::size_t nb)
{
const Selection& selection = this->game->get_selection();
if (selection.is_empty())
this->game->get_hud().add_info_message("No entity selected");
else
{
const auto entity = selection.get_entities().front();
Abilities* abilities = entity->get<Abilities>();
if (!abilities)
this->game->get_hud().add_info_message("Selected entity has no ability.");
else
{
const Ability* ability = abilities->get(nb);
if (!ability)
this->game->get_hud().add_info_message("Selected entity has no ability number " + std::to_string(nb));
else
{
this->game->get_hud().add_info_message("Activating ability " + ability->get_name());
}
}
}
// TODO actually only look in the abilities of active entity TYPE, not the
// first one
}
void Camera::handle_keypress(const sf::Event& event)
{
this->game->get_hud().add_info_message("Received key: " + std::to_string(event.key.code));
switch (event.key.code)
{
case sf::Keyboard::A:
this->activate_ability(0);
}
}
void Camera::handle_middle_click(const sf::Event&)
{
}
......
......@@ -24,7 +24,7 @@ Fog::Fog(const unsigned int width, const unsigned int height,
void Fog::resize(const unsigned int width, const unsigned int height)
{
// TODO, have a fallback
assert(this->texture.create(width, height));
this->texture.create(width, height);
this->texture.setSmooth(true);
this->invalidate();
}
......
#include <SFML/Graphics.hpp>
#include <gui/hud/abilities_panel.hpp>
#include <game/game_client.hpp>
#include <logging/logging.hpp>
#include <world/entity.hpp>
#include <world/abilities.hpp>
#include <world/abilities/blink.hpp>
#include <utility>
AbilitiesPanel::AbilitiesPanel(GameClient* game)
{
auto& blink = this->gui_abilities[AbilityType::Blink];
blink.left_click = {
[game](const Position& pos)
{
std::vector<EntityId> ids;
const auto& selection = game->get_selection();
if (selection.is_empty())
return true;
// Find all entities with a blink ability
for (const auto& entity: selection.get_entities())
{
Abilities* abilities = entity->get<Abilities>();
if (abilities)
{
Ability* ability = abilities->find(AbilityType::Blink);
if (ability)
{
Blink* blink = static_cast<Blink*>(ability);
// Check cooldown, mana, etc etc etc
Entity* e = const_cast<Entity*>(entity);
ids.push_back(e->get_id());
}
}
}
if (ids.empty())
return true;
const bool queue = (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) ||
sf::Keyboard::isKeyPressed(sf::Keyboard::RShift));
return game->action_cast(ids, pos, AbilityType::Blink, queue);
},
[](const sf::Vector2i&)
{
return cursor::Move;
}
};
}
const GuiAbility* AbilitiesPanel::get(const AbilityType& type) const
{
const auto it = this->gui_abilities.find(type);
if (it == this->gui_abilities.end())
return nullptr;
return &it->second;
}
#ifndef ABILITIES_PANEL_HPP_INCLUDED
#define ABILITIES_PANEL_HPP_INCLUDED
#include <world/abilities.hpp>
#include <gui/hud/gui_ability.hpp>
#include <map>
class GameClient;
class AbilitiesPanel
{
public:
AbilitiesPanel(GameClient* game);
~AbilitiesPanel() = default;
const GuiAbility* get(const AbilityType& type) const;
private:
std::map<AbilityType, GuiAbility> gui_abilities;
AbilitiesPanel(const AbilitiesPanel&) = delete;
AbilitiesPanel(AbilitiesPanel&&) = delete;
AbilitiesPanel& operator=(const AbilitiesPanel&) = delete;
AbilitiesPanel& operator=(AbilitiesPanel&&) = delete;
};
#endif /* ABILITIES_PANEL_HPP_INCLUDED */
#ifndef GUI_ABILITY_HPP_INCLUDED
#define GUI_ABILITY_HPP_INCLUDED
/**
* Contains all the information about an ability, for the GUI part. For
* example the icone to use, the activate callback (it can execute the
* ability directly, or change the LeftClick structure, which only involves
* the GUI)
*/
#include <gui/screen/left_click.hpp>
class GuiAbility
{
public:
GuiAbility() = default;
~GuiAbility() = default;
LeftClick left_click;
private:
GuiAbility(const GuiAbility&) = delete;
GuiAbility(GuiAbility&&) = delete;
GuiAbility& operator=(const GuiAbility&) = delete;
GuiAbility& operator=(GuiAbility&&) = delete;
};
#endif /* GUI_ABILITY_HPP_INCLUDED */
......@@ -7,6 +7,7 @@
#include <world/abilities.hpp>
#include <world/abilities/blink.hpp>
#include <gui/hud/abilities_panel.hpp>
#include <logging/logging.hpp>
......@@ -16,7 +17,8 @@ Hud::Hud(GameClient* game, Screen* screen):
// minimap(game, screen),
// selection_panel(game, world->get_selection_ptr()),
// action_panel(game, screen, world->get_selection_ptr(), mod),
game(game)
game(game),
abilities_panel(game)
{
const sf::Vector2u win_size = this->screen->get_window_size();
// Install a callback on the selection that will reset the action_panel
......@@ -87,12 +89,22 @@ void Hud::draw()
bool Hud::handle_event(const sf::Event& event)
{
// if (this->minimap.handle_event(event) == true)
// return true;
// if (this->selection_panel.handle_event(event, this->world) == true)
// return true;
// if (this->action_panel.handle_event(event) == true)
// return true;
if (event.type == sf::Event::KeyPressed)
return this->handle_keypress(event);
return false;
}
bool Hud::handle_keypress(const sf::Event& event)
{
this->game->get_hud().add_info_message("Received key: " + std::to_string(event.key.code));
switch (event.key.code)
{
case sf::Keyboard::A:
this->activate_ability(0);
return true;
default:
return false;
}
return false;
}
......@@ -111,3 +123,34 @@ bool Hud::is_entity_hovered(const Entity* entity) const
// return this->selection_panel.is_entity_hovered(entity);
return false;
}
void Hud::activate_ability(const std::size_t nb)
{
const Selection& selection = this->game->get_selection();
if (selection.is_empty())
this->game->get_hud().add_info_message("No entity selected");
else
{
const auto entity = selection.get_entities().front();
Abilities* abilities = entity->get<Abilities>();
if (!abilities)
this->game->get_hud().add_info_message("Selected entity has no ability.");
else
{
const Ability* ability = abilities->get(nb);
if (!ability)
this->game->get_hud().add_info_message("Selected entity has no ability number " + std::to_string(nb));
else
{
this->game->get_hud().add_info_message("Activating ability " + ability->get_name());
this->game->get_hud().add_info_message("The ability type is " + std::to_string(static_cast<int>(ability->get_type())));
const GuiAbility* gui_ab = this->abilities_panel.get(ability->get_type());
assert(gui_ab);
this->screen->set_left_click(gui_ab->left_click);
}
}
}
// TODO actually only look in the abilities of actiev entity TYPE, not the
// first one
}
......@@ -17,11 +17,9 @@
#define HUD_HEIGHT 323
#include <gui/camera/camera.hpp>
// #include <gui/hud/minimap.hpp>
// #include <gui/hud/selection_panel.hpp>
// #include <gui/hud/action_panel.hpp>
#include <gui/screen/screen_element.hpp>
#include <gui/hud/info_hud.hpp>
#include <gui/hud/abilities_panel.hpp>
class Screen;
class GameClient;
......@@ -36,18 +34,18 @@ public:
void update(const utils::Duration& dt) override final;
bool is_entity_hovered(const Entity*) const;
void add_info_message(std::string&& text);
void activate_ability(const std::size_t nb);
bool handle_keypress(const sf::Event& event);
private:
Hud(const Hud&);
Hud& operator=(const Hud&);
// Minimap minimap;
// SelectionPanel selection_panel;
// ActionPanel action_panel;
GameClient* game;
sf::Texture hud_texture;
sf::Sprite hud_sprite;
sf::Font font;
AbilitiesPanel abilities_panel;
InfoHud info_hud;
};
......
......@@ -31,31 +31,11 @@
#include <world/position.hpp>
#include <gui/screen/screen_element.hpp>
#include <gui/cursor.hpp>
#include <gui/screen/left_click.hpp>
#include <vector>
#include <memory>
/**
* Structure containing informations about the current “left click”
* action. It contains a callback of what happens when the user left clicks
* on the camera area, and what to draw at the cursor position.
*/
struct LeftClick
{
/**
* The callback called when we left click. Returns True if the callback
* has been successfull and the LeftClick can be reset to LeftClick::null.
*/
std::function<bool(const Position& world_position)> callback;
/**
* The callback called when we draw the cursor (for example to draw the
* area of effect of the selected spell, or the building to be built)
*/
std::function<cursor::type (const sf::Vector2i& mouse_pos)> cursor_callback;
static LeftClick null;
};
class Screen
{
public:
......
......@@ -24,6 +24,7 @@
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/chrono.hpp>
#include <logging/logging.hpp>
#include <network/base_ioservice.hpp>
......
#include <world/entity.hpp>
#include <world/abilities.hpp>
#include <cassert>
......@@ -19,3 +20,33 @@ void Abilities::add(const std::size_t index,
{
this->abilities[index] = std::move(ability);
}
Ability* Abilities::find(const AbilityType& type) const
{
for (auto& ability: this->abilities)
{
if (ability->get_type() == type)
return ability.get();
}
return nullptr;
}
template <typename T>
T* get_ability(Entity* entity)
{
Abilities* abilities = entity->get<Abilities>();
if (!abilities)
return nullptr;
Ability* ability = abilities->find(T::ability_type);
if (!ability)
return nullptr;
return static_cast<T*>(ability);
}
Ability* get_ability(Entity* entity, const AbilityType& type)
{
Abilities* abilities = entity->get<Abilities>();
if (!abilities)
return nullptr;
return abilities->find(type);
}
......@@ -3,7 +3,6 @@
/**
* Container for one or more abilities.
* This is static
*/
#include <world/components.hpp>
......@@ -32,6 +31,10 @@ public:
const std::vector<std::unique_ptr<Ability>>& get() const;
const Ability* get(const std::size_t index) const;
/**
* Look for an Ability with that type.
*/
Ability* find(const AbilityType& type) const;
private:
std::vector<std::unique_ptr<Ability>> abilities;
......@@ -42,4 +45,8 @@ private:
Abilities& operator=(Abilities&&) = delete;
};
template <typename T>
T* get_ability(Entity* entity, const AbilityType& type);
Ability* get_ability(Entity* entity, const AbilityType& type);
#endif /* ABILITIES_HPP_INCLUDED */
......@@ -5,15 +5,28 @@
class World;
class Entity;
class Vec2;
using Position = Vec2;
enum class AbilityType
{
Blink,
count
};