Commit aedda0ac authored by louiz’'s avatar louiz’

Introduce Concentrate and Dash abilities

parent d425c86b
......@@ -95,4 +95,10 @@ void Game::cast_callback(Message* message)
std::bind(&World::do_cast_on_pos, &this->world, ids, pos, type, srl.queue()),
srl.turn());
}
else
{
this->turn_handler.insert_action(
std::bind(&World::do_cast, &this->world, ids, type, srl.queue()),
srl.turn());
}
}
......@@ -313,6 +313,19 @@ bool GameClient::action_cast(const std::vector<EntityId>& ids,
return true;
}
bool GameClient::action_cast(const std::vector<EntityId>& ids,
const AbilityType& type, const bool queue)
{
ser::request::Cast srl;
if (queue)
srl.set_queue(queue);
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);
......
......@@ -278,6 +278,8 @@ void GameServer::on_cast_request(Message* message)
}
else if (srl.has_target())
this->send_cast_order(ids, srl.target(), srl.type(), srl.queue());
else
this->send_cast_order(ids, srl.type(), srl.queue());
}
void GameServer::send_cast_order(const std::vector<EntityId>& ids, const Position& pos,
......@@ -315,6 +317,22 @@ void GameServer::send_cast_order(const std::vector<EntityId>& ids, const EntityI
srl.turn());
}
void GameServer::send_cast_order(const std::vector<EntityId>& ids,
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);
this->send_order_to_all("CAST", srl);
this->turn_handler.insert_action(std::bind(&World::do_cast, &this->world, ids,
static_cast<AbilityType>(type), queue),
srl.turn());
}
void GameServer::send_new_entity_order(const EntityType type, const Position& pos,
const uint16_t team)
{
......
......@@ -10,6 +10,7 @@
#include <world/abilities/attack.hpp>
#include <world/abilities/phase.hpp>
#include <world/abilities/emp.hpp>
#include <world/abilities/dash.hpp>
#include <utility>
......@@ -90,7 +91,33 @@ AbilitiesPanel::AbilitiesPanel(GameClient* game)
}
};
// Dash button
auto& dash = this->gui_abilities[AbilityType::Dash];
dash.callback = [game]()
{
auto ids = get_selected_entities_with_ability<Dash>(game);
if (ids.empty())
return true;
const bool queue = (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) ||
sf::Keyboard::isKeyPressed(sf::Keyboard::RShift));
log_debug("Starting dash concentrate on " << ids.size() << " entities");
return game->action_cast({ids[0]}, AbilityType::Concentrate, queue);
};
dash.left_click = {
[game](const Position& pos)
{
auto ids = get_selected_entities_with_ability<Dash>(game);
if (ids.empty())
return true;
log_debug("left_click to start dashing");
return game->action_cast({ids[0]}, pos, AbilityType::Dash, false);
},
[](const sf::Vector2i&)
{
// Draw the dash load thingy
return cursor::Cast;
}
};
}
const GuiAbility* AbilitiesPanel::get(const AbilityType& type) const
......
......@@ -114,6 +114,9 @@ bool Hud::handle_keypress(const sf::Event& event)
case sf::Keyboard::E:
this->activate_ability(3);
return true;
case sf::Keyboard::P:
this->activate_ability(4);
return true;
default:
return false;
}
......@@ -174,7 +177,7 @@ void Hud::activate_ability(const std::size_t nb)
assert(gui_ab);
if (gui_ab->callback)
gui_ab->callback();
else
if (gui_ab->left_click.cursor_callback && gui_ab->left_click.callback)
this->screen->set_left_click(gui_ab->left_click);
}
}
......
......@@ -19,6 +19,7 @@ Screen::Screen():
// this->building_textures = mod.get_building_textures();
this->win.setPosition(sf::Vector2i(0, 0));
// this->win.setMouseCursorVisible(false);
this->win.setKeyRepeatEnabled(false);
}
Screen::~Screen()
......
......@@ -12,9 +12,11 @@ using Position = Vec2;
enum class AbilityType
{
Concentrate,
Attack,
Blink,
Phase,
Dash,
Emp,
count
......@@ -26,6 +28,7 @@ public:
Ability() = default;
virtual ~Ability() = default;
virtual void tick(Entity*, World*) {}
virtual void cast(Entity*, World*, const bool) {}
virtual void cast(Entity*, World*, const Position&, const bool) {}
virtual void cast(Entity*, World*, const std::shared_ptr<Entity>&, const bool) {}
......
#include <world/abilities/concentrate.hpp>
#include <world/works/concentrate_work.hpp>
#include <world/entity.hpp>
#include <logging/logging.hpp>
template <>
const std::string NamedAbility<Concentrate>::name = "Concentrate";
template <>
const AbilityType NamedAbility<Concentrate>::ability_type = AbilityType::Concentrate;
Concentrate::Concentrate():
ActiveAbility(TargetType::None)
{
}
void Concentrate::cast(Entity* entity, World* world, const bool queue)
{
log_debug("Concentrate::cast. Starting concentrate task");
// TODO queue, or disable queue alltogether
auto work = std::make_unique<ConcentrateWork>(entity, world);
entity->set_work(std::move(work));
}
#ifndef CONCENTRATE_HPP_INCLUDED
#define CONCENTRATE_HPP_INCLUDED
#include <world/abilities/active_ability.hpp>
/**
* A very simple ability that just starts a ConcentrateWork until the entity
* casts an other ability. That other ability will probably use the value
* of the ConcentrateWork, for example to determine its strength.
*/
class Concentrate: public ActiveAbility<Concentrate>
{
public:
Concentrate();
~Concentrate() = default;
void cast(Entity*, World*, const bool queue) override final;
private:
Concentrate(const Concentrate&) = delete;
Concentrate(Concentrate&&) = delete;
Concentrate& operator=(const Concentrate&) = delete;
Concentrate& operator=(Concentrate&&) = delete;
};
#endif /* CONCENTRATE_HPP_INCLUDED */
#include <world/abilities/dash.hpp>
#include <world/entity.hpp>
#include <world/works/concentrate_work.hpp>
#include <world/works/dash_work.hpp>
#include <logging/logging.hpp>
template <>
const std::string NamedAbility<Dash>::name = "Dash";
template <>
const AbilityType NamedAbility<Dash>::ability_type = AbilityType::Dash;
Dash::Dash():
ActiveAbility(TargetType::Point)
{
}
void Dash::cast(Entity* entity, World* world, const Position& pos, const bool queue)
{
log_debug("Dash::cast. Starting DashWork");
// Get the ConcentrateWork of the entity
auto concentrate = dynamic_cast<ConcentrateWork*>(entity->get_current_work());
if (concentrate == nullptr)
{
// If none is found, that's weird, but do nothing
log_debug("Work is not a ConcentrateWork, doing nothing.");
return;
}
// Otherwise, use its value for the DashWork
Task* task = concentrate->get_task();
if (!task)
return;
auto concentrate_task = static_cast<ConcentrateTask*>(task);
auto value = concentrate_task->value();
log_debug("The concentrate task value is: " << value);
auto work = std::make_unique<DashWork>(entity, world, pos, 35,
nullptr, nullptr);
entity->set_work(std::move(work));
}
#ifndef DASH_HPP_INCLUDED
#define DASH_HPP_INCLUDED
#include <world/abilities/active_ability.hpp>
class Dash: public ActiveAbility<Dash>
{
public:
Dash();
~Dash() = default;
void cast(Entity* entity, World*, const Position& pos, const bool queue) override final;
private:
Dash(const Dash&) = delete;
Dash(Dash&&) = delete;
Dash& operator=(const Dash&) = delete;
Dash& operator=(Dash&&) = delete;
};
#endif /* DASH_HPP_INCLUDED */
......@@ -37,6 +37,20 @@ void Entity::clear_works()
this->works.clear();
}
void Entity::interrupt()
{
if (this->works.empty())
return;
this->works.front()->interrupt();
}
Work* Entity::get_current_work()
{
if (this->works.empty())
return nullptr;
return this->works.front().get();
}
void Entity::tick(World* world)
{
for (const auto& stat: this->status)
......
......@@ -39,6 +39,8 @@ public:
void clear_works();
void set_work(std::unique_ptr<Work>);
void queue_work(std::unique_ptr<Work>);
void interrupt();
Work* get_current_work();
template <typename ComponentClass>
ComponentClass* get() const
......
......@@ -12,6 +12,8 @@
#include <world/abilities/attack.hpp>
#include <world/abilities/phase.hpp>
#include <world/abilities/emp.hpp>
#include <world/abilities/dash.hpp>
#include <world/abilities/concentrate.hpp>
EntityFactory::EntityFactory()
{
......@@ -35,11 +37,14 @@ std::unique_ptr<Entity> EntityFactory::make_entity(const EntityType type)
entity->add_component(std::make_unique<Mobility>(2.2_fix));
entity->add_component(std::make_unique<Acquisition>(200_fix));
auto abilities = std::make_unique<Abilities>(4u, 20u, 20u);
auto abilities = std::make_unique<Abilities>(6u, 0u, 0u);
abilities->add(0, std::make_unique<Attack>(20u, 20u));
abilities->add(1, std::make_unique<Blink>());
abilities->add(2, std::make_unique<Phase>());
abilities->add(3, std::make_unique<Emp>());
abilities->add(4, std::make_unique<Dash>());
abilities->add(5, std::make_unique<Concentrate>());
entity->add_component(std::move(abilities));
}
......
......@@ -79,6 +79,13 @@ void Mobility::move_towards(const Position& goal, Location* location)
this->last_movement = movement;
}
void Mobility::move_towards(const Position& goal, Location* location, Fix16 speed)
{
auto speed_backup = this->get_speed();
this->set_speed(speed);
this->move_towards(goal, location);
this->set_speed(speed_backup);
}
Fix16 Mobility::get_angle() const
{
return this->last_movement.angle();
......
......@@ -32,6 +32,7 @@ public:
* The goal is reached if after this function, the location's position == goal.
*/
void move_towards(const Position& goal, Location* location);
void move_towards(const Position& goal, Location* location, Fix16 speed);
/**
* Get the angle of the entity that has this mobility, based on the latest
* movement done.
......
......@@ -4,12 +4,14 @@
enum class TaskType
{
None,
Concentrate,
Path,
Follow,
Attack,
Blink,
Emp,
Projectile,
Dash,
count
};
......
#include <world/tasks/concentrate_task.hpp>
#include <logging/logging.hpp>
ConcentrateTask::ConcentrateTask(Entity* entity):
Task(entity),
quantity(0)
{
}
bool ConcentrateTask::tick(World* world)
{
// TODO check that we do not overflow the Fix16 type
this->quantity++;
return false;
}
Fix16 ConcentrateTask::value() const
{
return this->quantity;
}
#ifndef CONCENTRATE_TASK_HPP_INCLUDED
#define CONCENTRATE_TASK_HPP_INCLUDED
#include <world/task.hpp>
#include <fixmath/fix16.hpp>
#include <functional>
class ConcentrateTask: public Task
{
public:
ConcentrateTask(Entity* entity);
~ConcentrateTask() = default;
bool tick(World* world) override final;
TaskType get_type() const override final
{
return TaskType::Concentrate;
}
Fix16 value() const;
private:
Fix16 quantity;
ConcentrateTask(const ConcentrateTask&) = delete;
ConcentrateTask(ConcentrateTask&&) = delete;
ConcentrateTask& operator=(const ConcentrateTask&) = delete;
ConcentrateTask& operator=(ConcentrateTask&&) = delete;
};
#endif /* CONCENTRATE_TASK_HPP_INCLUDED */
#include <world/tasks/dash_task.hpp>
#include <world/entity.hpp>
#include <world/location.hpp>
#include <world/mobility.hpp>
DashTask::DashTask(Entity* entity, const Position& goal, Fix16 speed, std::function<void(Entity*)> impact_cb,
std::function<void(void)> goal_cb):
Task(entity),
goal(goal),
speed(speed),
impact_callback(impact_cb),
goal_callback(goal_cb),
mobility(entity->get<Mobility>()),
location(entity->get<Location>())
{
assert(this->mobility);
assert(this->location);
}
bool DashTask::tick(World* world)
{
this->mobility->move_towards(this->goal, this->location, this->speed);
// TODO check collision
if (this->goal == this->location->position())
{
// TODO adjust location to be on a valid world position
if (this->goal_callback)
this->goal_callback();
return true;
}
return false;
}
#ifndef DASH_TASK_HPP_INCLUDED
#define DASH_TASK_HPP_INCLUDED
#include <world/task.hpp>
#include <world/position.hpp>
#include <functional>
class Location;
class Mobility;
class DashTask: public Task
{
public:
DashTask(Entity* entity, const Position& goal, Fix16 speed, std::function<void(Entity*)> impact_cb,
std::function<void(void)> goal_cb);
~DashTask() = default;
TaskType get_type() const
{
return TaskType::Dash;
}
bool tick(World* world) override final;
private:
const Position goal;
const Fix16 speed;
/**
* Function called for each encountered entity
*/
std::function<void(Entity*)> impact_callback;
/**
* Function called when the goal is reached
*/
std::function<void(void)> goal_callback;
Location* location;
Mobility* mobility;
DashTask(const DashTask&) = delete;
DashTask(DashTask&&) = delete;
DashTask& operator=(const DashTask&) = delete;
DashTask& operator=(DashTask&&) = delete;
};
#endif /* DASH_TASK_HPP_INCLUDED */
......@@ -14,3 +14,8 @@ void Work::interrupt()
this->on_interrupted();
this->task.reset(nullptr);
}
Task* Work::get_task()
{
return this->task.get();
}
......@@ -40,6 +40,7 @@ public:
* Calls the on_interrupted method of the subclass
*/
void interrupt();
Task* get_task();
protected:
/**
......
#ifndef CONCENTRATE_WORK_HPP_INCLUDED
#define CONCENTRATE_WORK_HPP_INCLUDED
#include <world/works/single_task_work.hpp>
#include <world/tasks/concentrate_task.hpp>
/**
* A work, similar to channeling on dota 2 (http://dota2.gamepedia.com/Channeling).
*/
using ConcentrateWork = SingleTaskWork<ConcentrateTask>;
#endif
#ifndef DASH_WORK_HPP_INCLUDED
#define DASH_WORK_HPP_INCLUDED
#include <world/works/single_task_work.hpp>
#include <world/tasks/dash_task.hpp>
using DashWork = SingleTaskWork<DashTask>;
#endif /* DASH_WORK_HPP_INCLUDED */
......@@ -213,6 +213,25 @@ void World::do_cast_on_pos(const std::vector<EntityId>& ids, const Position& pos
}
}
void World::do_cast(const std::vector<EntityId>& ids, const AbilityType& type, const bool queue)
{
for (const EntityId id: ids)
{
log_debug("do_cast: " << id);
Entity* entity = this->get_entity_by_id(id);
if (!entity)
continue;
log_debug("ok");
Ability* ability = get_ability(entity, type);
if (ability)
ability->cast(entity, this, queue);
else
{
log_warning("Received a cast order, but entity " << id << " does not have ability: " << static_cast<int>(type));
}
}
}
std::shared_ptr<Entity> World::get_shared_entity_by_id(EntityId id)
{
// Should use something like this->entities[id], to optimize.
......
......@@ -72,6 +72,8 @@ public:
const bool queue);
void do_follow(const std::vector<EntityId>& ids, const EntityId& target_id,
const bool queue);
void do_cast(const std::vector<EntityId>& ids, const AbilityType& type,
const bool queue);
void do_cast_on_pos(const std::vector<EntityId>& ids, const Position& pos,
const AbilityType& type, const bool queue);
void do_cast_on_target(const std::vector<EntityId>& ids,
......
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