Refactored rituals so they can be used in different situations.
This commit is contained in:
parent
5af9a6664e
commit
da273cbee6
8 changed files with 123 additions and 66 deletions
4
Makefile
4
Makefile
|
@ -22,7 +22,7 @@ tracy_build:
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
./builddir/runtests
|
./builddir/runtests "[combat]"
|
||||||
|
|
||||||
run: build test
|
run: build test
|
||||||
powershell "cp ./builddir/zedcaster.exe ."
|
powershell "cp ./builddir/zedcaster.exe ."
|
||||||
|
@ -41,7 +41,7 @@ clean:
|
||||||
meson compile --clean -C builddir
|
meson compile --clean -C builddir
|
||||||
|
|
||||||
debug_test: build
|
debug_test: build
|
||||||
gdb --nx -x .gdbinit --ex run --args builddir/runtests.exe -e
|
gdb --nx -x .gdbinit --ex run --args builddir/runtests.exe -e "[combat]"
|
||||||
|
|
||||||
win_installer:
|
win_installer:
|
||||||
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" win_installer.ifp'
|
powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" win_installer.ifp'
|
||||||
|
|
5
ai.hpp
5
ai.hpp
|
@ -33,7 +33,6 @@ namespace ai {
|
||||||
|
|
||||||
struct AIManager {
|
struct AIManager {
|
||||||
AIProfile profile;
|
AIProfile profile;
|
||||||
|
|
||||||
std::unordered_map<std::string, Action> actions;
|
std::unordered_map<std::string, Action> actions;
|
||||||
std::unordered_map<std::string, State> states;
|
std::unordered_map<std::string, State> states;
|
||||||
std::unordered_map<std::string, std::vector<Action>> scripts;
|
std::unordered_map<std::string, std::vector<Action>> scripts;
|
||||||
|
@ -43,8 +42,8 @@ namespace ai {
|
||||||
void reset();
|
void reset();
|
||||||
void init(std::string config_path);
|
void init(std::string config_path);
|
||||||
|
|
||||||
Action config_action(nlohmann::json& profile, nlohmann::json& config);
|
Action config_action(AIProfile& profile, nlohmann::json& config);
|
||||||
State config_state(nlohmann::json& profile, nlohmann::json& config);
|
State config_state(AIProfile& profile, nlohmann::json& config);
|
||||||
|
|
||||||
int state_id(std::string name);
|
int state_id(std::string name);
|
||||||
State load_state(std::string state_name);
|
State load_state(std::string state_name);
|
||||||
|
|
|
@ -141,6 +141,7 @@ executable('runtests', sources + [
|
||||||
'tests/matrix.cpp',
|
'tests/matrix.cpp',
|
||||||
'tests/pathing.cpp',
|
'tests/pathing.cpp',
|
||||||
'tests/rituals.cpp',
|
'tests/rituals.cpp',
|
||||||
|
'tests/combat.cpp',
|
||||||
'tests/sound.cpp',
|
'tests/sound.cpp',
|
||||||
'tests/spatialmap.cpp',
|
'tests/spatialmap.cpp',
|
||||||
'tests/animation.cpp',
|
'tests/animation.cpp',
|
||||||
|
|
70
rituals.cpp
70
rituals.cpp
|
@ -1,33 +1,75 @@
|
||||||
#include "rituals.hpp"
|
#include "rituals.hpp"
|
||||||
#include "ai_debug.hpp"
|
#include "ai_debug.hpp"
|
||||||
|
#include "ai.hpp"
|
||||||
|
|
||||||
namespace combat {
|
namespace combat {
|
||||||
void RitualAI::reset() {
|
|
||||||
start = original;
|
RitualEngine::RitualEngine(std::string config_path) :
|
||||||
|
$config(config_path)
|
||||||
|
{
|
||||||
|
$profile = $config["profile"];
|
||||||
|
|
||||||
|
auto& actions = $config["actions"];
|
||||||
|
|
||||||
|
for(auto& ac : actions) {
|
||||||
|
auto action = ai::config_action($profile, ac);
|
||||||
|
$actions.insert_or_assign(action.name, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& [name, sc] : $config["states"].items()) {
|
||||||
|
auto state = ai::config_state($profile, sc);
|
||||||
|
$states.insert_or_assign(name, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& scripts = $config["scripts"];
|
||||||
|
for(auto& [script_name, action_names] : scripts.items()) {
|
||||||
|
std::vector<ai::Action> the_script;
|
||||||
|
for(auto name : action_names) {
|
||||||
|
the_script.push_back($actions.at(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$scripts.insert_or_assign(script_name, the_script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ai::State RitualEngine::load_state(std::string name) {
|
||||||
|
return $states.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ai::Action RitualEngine::load_action(std::string name) {
|
||||||
|
return $actions.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
RitualAI RitualEngine::start() {
|
||||||
|
auto start = load_state("initial");
|
||||||
|
auto goal = load_state("final");
|
||||||
|
return {"actions", start, goal};
|
||||||
|
}
|
||||||
|
|
||||||
|
void RitualEngine::set_state(RitualAI& ritual, std::string name, bool setting) {
|
||||||
|
ritual.start.set($profile.at(name), setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RitualEngine::reset(RitualAI& ritual) {
|
||||||
|
ritual.start = ritual.original;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RitualEngine::plan(RitualAI& ritual) {
|
||||||
|
ritual.plan = ai::plan_actions($scripts.at(ritual.script), ritual.start, ritual.goal);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RitualAI::will_do(std::string name) {
|
bool RitualAI::will_do(std::string name) {
|
||||||
ai::check_valid_action(name, "RitualAI::is_able_to");
|
if(plan.script.size() == 0) return false;
|
||||||
|
|
||||||
return plan.script[0].name == name;
|
return plan.script[0].name == name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RitualAI::set_state(std::string name, bool setting) {
|
|
||||||
ai::set(start, name, setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BUG: I don't like this, maybe an iterator is better?
|
|
||||||
*/
|
|
||||||
ai::Action RitualAI::pop() {
|
ai::Action RitualAI::pop() {
|
||||||
auto result = plan.script.front();
|
auto result = plan.script.front();
|
||||||
plan.script.pop_front();
|
plan.script.pop_front();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RitualAI::update() {
|
|
||||||
plan = ai::plan(script, start, goal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RitualAI::dump() {
|
void RitualAI::dump() {
|
||||||
ai::dump_script(script, start, plan.script);
|
ai::dump_script(script, start, plan.script);
|
||||||
}
|
}
|
||||||
|
|
22
rituals.hpp
22
rituals.hpp
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "goap.hpp"
|
||||||
#include "ai.hpp"
|
#include "ai.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
namespace combat {
|
namespace combat {
|
||||||
struct RitualAI {
|
struct RitualAI {
|
||||||
|
@ -16,11 +18,25 @@ namespace combat {
|
||||||
|
|
||||||
RitualAI() {};
|
RitualAI() {};
|
||||||
|
|
||||||
void reset();
|
|
||||||
bool will_do(std::string name);
|
bool will_do(std::string name);
|
||||||
void set_state(std::string name, bool setting);
|
|
||||||
void update();
|
|
||||||
void dump();
|
void dump();
|
||||||
ai::Action pop();
|
ai::Action pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RitualEngine {
|
||||||
|
Config $config;
|
||||||
|
ai::AIProfile $profile;
|
||||||
|
std::unordered_map<std::string, ai::Action> $actions;
|
||||||
|
std::unordered_map<std::string, ai::State> $states;
|
||||||
|
std::unordered_map<std::string, std::vector<ai::Action>> $scripts;
|
||||||
|
|
||||||
|
RitualEngine(std::string config_path);
|
||||||
|
|
||||||
|
ai::State load_state(std::string name);
|
||||||
|
ai::Action load_action(std::string name);
|
||||||
|
RitualAI start();
|
||||||
|
void reset(RitualAI& ritual);
|
||||||
|
void set_state(RitualAI& ritual, std::string name, bool setting);
|
||||||
|
void plan(RitualAI& ritual);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
21
tests/ai.cpp
21
tests/ai.cpp
|
@ -209,24 +209,3 @@ TEST_CASE("Confirm EntityAI behaves as expected", "[ai]") {
|
||||||
enemy.update();
|
enemy.update();
|
||||||
REQUIRE(enemy.wants_to("run_away"));
|
REQUIRE(enemy.wants_to("run_away"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("confirm that cycles are avoided/detected", "[ai]") {
|
|
||||||
ai::reset();
|
|
||||||
ai::init("tests/cyclic_rituals.json");
|
|
||||||
|
|
||||||
auto start = ai::load_state("initial");
|
|
||||||
auto goal = ai::load_state("final");
|
|
||||||
|
|
||||||
combat::RitualAI ritual("actions", start, goal);
|
|
||||||
ritual.reset();
|
|
||||||
ritual.set_state("has_magick", true);
|
|
||||||
ritual.set_state("cursed_item", true);
|
|
||||||
ritual.set_state("shiny_bauble", true);
|
|
||||||
|
|
||||||
bool it_throws = false;
|
|
||||||
try { ritual.update(); } catch(...) { it_throws = true; }
|
|
||||||
REQUIRE(it_throws);
|
|
||||||
|
|
||||||
fmt::println("\n\n------------ CYCLES AVOIDED");
|
|
||||||
ritual.dump();
|
|
||||||
}
|
|
||||||
|
|
9
tests/combat.cpp
Normal file
9
tests/combat.cpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include "rituals.hpp"
|
||||||
|
|
||||||
|
using namespace combat;
|
||||||
|
|
||||||
|
TEST_CASE("turn based combat engine sorted", "[combat]") {
|
||||||
|
dbc::log("does nothing.");
|
||||||
|
}
|
|
@ -5,24 +5,24 @@
|
||||||
using namespace combat;
|
using namespace combat;
|
||||||
|
|
||||||
TEST_CASE("prototype combat system ideas", "[combat]") {
|
TEST_CASE("prototype combat system ideas", "[combat]") {
|
||||||
ai::reset();
|
RitualEngine re("assets/rituals.json");
|
||||||
ai::init("assets/rituals.json");
|
auto ritual = re.start();
|
||||||
|
|
||||||
auto start = ai::load_state("initial");
|
re.set_state(ritual, "has_spikes", true);
|
||||||
auto goal = ai::load_state("final");
|
re.plan(ritual);
|
||||||
|
|
||||||
RitualAI ritual("actions", start, goal);
|
|
||||||
|
|
||||||
ritual.set_state("has_spikes", true);
|
|
||||||
ritual.update();
|
|
||||||
fmt::println("\n\n------------ TEST WILL DO PIERCE");
|
fmt::println("\n\n------------ TEST WILL DO PIERCE");
|
||||||
ritual.dump();
|
ritual.dump();
|
||||||
REQUIRE(ritual.will_do("pierce_type"));
|
REQUIRE(ritual.will_do("pierce_type"));
|
||||||
|
|
||||||
ritual.reset();
|
REQUIRE(ritual.start != ritual.original);
|
||||||
ritual.set_state("has_magick", true);
|
re.reset(ritual);
|
||||||
ritual.set_state("has_spikes", true);
|
REQUIRE(ritual.start == ritual.original);
|
||||||
ritual.update();
|
|
||||||
|
re.set_state(ritual, "has_magick", true);
|
||||||
|
re.set_state(ritual, "has_spikes", true);
|
||||||
|
re.plan(ritual);
|
||||||
|
|
||||||
fmt::println("\n\n------------ TEST WILL DO MAGICK TOO");
|
fmt::println("\n\n------------ TEST WILL DO MAGICK TOO");
|
||||||
ritual.dump();
|
ritual.dump();
|
||||||
REQUIRE(ritual.will_do("magick_type"));
|
REQUIRE(ritual.will_do("magick_type"));
|
||||||
|
@ -30,20 +30,31 @@ TEST_CASE("prototype combat system ideas", "[combat]") {
|
||||||
ritual.pop();
|
ritual.pop();
|
||||||
REQUIRE(ritual.will_do("pierce_type"));
|
REQUIRE(ritual.will_do("pierce_type"));
|
||||||
|
|
||||||
ritual.reset();
|
re.reset(ritual);
|
||||||
ritual.set_state("has_magick", true);
|
re.set_state(ritual, "has_magick", true);
|
||||||
ritual.set_state("has_spikes", true);
|
re.set_state(ritual, "has_spikes", true);
|
||||||
ritual.set_state("shiny_bauble", true);
|
re.set_state(ritual, "shiny_bauble", true);
|
||||||
ritual.update();
|
re.plan(ritual);
|
||||||
fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST");
|
fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST");
|
||||||
ritual.dump();
|
ritual.dump();
|
||||||
|
|
||||||
ritual.reset();
|
re.reset(ritual);
|
||||||
ritual.set_state("has_magick", true);
|
re.set_state(ritual, "has_magick", true);
|
||||||
ritual.set_state("cursed_item", true);
|
re.set_state(ritual, "cursed_item", true);
|
||||||
ritual.set_state("shiny_bauble", true);
|
re.set_state(ritual, "shiny_bauble", true);
|
||||||
ritual.update();
|
re.plan(ritual);
|
||||||
fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST");
|
fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST");
|
||||||
ritual.dump();
|
ritual.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("confirm that cycles are avoided/detected", "[combat]") {
|
||||||
|
RitualEngine re("assets/rituals.json");
|
||||||
|
auto ritual = re.start();
|
||||||
|
|
||||||
|
re.set_state(ritual, "has_magick", true);
|
||||||
|
re.set_state(ritual, "cursed_item", true);
|
||||||
|
re.set_state(ritual, "shiny_bauble", true);
|
||||||
|
|
||||||
|
fmt::println("\n\n------------ CYCLES AVOIDED");
|
||||||
|
ritual.dump();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue