Commit 109d98d4 authored by louiz’'s avatar louiz’

Add a FollowTask and thus a way to Attack a single target

parent 34a8d8f1
......@@ -47,7 +47,10 @@ AbilitiesPanel::AbilitiesPanel(GameClient* game)
const Entity* target = game->get_camera().get_entity_under_mouse();
log_debug("attacking with " << ids.size() << " entities");
return game->action_cast(ids, pos, AbilityType::Attack, queue);
if (target)
return game->action_cast(ids, target->get_id(), AbilityType::Attack, queue);
else
return game->action_cast(ids, pos, AbilityType::Attack, queue);
},
[](const sf::Vector2i&)
{
......
......@@ -28,6 +28,12 @@ void Attack::cast(Entity* entity, World*, const Position& pos, const bool queue)
entity->set_work(std::move(work));
}
void Attack::cast(Entity* entity, World *, Entity* target, const bool queue)
void Attack::cast(Entity* entity, World *, const std::shared_ptr<Entity>& target, const bool queue)
{
log_debug("Attacking with entity " << entity->get_id() << " the target " << target->get_id());
auto work = std::make_unique<AttackWork>(entity, target);
if (queue)
entity->queue_work(std::move(work));
else
entity->set_work(std::move(work));
}
......@@ -9,7 +9,7 @@ public:
Attack(const std::size_t fs, const std::size_t bs);
~Attack() = default;
void cast(Entity* entity, World *, const Position& pos, const bool queue) override final;
void cast(Entity* entity, World *, Entity* target, const bool queue) override final;
void cast(Entity* entity, World *, const std::shared_ptr<Entity>& target, const bool queue) override final;
protected:
std::size_t frontswing_duration;
......
......@@ -5,6 +5,7 @@ enum class TaskType
{
None,
Path,
Follow,
Attack,
Blink,
Emp,
......
#include <world/tasks/follow_task.hpp>
#include <world/world.hpp>
#include <world/entity.hpp>
#include <world/mobility.hpp>
#include <world/location.hpp>
#include <cassert>
#include <logging/logging.hpp>
FollowTask::FollowTask(Entity* entity, const std::weak_ptr<Entity>& target):
Task(entity),
target(target),
current_destination(Position::invalid),
path{},
mobility(entity->get<Mobility>()),
location(entity->get<Location>())
{
assert(this->mobility);
assert(this->location);
}
bool FollowTask::tick(World* world)
{
if (this->target.expired())
return true;
// TODO check if the target is visible by the entity's team
Location* target_location = this->target.lock()->get<Location>();
if (this->current_destination == Position::invalid ||
Position::distance(this->current_destination, target_location->position()) > 100)
{
log_debug("Calculating path to target (because it moved, or to init)");
this->current_destination = target_location->position();
this->path = world->calculate_path(this->current_destination, this->location);
}
this->mobility->follow_path(this->path, world, this->location);
return false;
}
#ifndef FOLLOW_TASK_HPP_INCLUDED
#define FOLLOW_TASK_HPP_INCLUDED
#include <world/task.hpp>
#include <world/path.hpp>
#include <world/position.hpp>
#include <memory>
class Entity;
class World;
class Mobility;
class Location;
class FollowTask: public Task
{
public:
FollowTask(Entity* entity, const std::weak_ptr<Entity>& target);
~FollowTask() = default;
bool tick(World* world) override final;
TaskType get_type() const override final
{ return TaskType::Follow; }
private:
/**
* The entity we follow. If it goes out of sight or disappears, we have
* finished the task.
*/
std::weak_ptr<Entity> target;
/**
* The position the target had when we calculated the path for the last
* time. Each tick we check if the current position is too far from this
* one, if yes we recalculate the entire path.
*/
Position current_destination;
/**
* The path to track to go near the target. This path is recalculated if
* the target's position changed too much.
*/
Path path;
/**
* The entity elements that we need to execute the work on.
*/
Mobility* mobility;
Location* location;
FollowTask(const FollowTask&) = delete;
FollowTask(FollowTask&&) = delete;
FollowTask& operator=(const FollowTask&) = delete;
FollowTask& operator=(FollowTask&&) = delete;
};
#endif /* FOLLOW_TASK_HPP_INCLUDED */
......@@ -6,14 +6,21 @@
#include <world/team.hpp>
#include <world/tasks/attack_task.hpp>
#include <world/tasks/path_task.hpp>
#include <world/tasks/follow_task.hpp>
#include <logging/logging.hpp>
AttackWork::AttackWork(Entity* entity, const Position& destination):
AttackWork(entity, {}, destination)
{
}
AttackWork::AttackWork(Entity* entity, std::weak_ptr<Entity> target, const Position& destination):
Work(entity),
location(entity->get<Location>()),
acquire_distance(380_fix),
destination(destination)
destination(destination),
target(target)
{
assert(location);
}
......@@ -61,13 +68,35 @@ bool AttackWork::tick(World* world)
return false;
}
// If we are not attacking, try to acquire a target. If we find one, we
// set our task to an AttackTask
this->try_acquire_target(world);
if (!this->task)
this->set_task(world, std::make_unique<PathTask>(this->entity, this->destination));
// If we are attacking a position
if (this->destination != Position::invalid)
{
// Try to acquire a target. If we find one, we
// set our task to an AttackTask
this->try_acquire_target(world);
// If we did not find any, just follow the path to destination
if (!this->task)
this->set_task(world, std::make_unique<PathTask>(this->entity,
this->destination));
}
else
{
// If we are follow-attacking a specific target, follow it until it is
// within reach.
// Find out if the target is in our reach
Location* target_location = this->target.lock()->get<Location>();
assert(target_location);
if (Position::distance(target_location->position(),
this->location->position()) < this->acquire_distance)
this->set_task(world, std::make_unique<AttackTask>(this->entity, this->target));
else if (!this->task)
this->set_task(world, std::make_unique<FollowTask>(this->entity, this->target));
}
bool res = this->task->tick(world);
if (this->task->get_type() == TaskType::Path)
if (this->task->get_type() == TaskType::Path ||
this->task->get_type() == TaskType::Follow)
return res;
return false;
}
......@@ -17,6 +17,8 @@ class AttackWork: public Work
{
public:
AttackWork(Entity* entity, const Position& destination);
AttackWork(Entity* entity, std::weak_ptr<Entity> target,
const Position& destination = Position::invalid);
~AttackWork() = default;
bool tick(World* world) override final;
......@@ -26,7 +28,15 @@ private:
// The entity position, to do the acquire-target thing
Location* location;
Fix16 acquire_distance;
/**
* Only used when attacking a position, if we attack a target, it's set to
* Position::invalid.
*/
const Position destination;
/**
* Only used when attacking a specific target
*/
const std::weak_ptr<Entity> target;
AttackWork(const AttackWork&) = delete;
AttackWork(AttackWork&&) = delete;
......
......@@ -189,6 +189,18 @@ void World::do_cast_on_pos(const std::vector<EntityId>& ids, const Position& pos
}
}
std::shared_ptr<Entity> World::get_shared_entity_by_id(EntityId id)
{
// Should use something like this->entities[id], to optimize.
// Entities should be placed directly in a vector, for fast access.
for (const auto& entity: this->entities)
{
if (entity->get_id() == id)
return entity;
}
return {};
}
Entity* World::get_entity_by_id(EntityId id)
{
// Should use something like this->entities[id], to optimize.
......
......@@ -76,6 +76,7 @@ public:
const EntityId target_id, const AbilityType& type,
const bool queue);
Entity* get_entity_by_id(EntityId id);
std::shared_ptr<Entity> get_shared_entity_by_id(EntityId id);
/**
* Sends a message to the server saying that we confirm that action.
*/
......
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