Right before coverage destroys everything.
This commit is contained in:
parent
1d2968f826
commit
c4611c0138
9 changed files with 54 additions and 46 deletions
3
Makefile
3
Makefile
|
@ -45,3 +45,6 @@ debug_test: build
|
||||||
|
|
||||||
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'
|
||||||
|
|
||||||
|
coverage_report:
|
||||||
|
echo "test"
|
||||||
|
|
6
ai.cpp
6
ai.cpp
|
@ -18,7 +18,6 @@ namespace ai {
|
||||||
Action config_action(AIProfile& profile, nlohmann::json& config) {
|
Action config_action(AIProfile& profile, nlohmann::json& config) {
|
||||||
check(config.contains("name"), "config_action: action config missing name");
|
check(config.contains("name"), "config_action: action config missing name");
|
||||||
check(config.contains("cost"), "config_action: action config missing cost");
|
check(config.contains("cost"), "config_action: action config missing cost");
|
||||||
// check(config["cost"] < STATE_MAX, "config_action: action cost is greater than STATE_MAX");
|
|
||||||
|
|
||||||
Action result(config["name"], config["cost"]);
|
Action result(config["name"], config["cost"]);
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@ namespace ai {
|
||||||
AIMGR.actions.clear();
|
AIMGR.actions.clear();
|
||||||
AIMGR.states.clear();
|
AIMGR.states.clear();
|
||||||
AIMGR.scripts.clear();
|
AIMGR.scripts.clear();
|
||||||
AIMGR.profile = R"({})"_json;
|
AIMGR.profile = json({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(std::string config_path) {
|
void init(std::string config_path) {
|
||||||
|
@ -140,6 +139,9 @@ namespace ai {
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionPlan plan(std::string script_name, State start, State goal) {
|
ActionPlan plan(std::string script_name, State start, State goal) {
|
||||||
|
// BUG: could probably memoize here, since:
|
||||||
|
// same script+same start+same goal will/should produce the same results
|
||||||
|
|
||||||
check(initialized, "you forgot to initialize the AI first.");
|
check(initialized, "you forgot to initialize the AI first.");
|
||||||
auto script = load_script(script_name);
|
auto script = load_script(script_name);
|
||||||
return plan_actions(script, start, goal);
|
return plan_actions(script, start, goal);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false},
|
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
||||||
{"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false},
|
{"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false},
|
||||||
{"_type": "Sprite", "name": "armored_knight", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "armored_knight", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 40, "max_hp": 40, "damage": 10, "dead": false},
|
{"_type": "Combat", "hp": 40, "max_hp": 40, "damage": 10, "dead": false},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": true},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": true},
|
||||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
||||||
{"_type": "Sprite", "name": "axe_ranger", "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "axe_ranger", "width": 256, "height": 256, "scale": 1.0},
|
||||||
{"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": false, "frames": 2, "speed": 0.6, "stationary": false},
|
{"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": false, "frames": 2, "speed": 0.6, "stationary": false},
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
||||||
{"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false},
|
{"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false},
|
||||||
{"_type": "Sprite", "name": "rat_with_sword", "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "rat_with_sword", "width": 256, "height": 256, "scale": 1.0},
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
||||||
{"_type": "Animation", "easing": 2, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false},
|
{"_type": "Animation", "easing": 2, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false},
|
||||||
{"_type": "Sprite", "name": "hairy_spider", "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "hairy_spider", "width": 256, "height": 256, "scale": 1.0},
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace components {
|
||||||
components::enroll<Position>(component_map);
|
components::enroll<Position>(component_map);
|
||||||
components::enroll<Weapon>(component_map);
|
components::enroll<Weapon>(component_map);
|
||||||
components::enroll<Curative>(component_map);
|
components::enroll<Curative>(component_map);
|
||||||
components::enroll<EnemyAI>(component_map);
|
components::enroll<EnemyConfig>(component_map);
|
||||||
components::enroll<Personality>(component_map);
|
components::enroll<Personality>(component_map);
|
||||||
components::enroll<Tile>(component_map);
|
components::enroll<Tile>(component_map);
|
||||||
components::enroll<Motion>(component_map);
|
components::enroll<Motion>(component_map);
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace components {
|
||||||
bool tough = true;
|
bool tough = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EnemyAI {
|
struct EnemyConfig {
|
||||||
std::string ai_script;
|
std::string ai_script;
|
||||||
std::string ai_start_name;
|
std::string ai_start_name;
|
||||||
std::string ai_goal_name;
|
std::string ai_goal_name;
|
||||||
|
@ -146,7 +146,7 @@ namespace components {
|
||||||
ENROLL_COMPONENT(Weapon, damage);
|
ENROLL_COMPONENT(Weapon, damage);
|
||||||
ENROLL_COMPONENT(Loot, amount);
|
ENROLL_COMPONENT(Loot, amount);
|
||||||
ENROLL_COMPONENT(Position, location.x, location.y);
|
ENROLL_COMPONENT(Position, location.x, location.y);
|
||||||
ENROLL_COMPONENT(EnemyAI, ai_script, ai_start_name, ai_goal_name);
|
ENROLL_COMPONENT(EnemyConfig, ai_script, ai_start_name, ai_goal_name);
|
||||||
ENROLL_COMPONENT(Personality, hearing_distance, tough);
|
ENROLL_COMPONENT(Personality, hearing_distance, tough);
|
||||||
ENROLL_COMPONENT(Motion, dx, dy, random);
|
ENROLL_COMPONENT(Motion, dx, dy, random);
|
||||||
ENROLL_COMPONENT(Combat, hp, max_hp, damage, dead);
|
ENROLL_COMPONENT(Combat, hp, max_hp, damage, dead);
|
||||||
|
|
|
@ -15,6 +15,15 @@ namespace combat {
|
||||||
ai::set(start, name, setting);
|
ai::set(start, name, setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BUG: I don't like this, maybe an iterator is better?
|
||||||
|
*/
|
||||||
|
ai::Action RitualAI::pop() {
|
||||||
|
auto result = plan.script.front();
|
||||||
|
plan.script.pop_front();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void RitualAI::update() {
|
void RitualAI::update() {
|
||||||
plan = ai::plan(script, start, goal);
|
plan = ai::plan(script, start, goal);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,5 +21,6 @@ namespace combat {
|
||||||
void set_state(std::string name, bool setting);
|
void set_state(std::string name, bool setting);
|
||||||
void update();
|
void update();
|
||||||
void dump();
|
void dump();
|
||||||
|
ai::Action pop();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
65
systems.cpp
65
systems.cpp
|
@ -48,10 +48,10 @@ void System::enemy_ai_initialize(GameLevel &level) {
|
||||||
auto &world = *level.world;
|
auto &world = *level.world;
|
||||||
auto &map = *level.map;
|
auto &map = *level.map;
|
||||||
|
|
||||||
world.query<Position, EnemyAI>([&](const auto ent, auto& pos, auto& config) {
|
world.query<Position, EnemyConfig>([&](const auto ent, auto& pos, auto& config) {
|
||||||
if(world.has<ai::EntityAI>(ent)) {
|
if(world.has<ai::EntityAI>(ent)) {
|
||||||
auto&enemy = world.get<ai::EntityAI>(ent);
|
auto& enemy = world.get<ai::EntityAI>(ent);
|
||||||
auto&personality = world.get<Personality>(ent);
|
auto& personality = world.get<Personality>(ent);
|
||||||
|
|
||||||
enemy.set_state("detect_enemy", map.distance(pos.location) < personality.hearing_distance);
|
enemy.set_state("detect_enemy", map.distance(pos.location) < personality.hearing_distance);
|
||||||
enemy.update();
|
enemy.update();
|
||||||
|
@ -85,10 +85,7 @@ void System::enemy_pathing(GameLevel &level) {
|
||||||
|
|
||||||
if(enemy_ai.wants_to("find_enemy")) {
|
if(enemy_ai.wants_to("find_enemy")) {
|
||||||
map.neighbors(out, motion.random, PATHING_TOWARD);
|
map.neighbors(out, motion.random, PATHING_TOWARD);
|
||||||
}
|
} else if(enemy_ai.wants_to("run_away")) {
|
||||||
|
|
||||||
if(enemy_ai.wants_to("run_away")) {
|
|
||||||
fmt::println("ENEMY {} wants to run away", ent);
|
|
||||||
map.neighbors(out, motion.random, PATHING_AWAY);
|
map.neighbors(out, motion.random, PATHING_AWAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +97,6 @@ void System::enemy_pathing(GameLevel &level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init_positions(DinkyECS::World &world, SpatialMap &collider) {
|
void System::init_positions(DinkyECS::World &world, SpatialMap &collider) {
|
||||||
// BUG: instead of separate things maybe just one
|
|
||||||
// BUG: Collision component that references what is collide
|
|
||||||
world.query<Position>([&](auto ent, auto &pos) {
|
world.query<Position>([&](auto ent, auto &pos) {
|
||||||
if(world.has<Combat>(ent)) {
|
if(world.has<Combat>(ent)) {
|
||||||
const auto& combat = world.get<Combat>(ent);
|
const auto& combat = world.get<Combat>(ent);
|
||||||
|
@ -132,14 +127,11 @@ inline void move_entity(SpatialMap &collider, Map &game_map, Position &position,
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::motion(GameLevel &level) {
|
void System::motion(GameLevel &level) {
|
||||||
auto &map = *level.map;
|
level.world->query<Position, Motion>(
|
||||||
auto &world = *level.world;
|
[&](auto ent, auto &position, auto &motion) {
|
||||||
auto &collider = *level.collision;
|
|
||||||
|
|
||||||
world.query<Position, Motion>([&](auto ent, auto &position, auto &motion) {
|
|
||||||
// don't process entities that don't move
|
// don't process entities that don't move
|
||||||
if(motion.dx != 0 || motion.dy != 0) {
|
if(motion.dx != 0 || motion.dy != 0) {
|
||||||
move_entity(collider, map, position, motion, ent);
|
move_entity(*level.collision, *level.map, position, motion, ent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -161,7 +153,7 @@ void System::death(GameLevel &level, components::ComponentMap& components) {
|
||||||
// we need to send this event for everything that dies
|
// we need to send this event for everything that dies
|
||||||
world.send<Events::GUI>(Events::GUI::DEATH, ent, {});
|
world.send<Events::GUI>(Events::GUI::DEATH, ent, {});
|
||||||
} else if(float(combat.hp) / float(combat.max_hp) < 0.5f) {
|
} else if(float(combat.hp) / float(combat.max_hp) < 0.5f) {
|
||||||
fmt::println("ENEMY HP low: {}/{}", combat.hp, combat.max_hp);
|
// if enemies are below 50% health they are marked with bad health
|
||||||
if(world.has<ai::EntityAI>(ent)) {
|
if(world.has<ai::EntityAI>(ent)) {
|
||||||
auto& enemy_ai = world.get<ai::EntityAI>(ent);
|
auto& enemy_ai = world.get<ai::EntityAI>(ent);
|
||||||
enemy_ai.set_state("health_good", false);
|
enemy_ai.set_state("health_good", false);
|
||||||
|
@ -177,7 +169,7 @@ void System::death(GameLevel &level, components::ComponentMap& components) {
|
||||||
// remove their enemy setting
|
// remove their enemy setting
|
||||||
world.remove<Motion>(ent);
|
world.remove<Motion>(ent);
|
||||||
world.remove<Combat>(ent);
|
world.remove<Combat>(ent);
|
||||||
world.remove<EnemyAI>(ent);
|
world.remove<EnemyConfig>(ent);
|
||||||
world.remove<Personality>(ent);
|
world.remove<Personality>(ent);
|
||||||
world.remove<ai::EntityAI>(ent);
|
world.remove<ai::EntityAI>(ent);
|
||||||
world.remove<Animation>(ent);
|
world.remove<Animation>(ent);
|
||||||
|
@ -196,6 +188,17 @@ void System::death(GameLevel &level, components::ComponentMap& components) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void animate_entity(DinkyECS::World &world, DinkyECS::Entity entity) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void System::combat(GameLevel &level) {
|
void System::combat(GameLevel &level) {
|
||||||
auto &collider = *level.collision;
|
auto &collider = *level.collision;
|
||||||
auto &world = *level.world;
|
auto &world = *level.world;
|
||||||
|
@ -211,32 +214,19 @@ void System::combat(GameLevel &level) {
|
||||||
for(auto entity : nearby) {
|
for(auto entity : nearby) {
|
||||||
if(world.has<ai::EntityAI>(entity)) {
|
if(world.has<ai::EntityAI>(entity)) {
|
||||||
auto& enemy_ai = world.get<ai::EntityAI>(entity);
|
auto& enemy_ai = world.get<ai::EntityAI>(entity);
|
||||||
enemy_ai.set_state("enemy_found", true);
|
|
||||||
enemy_ai.update();
|
|
||||||
auto& enemy_combat = world.get<Combat>(entity);
|
auto& enemy_combat = world.get<Combat>(entity);
|
||||||
|
|
||||||
Events::Combat result {
|
Events::Combat result {
|
||||||
player_combat.attack(enemy_combat), 0
|
player_combat.attack(enemy_combat), 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if(world.has<ai::EntityAI>(entity)) {
|
enemy_ai.set_state("enemy_found", true);
|
||||||
auto& enemy_ai = world.get<ai::EntityAI>(entity);
|
enemy_ai.set_state("in_combat", true);
|
||||||
enemy_ai.set_state("in_combat", true);
|
enemy_ai.update();
|
||||||
enemy_ai.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
enemy_ai.dump();
|
|
||||||
if(enemy_ai.wants_to("kill_enemy")) {
|
if(enemy_ai.wants_to("kill_enemy")) {
|
||||||
result.enemy_did = enemy_combat.attack(player_combat);
|
result.enemy_did = enemy_combat.attack(player_combat);
|
||||||
|
animate_entity(world, entity);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
world.send<Events::GUI>(Events::GUI::COMBAT, entity, result);
|
world.send<Events::GUI>(Events::GUI::COMBAT, entity, result);
|
||||||
|
@ -258,8 +248,6 @@ void System::collision(GameLevel &level) {
|
||||||
int combat_count = 0;
|
int combat_count = 0;
|
||||||
|
|
||||||
// AI: I think also this would a possible place to run AI decisions
|
// AI: I think also this would a possible place to run AI decisions
|
||||||
|
|
||||||
// BUG: this logic is garbage, needs a refactor
|
|
||||||
for(auto entity : nearby) {
|
for(auto entity : nearby) {
|
||||||
if(world.has<Combat>(entity)) {
|
if(world.has<Combat>(entity)) {
|
||||||
auto combat = world.get<Combat>(entity);
|
auto combat = world.get<Combat>(entity);
|
||||||
|
@ -268,6 +256,8 @@ void System::collision(GameLevel &level) {
|
||||||
world.send<Events::GUI>(Events::GUI::COMBAT_START, entity, entity);
|
world.send<Events::GUI>(Events::GUI::COMBAT_START, entity, entity);
|
||||||
}
|
}
|
||||||
} else if(world.has<InventoryItem>(entity)) {
|
} else if(world.has<InventoryItem>(entity)) {
|
||||||
|
// BUG: this should really be part of the inventory API and I just
|
||||||
|
// call into that to work it, rather than this hard coded crap
|
||||||
auto item = world.get<InventoryItem>(entity);
|
auto item = world.get<InventoryItem>(entity);
|
||||||
auto& item_pos = world.get<Position>(entity);
|
auto& item_pos = world.get<Position>(entity);
|
||||||
auto& inventory = world.get<Inventory>(player.entity);
|
auto& inventory = world.get<Inventory>(player.entity);
|
||||||
|
@ -309,6 +299,7 @@ void System::collision(GameLevel &level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(combat_count == 0) {
|
if(combat_count == 0) {
|
||||||
|
// BUG: this is probably how we get stuck in combat
|
||||||
world.send<Events::GUI>(Events::GUI::NO_NEIGHBORS, player.entity, player.entity);
|
world.send<Events::GUI>(Events::GUI::NO_NEIGHBORS, player.entity, player.entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,6 +332,8 @@ void System::plan_motion(DinkyECS::World& world, Point move_to) {
|
||||||
/*
|
/*
|
||||||
* This one is called inside the MapViewUI very often so
|
* This one is called inside the MapViewUI very often so
|
||||||
* just avoide GameMap unlike the others.
|
* just avoide GameMap unlike the others.
|
||||||
|
*
|
||||||
|
* BUG: Just get rid of this and use the new UI to do the map
|
||||||
*/
|
*/
|
||||||
void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) {
|
void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) {
|
||||||
auto &tiles = map.tiles();
|
auto &tiles = map.tiles();
|
||||||
|
|
|
@ -27,7 +27,7 @@ TEST_CASE("prototype combat system ideas", "[combat]") {
|
||||||
ritual.dump();
|
ritual.dump();
|
||||||
REQUIRE(ritual.will_do("magick_type"));
|
REQUIRE(ritual.will_do("magick_type"));
|
||||||
|
|
||||||
ritual.plan.script.pop_front();
|
ritual.pop();
|
||||||
REQUIRE(ritual.will_do("pierce_type"));
|
REQUIRE(ritual.will_do("pierce_type"));
|
||||||
|
|
||||||
ritual.reset();
|
ritual.reset();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue