Updated lots of things

This commit is contained in:
2025-10-22 16:39:19 +03:00
parent 9c4bea5983
commit 3f0484e87c
56 changed files with 4497 additions and 160 deletions

349
src/gamedata/goap.h Normal file
View File

@@ -0,0 +1,349 @@
#ifndef H_GOAP_H_
#define H_GOAP_H_
#include <string>
#include <memory>
#include <flecs.h>
namespace ECS
{
struct Blackboard;
}
struct BaseAction;
struct BaseActionExec;
namespace ECS
{
struct Blackboard {
enum {
HIGH_LUST = (1 << 0),
LOW_LUST = (1 << 1),
FULL_LUST = (1 << 2),
LOW_HEALTH = (1 << 3),
FULL_HEALTH = (1 << 4),
HAS_TARGET = (1 << 5),
LOW_STAMINA = (1 << 6),
FULL_STAMINA = (1 << 7),
REACHED_TARGET = (1 << 8),
};
flecs::entity me;
int health;
int stamina;
int lust;
uint32_t flags;
std::set<BaseActionExec *> _active;
bool operator==(const Blackboard &other)
{
return flags == other.flags;
}
void set_flag(int flag)
{
flags |= flag;
}
void clear_flag(int flag)
{
flags &= ~flag;
}
bool get_flag(int flag) const
{
return flags & flag;
}
bool check_flag(int flag) const
{
return (flags & flag) == flag;
}
};
}
struct BaseAction {
std::string m_name;
public:
enum { OK = 0, BUSY, ABORT };
private:
std::unique_ptr<BaseActionExec> m_exec;
public:
BaseAction(const std::string &name, BaseActionExec *exec)
: m_name(name)
, m_exec(exec)
{
}
const std::string &get_name() const
{
return m_name;
}
void plan_effects(ECS::Blackboard &state)
{
// std::cout << m_name << " pre: " << &state << " " << state.flags
// << std::endl;
_plan_effects(state);
// std::cout << m_name << " post: " << &state << " " << state.flags
// << std::endl;
}
virtual bool can_run(const ECS::Blackboard &state, bool debug = false);
virtual void _plan_effects(ECS::Blackboard &state);
bool is_active(const ECS::Blackboard &state);
bool stop(ECS::Blackboard &state);
int execute(ECS::Blackboard &state);
virtual int get_cost(const ECS::Blackboard &state) const;
};
struct BaseActionExec {
enum {
OK = BaseAction::OK,
BUSY = BaseAction::BUSY,
ABORT = BaseAction::ABORT
};
BaseActionExec(int set_bits, int clear_bits)
: m_set_bits(set_bits)
, m_clear_bits(clear_bits)
{
}
bool is_active(const ECS::Blackboard &state)
{
return state._active.find(this) != state._active.end();
}
virtual int _execute(ECS::Blackboard &state) = 0;
virtual void _enter(ECS::Blackboard &state) = 0;
virtual void _exit(ECS::Blackboard &state) = 0;
virtual int _get_cost(const ECS::Blackboard &state) const
{
return 1;
}
virtual bool _can_run(const ECS::Blackboard &state, bool debug = false)
{
return true;
}
int m_set_bits, m_clear_bits;
};
template <typename State, typename Act, int N = 100> class BasePlanner {
struct VisitedState {
int priority;
int cost;
State state;
// Used to reconstruct the path
VisitedState *parent;
Act *action;
// Only used for linked list management
VisitedState *next;
};
VisitedState nodes[N];
void visited_states_array_to_list(VisitedState *nodes, int len)
{
for (auto i = 0; i < len - 1; i++) {
nodes[i].next = &nodes[i + 1];
}
nodes[len - 1].next = nullptr;
}
VisitedState *priority_list_pop(VisitedState *&list)
{
VisitedState *min_elem = nullptr;
auto min_prio = std::numeric_limits<int>::max();
// Find the element with the lowest priority
for (auto p = list; p != nullptr; p = p->next) {
if (p->priority < min_prio) {
min_elem = p;
min_prio = p->priority;
}
}
// Remove it from the list
if (min_elem == list) {
list = list->next;
} else {
for (auto p = list; p != nullptr; p = p->next) {
if (p->next == min_elem) {
p->next = p->next->next;
min_elem->next = nullptr;
}
}
}
return min_elem;
}
VisitedState *list_pop_head(VisitedState *&head)
{
auto ret = head;
head = head->next;
ret->next = nullptr;
return ret;
}
void list_push_head(VisitedState *&head, VisitedState *elem)
{
elem->next = head;
head = elem;
}
void update_queued_state(VisitedState *previous,
const VisitedState *current)
{
if (previous->cost > current->cost) {
previous->priority = current->cost;
previous->priority = current->priority;
previous->parent = current->parent;
previous->action = current->action;
}
}
public:
struct BaseGoal {
std::string m_name;
public:
BaseGoal(const std::string &name)
: m_name(name)
{
}
/** Checks if the goal is reached for the given state. */
virtual bool is_reached(const State &state) const
{
return distance_to(state) == 0;
}
/** Computes the distance from state to goal. */
virtual int distance_to(const State &state) const = 0;
virtual ~BaseGoal() = default;
const std::string &get_name() const
{
return m_name;
}
};
/** Finds a plan from state to goal and returns its length.
*
* If path is given, then the found path is stored there.
*/
int plan(const State &state, BaseGoal &goal, Act *actions[],
unsigned action_count, Act **path = nullptr, int path_len = 10)
{
visited_states_array_to_list(nodes, N);
auto free_nodes = &nodes[0];
VisitedState *open = list_pop_head(free_nodes);
VisitedState *close = nullptr;
open->state = state;
open->cost = 0;
open->priority = 0;
open->parent = nullptr;
open->action = nullptr;
while (open) {
VisitedState *current = priority_list_pop(open);
list_push_head(close, current);
if (goal.is_reached(current->state)) {
auto len = 0;
for (auto p = current->parent; p;
p = p->parent) {
len++;
}
if (len > path_len) {
return -1;
}
if (path) {
auto i = len - 1;
for (auto p = current; p->parent;
p = p->parent, i--) {
path[i] = p->action;
}
}
return len;
}
for (auto i = 0u; i < action_count; i++) {
auto action = actions[i];
if (action->can_run(current->state)) {
// Cannot allocate a new node, abort
if (free_nodes == nullptr) {
// Garbage collect the node that is most unlikely to be
// visited (i.e. lowest priority)
VisitedState *gc,
*gc_prev = nullptr;
for (gc = open; gc && gc->next;
gc = gc->next) {
gc_prev = gc;
}
if (!gc) {
return -2 /* OOM */;
}
if (gc_prev) {
gc_prev->next = nullptr;
}
free_nodes = gc;
}
auto neighbor =
list_pop_head(free_nodes);
neighbor->state = current->state;
action->plan_effects(neighbor->state);
neighbor->cost =
current->cost + 1 +
action->get_cost(
neighbor->state);
neighbor->priority =
current->priority + 1 +
goal.distance_to(
neighbor->state);
neighbor->parent = current;
neighbor->action = action;
bool should_insert = true;
// Check if the node is already in the list of nodes
// scheduled to be visited
for (auto p = open; p; p = p->next) {
if (p->state ==
neighbor->state) {
should_insert = false;
update_queued_state(
p, neighbor);
}
}
// Check if the state is in the list of already visited
// state
for (auto p = close; p; p = p->next) {
if (p->state ==
neighbor->state) {
should_insert = false;
update_queued_state(
p, neighbor);
}
}
if (should_insert) {
list_push_head(open, neighbor);
} else {
list_push_head(free_nodes,
neighbor);
}
}
}
}
// Reaching here means we did not find a path
return -1 /* No path */;
}
};
template <class RunnerType> struct DeclareAction : public BaseAction {
RunnerType runner;
template <class... Args>
DeclareAction(const std::string &name, Args... args)
: runner(args...)
, BaseAction(name, &runner)
{
}
};
#endif