diff --git a/ai.cpp b/ai.cpp index 87b8758..6102618 100644 --- a/ai.cpp +++ b/ai.cpp @@ -27,12 +27,12 @@ namespace ai { fmt::format("config_action: no 'effects' field", result.name)); for(auto& [name_key, value] : config["needs"].items()) { - check(profile.contains(name_key), fmt::format("config_action: profile does not have name {}", result.name, name_key)); + check(profile.contains(name_key), fmt::format("config_action({}): profile does not have need named {}", result.name, name_key)); result.needs(profile.at(name_key), bool(value)); } for(auto& [name_key, value] : config["effects"].items()) { - check(profile.contains(name_key), fmt::format("config_action: profile does not have name {}", result.name, name_key)); + check(profile.contains(name_key), fmt::format("config_action({}): profile does not have effect named {}", result.name, name_key)); result.effect(profile.at(name_key), bool(value)); } diff --git a/assets/ai.json b/assets/ai.json index a194d83..7f53fa2 100644 --- a/assets/ai.json +++ b/assets/ai.json @@ -4,13 +4,17 @@ "enemy_dead": 1, "health_good": 2, "no_more_items": 3, - "no_more_enemies": 4 + "no_more_enemies": 4, + "in_combat": 5, + "have_item": 6, + "have_healing": 7 }, "actions": [ { "name": "find_enemy", "cost": 5, "needs": { + "in_combat": false, "no_more_enemies": false, "health_good": true, "enemy_found": false @@ -45,14 +49,39 @@ }, { "name": "find_healing", - "cost": 5, + "cost": 0, "needs": { + "enemy_found": false, + "in_combat": false, "health_good": false, "no_more_items": false }, "effects": { "health_good": true } + }, + { + "name": "use_item", + "cost": 0, + "needs": { + "have_item": true, + "health_good": true + }, + "effects": { + "have_item": false + } + }, + { + "name": "use_healing", + "cost": 0, + "needs": { + "have_item": true, + "have_healing": true, + "health_good": false + }, + "effects": { + "health_good": true + } } ], "states": { @@ -76,6 +105,8 @@ ["find_enemy", "kill_enemy", "find_healing", - "collect_items"] + "collect_items", + "use_item", + "use_healing"] } } diff --git a/autowalker.cpp b/autowalker.cpp index 063def6..64ead22 100644 --- a/autowalker.cpp +++ b/autowalker.cpp @@ -40,6 +40,19 @@ Pathing compute_paths(gui::FSM& fsm) { return paths; } +void Autowalker::log(std::string msg) { + fmt::println(">>> AUTOWALK: {}", msg); + fsm.$status_ui.log(msg); +} + +void Autowalker::status(std::string msg) { + fsm.$main_ui.$overlay_ui.show_text("bottom", msg); +} + +void Autowalker::close_status() { + fsm.$main_ui.$overlay_ui.close_text("bottom"); +} + Pathing Autowalker::path_to_enemies() { return compute_paths(fsm); } @@ -57,11 +70,13 @@ void Autowalker::window_events() { fsm.$window.handleEvents( [&](const sf::Event::KeyPressed &) { fsm.autowalking = false; - fmt::println("ABORT AUTOWALK"); + close_status(); + log("Aborting autowalk. You can move now."); }, [&](const sf::Event::MouseButtonPressed &) { fsm.autowalking = false; - fmt::println("ABORT AUTOWALK"); + close_status(); + log("Aborting autowalk. You can move now."); } ); } @@ -71,10 +86,8 @@ void Autowalker::process_combat() { || fsm.in_state(gui::State::ATTACKING)) { if(fsm.in_state(gui::State::ATTACKING)) { - fmt::println("In attacking state, sending a TICK"); send_event(gui::Event::TICK); } else { - fmt::println("Not in ATTACK, sending an ATTACK to continue combat."); send_event(gui::Event::ATTACK);; } } @@ -91,14 +104,16 @@ bool Autowalker::path_player(Pathing& paths, Point& target_out) { if(!found) { // failed to find a linear path, try diagonal if(!paths.random_walk(target_out, false, PATHING_TOWARD, MOVE_DIAGONAL)) { - dbc::log("couldn't find a diagonal direction"); + status("PATH FAIL"); + log("Autowalk failed to find a path."); matrix::dump("MOVE FAIL PATHS", paths.$paths, target_out.x, target_out.y); return false; } } if(!fsm.$level.map->can_move(target_out)) { - dbc::log("neighbors is telling me to go to a bad spot."); + status("PATH FAIL"); + log("Autowalk major pathing failure. You can move now."); matrix::dump("BAD TARGET PATHS", paths.$paths, target_out.x, target_out.y); matrix::dump("BAD TARGET MAP", fsm.$level.map->walls(), target_out.x, target_out.y); return false; @@ -133,7 +148,7 @@ void Autowalker::rotate_player(Point current, Point target) { target_facing = 6; } else if(delta_x == 1 && delta_y == -1) { // north east - target_facing = 5; + target_facing = 7; } else if(delta_x == 1 && delta_y == 1) { // south east target_facing = 1; @@ -165,35 +180,12 @@ void Autowalker::rotate_player(Point current, Point target) { "player isn't facing the correct direction"); } -void Autowalker::show_map_overlay(matrix::Matrix& map, Point current) { - auto debug = fsm.$level.world->get_the(); - if(!debug.FPS) { - fsm.$main_ui.$overlay_ui.close_text("top_right"); - return; - } - - std::string map_overlay; - for(matrix::box it{map, current.x, current.y, 6}; it.next();) { - if(it.x == it.left) map_overlay += "\n"; - int cell = map[it.y][it.x]; - - if(it.x == current.x && it.y == current.y) { - map_overlay += fmt::format("{:x}<", cell); - } else if(cell == WALL_PATH_LIMIT) { - map_overlay += fmt::format("# "); - } else if(cell > 15) { - map_overlay += fmt::format("* "); - } else { - map_overlay += fmt::format("{:x} ", cell); - } - } - - fsm.$main_ui.$overlay_ui.show_text("top_right", map_overlay); -} - void Autowalker::autowalk() { window_events(); - if(!fsm.autowalking) return; + if(!fsm.autowalking) { + close_status(); + return; + } int move_attempts = 0; @@ -204,14 +196,17 @@ void Autowalker::autowalk() { int enemy_count = number_left(fsm); int item_count = number_left(fsm); - fmt::println("ENEMY COUNT: {}, ITEM COUNT: {}", enemy_count, item_count); - window_events(); ai::set(start, "no_more_enemies", enemy_count == 0); ai::set(start, "no_more_items", item_count == 0); ai::set(start, "enemy_found", fsm.in_state(gui::State::IN_COMBAT) || fsm.in_state(gui::State::ATTACKING)); + ai::set(start, "health_good", player_health_good()); + ai::set(start, "in_combat", + fsm.in_state(gui::State::IN_COMBAT) || + fsm.in_state(gui::State::ATTACKING)); + ai::set(start, "have_item", player_item_count() > 0); auto a_plan = ai::plan("Walker::actions", start, goal); @@ -219,27 +214,32 @@ void Autowalker::autowalk() { for(auto action : a_plan.script) { if(action.name == "find_enemy") { // this is where to test if enemy found and update state - fmt::println("FINDING AN ENEMY"); + status("FINDING ENEMY"); auto paths = path_to_enemies(); process_move(paths); send_event(gui::Event::ATTACK); + } else if(action.name == "use_item") { + status("USE ITEMS"); } else if(action.name == "kill_enemy") { - fmt::println("KILLING ENEMY"); + status("KILLING ENEMY"); process_combat(); } else if(action.name == "find_healing") { - fmt::println("FINDING HEALING"); + status("FINDING HEALING"); auto paths = path_to_items(); process_move(paths); // do the path to healing thing } else if(action.name == "collect_items") { - fmt::println("COLLECTING ITEMS"); + status("COLLECTING ITEMS"); auto paths = path_to_items(); process_move(paths); // path to the items and get them all } else if(action == ai::FINAL_ACTION) { - fmt::println("END STATE, complete? {}", a_plan.complete); + close_status(); + log("Autowalk done, nothing left to do."); fsm.autowalking = false; } else { + close_status(); + log("Autowalk has a bug. Unknown action."); fmt::println("Unknown action: {}", action.name); } @@ -253,7 +253,8 @@ void Autowalker::process_move(Pathing& paths) { Point target = current; if(!path_player(paths, target)) { - dbc::log("no paths found, aborting autowalk"); + close_status(); + log("No paths found, aborting autowalk. You can move now."); fsm.autowalking = false; return; } @@ -271,6 +272,16 @@ void Autowalker::send_event(gui::Event ev) { fsm.handle_world_events(); } +bool Autowalker::player_health_good() { + auto combat = fsm.$level.world->get(fsm.$level.player); + return float(combat.hp) / float(combat.max_hp) > 0.5f; +} + +int Autowalker::player_item_count() { + auto inventory = fsm.$level.world->get(fsm.$level.player); + return inventory.count(); +} + void Autowalker::start_autowalk() { fsm.autowalking = true; } diff --git a/autowalker.hpp b/autowalker.hpp index aae0381..d645612 100644 --- a/autowalker.hpp +++ b/autowalker.hpp @@ -20,8 +20,13 @@ struct Autowalker { Point get_current_position(); void rotate_player(Point current, Point target); void process_move(Pathing& paths); + void log(std::string msg); + void status(std::string msg); + void close_status(); + bool player_health_good(); + int player_item_count(); + Pathing path_to_enemies(); Pathing path_to_items(); Pathing path_to_devices(); - void show_map_overlay(matrix::Matrix& map, Point current); }; diff --git a/gui_fsm.hpp b/gui_fsm.hpp index 726101a..046b020 100644 --- a/gui_fsm.hpp +++ b/gui_fsm.hpp @@ -8,7 +8,6 @@ #include "main_ui.hpp" #include "combat_ui.hpp" #include "status_ui.hpp" -#include "overlay_ui.hpp" #include "boss_fight_ui.hpp" namespace gui { diff --git a/tests/ai.cpp b/tests/ai.cpp index 0a689d2..4c7a550 100644 --- a/tests/ai.cpp +++ b/tests/ai.cpp @@ -148,6 +148,8 @@ TEST_CASE("ai autowalker ai test", "[ai]") { // health is low, go heal ai::set(result, "health_good", false); + ai::set(result, "in_combat", false); + ai::set(result, "enemy_found", false); REQUIRE(!ai::test(result, "health_good")); auto health_plan = ai::plan("Walker::actions", result, goal);