|
|
|
@ -18,6 +18,32 @@ using namespace components; |
|
|
|
|
using lighting::LightSource; |
|
|
|
|
using ftxui::Color; |
|
|
|
|
|
|
|
|
|
struct EntityAI { |
|
|
|
|
std::string script; |
|
|
|
|
ai::State start; |
|
|
|
|
ai::State goal; |
|
|
|
|
ai::ActionPlan plan; |
|
|
|
|
|
|
|
|
|
EntityAI(std::string script, ai::State start, ai::State goal) : |
|
|
|
|
script(script), start(start), goal(goal) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
EntityAI() {}; |
|
|
|
|
|
|
|
|
|
bool wants_to(std::string name) { |
|
|
|
|
return plan.script[0].name == name; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void set_state(std::string name, bool setting) { |
|
|
|
|
ai::set(start, name, setting); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void update() { |
|
|
|
|
plan = ai::plan(script, start, goal); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
void System::lighting(GameLevel &level) { |
|
|
|
|
auto &light = *level.lights; |
|
|
|
|
auto &world = *level.world; |
|
|
|
@ -36,26 +62,34 @@ void System::lighting(GameLevel &level) { |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void System::generate_paths(GameLevel &level) { |
|
|
|
|
auto player = level.world->get_the<Player>(); |
|
|
|
|
const auto &player_position = level.world->get<Position>(player.entity); |
|
|
|
|
|
|
|
|
|
level.map->set_target(player_position.location); |
|
|
|
|
level.map->make_paths(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void System::enemy_ai(GameLevel &level) { |
|
|
|
|
auto &world = *level.world; |
|
|
|
|
auto &map = *level.map; |
|
|
|
|
auto player = world.get_the<Player>(); |
|
|
|
|
const auto &player_position = world.get<Position>(player.entity); |
|
|
|
|
map.set_target(player_position.location); |
|
|
|
|
map.make_paths(); |
|
|
|
|
|
|
|
|
|
world.query<Position, EnemyConfig>([&](const auto ent, auto& pos, auto& config) { |
|
|
|
|
config.ai_start = ai::load_state(config.ai_start_name); |
|
|
|
|
config.ai_goal = ai::load_state(config.ai_goal_name); |
|
|
|
|
|
|
|
|
|
ai::set(config.ai_start, "detect_enemy", |
|
|
|
|
map.distance(pos.location) < config.hearing_distance); |
|
|
|
|
if(world.has<EntityAI>(ent)) { |
|
|
|
|
auto&enemy = world.get<EntityAI>(ent); |
|
|
|
|
enemy.set_state("detect_enemy", map.distance(pos.location) < config.hearing_distance); |
|
|
|
|
enemy.update(); |
|
|
|
|
} else { |
|
|
|
|
auto ai_start = ai::load_state(config.ai_start_name); |
|
|
|
|
auto ai_goal = ai::load_state(config.ai_goal_name); |
|
|
|
|
|
|
|
|
|
auto a_plan = ai::plan(config.ai_script, config.ai_start, config.ai_goal); |
|
|
|
|
EntityAI enemy(config.ai_script, ai_start, ai_goal); |
|
|
|
|
enemy.set_state("detect_enemy", map.distance(pos.location) < config.hearing_distance); |
|
|
|
|
enemy.update(); |
|
|
|
|
|
|
|
|
|
ai::dump_script("\n\n\n-----ENEMY SCRIPT", config.ai_start, a_plan.script); |
|
|
|
|
auto action = a_plan.script.front(); |
|
|
|
|
world.set<ai::Action>(ent, action); |
|
|
|
|
ai::dump_script("\n\n\n-----ENEMY SCRIPT", enemy.start, enemy.plan.script); |
|
|
|
|
world.set<EntityAI>(ent, enemy); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -65,13 +99,12 @@ void System::enemy_pathing(GameLevel &level) { |
|
|
|
|
auto player = world.get_the<Player>(); |
|
|
|
|
|
|
|
|
|
const auto &player_position = world.get<Position>(player.entity); |
|
|
|
|
map.set_target(player_position.location); |
|
|
|
|
map.make_paths(); |
|
|
|
|
|
|
|
|
|
world.query<Position, Motion>([&](auto ent, auto &position, auto &motion) { |
|
|
|
|
if(ent != player.entity) { |
|
|
|
|
auto action = world.get_if<ai::Action>(ent); |
|
|
|
|
if(action && (*action).name == "find_enemy") { |
|
|
|
|
auto& action = world.get<EntityAI>(ent); |
|
|
|
|
|
|
|
|
|
if(action.wants_to("find_enemy")) { |
|
|
|
|
Point out = position.location; // copy
|
|
|
|
|
map.neighbors(out, motion.random); |
|
|
|
|
motion = { int(out.x - position.location.x), int(out.y - position.location.y)}; |
|
|
|
@ -154,6 +187,7 @@ void System::death(GameLevel &level, components::ComponentMap& components) { |
|
|
|
|
world.remove<Motion>(ent); |
|
|
|
|
world.remove<Combat>(ent); |
|
|
|
|
world.remove<EnemyConfig>(ent); |
|
|
|
|
world.remove<EntityAI>(ent); |
|
|
|
|
world.remove<Animation>(ent); |
|
|
|
|
|
|
|
|
|
if(auto snd = world.get_if<Sound>(ent)) { |
|
|
|
@ -181,29 +215,32 @@ void System::combat(GameLevel &level) { |
|
|
|
|
// this is guaranteed to not return the given position
|
|
|
|
|
auto [found, nearby] = collider.neighbors(player_position.location); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(found) { |
|
|
|
|
for(auto entity : nearby) { |
|
|
|
|
// AI: process AI combat actions here
|
|
|
|
|
if(world.has<EntityAI>(entity)) { |
|
|
|
|
auto& enemy_ai = world.get<EntityAI>(entity); |
|
|
|
|
enemy_ai.set_state("enemy_found", true); |
|
|
|
|
enemy_ai.update(); |
|
|
|
|
|
|
|
|
|
if(world.has<Combat>(entity)) { |
|
|
|
|
auto& enemy_combat = world.get<Combat>(entity); |
|
|
|
|
if(enemy_ai.wants_to("kill_enemy")) { |
|
|
|
|
auto& enemy_combat = world.get<Combat>(entity); |
|
|
|
|
|
|
|
|
|
Events::Combat result { |
|
|
|
|
player_combat.attack(enemy_combat), |
|
|
|
|
enemy_combat.attack(player_combat) |
|
|
|
|
}; |
|
|
|
|
Events::Combat result { |
|
|
|
|
player_combat.attack(enemy_combat), |
|
|
|
|
enemy_combat.attack(player_combat) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if(world.has<Animation>(entity)) { |
|
|
|
|
auto& animation = world.get<Animation>(entity); |
|
|
|
|
animation.play(); |
|
|
|
|
} |
|
|
|
|
if(world.has<Animation>(entity)) { |
|
|
|
|
auto& animation = world.get<Animation>(entity); |
|
|
|
|
animation.play(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(auto snd = world.get_if<Sound>(entity)) { |
|
|
|
|
sound::play(snd->attack); |
|
|
|
|
} |
|
|
|
|
if(auto snd = world.get_if<Sound>(entity)) { |
|
|
|
|
sound::play(snd->attack); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
world.send<Events::GUI>(Events::GUI::COMBAT, entity, result); |
|
|
|
|
world.send<Events::GUI>(Events::GUI::COMBAT, entity, result); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|