AI is now mostly working. Enemies will attack the player, and some of them are marked as not tough so they'll run away when they get low health.
This commit is contained in:
parent
c4e01775bc
commit
75db188dc6
6 changed files with 56 additions and 27 deletions
|
@ -8,7 +8,8 @@
|
|||
"in_combat": 5,
|
||||
"have_item": 6,
|
||||
"have_healing": 7,
|
||||
"detect_enemy": 8
|
||||
"detect_enemy": 8,
|
||||
"tough_personality": 9
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
|
@ -28,7 +29,7 @@
|
|||
"name": "kill_enemy",
|
||||
"cost": 5,
|
||||
"needs": {
|
||||
"health_good": true,
|
||||
"tough_personality": true,
|
||||
"no_more_enemies": false,
|
||||
"enemy_found": true,
|
||||
"enemy_dead": false
|
||||
|
@ -66,6 +67,7 @@
|
|||
"name": "run_away",
|
||||
"cost": 0,
|
||||
"needs": {
|
||||
"tough_personality": false,
|
||||
"in_combat": true,
|
||||
"have_healing": false,
|
||||
"health_good": false
|
||||
|
@ -97,6 +99,7 @@
|
|||
},
|
||||
"Enemy::initial_state": {
|
||||
"detect_enemy": false,
|
||||
"tough_personality": true,
|
||||
"enemy_found": false,
|
||||
"enemy_dead": false,
|
||||
"health_good": true,
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
},
|
||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false},
|
||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||
{"_type": "EnemyConfig", "hearing_distance": 5, "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_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": "Sprite", "name": "armored_knight", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
|
||||
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
|
||||
|
@ -33,7 +34,8 @@
|
|||
},
|
||||
{"_type": "Combat", "hp": 40, "max_hp": 40, "damage": 10, "dead": false},
|
||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": true},
|
||||
{"_type": "EnemyConfig", "hearing_distance": 5, "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_type": "Personality", "hearing_distance": 5, "tough": true},
|
||||
{"_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": "Sound", "attack": "Sword_Hit_2", "death": "Ranger_1"}
|
||||
|
@ -47,7 +49,8 @@
|
|||
},
|
||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||
{"_type": "EnemyConfig", "hearing_distance": 10, "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_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": "Sprite", "name": "rat_with_sword", "width": 256, "height": 256, "scale": 1.0},
|
||||
{"_type": "Sound", "attack": "Small_Rat", "death": "Creature_Death_1"}
|
||||
|
@ -61,7 +64,8 @@
|
|||
},
|
||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||
{"_type": "EnemyConfig", "hearing_distance": 10, "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_type": "EnemyAI", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||
{"_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": "Sprite", "name": "hairy_spider", "width": 256, "height": 256, "scale": 1.0},
|
||||
{"_type": "Sound", "attack": "Spider_1", "death": "Spider_2"}
|
||||
|
|
|
@ -18,7 +18,8 @@ namespace components {
|
|||
components::enroll<Position>(component_map);
|
||||
components::enroll<Weapon>(component_map);
|
||||
components::enroll<Curative>(component_map);
|
||||
components::enroll<EnemyConfig>(component_map);
|
||||
components::enroll<EnemyAI>(component_map);
|
||||
components::enroll<Personality>(component_map);
|
||||
components::enroll<Tile>(component_map);
|
||||
components::enroll<Motion>(component_map);
|
||||
components::enroll<LightSource>(component_map);
|
||||
|
|
|
@ -44,8 +44,12 @@ namespace components {
|
|||
Config bosses;
|
||||
};
|
||||
|
||||
struct EnemyConfig {
|
||||
struct Personality {
|
||||
int hearing_distance = 10;
|
||||
bool tough = true;
|
||||
};
|
||||
|
||||
struct EnemyAI {
|
||||
std::string ai_script;
|
||||
std::string ai_start_name;
|
||||
std::string ai_goal_name;
|
||||
|
@ -142,8 +146,8 @@ namespace components {
|
|||
ENROLL_COMPONENT(Weapon, damage);
|
||||
ENROLL_COMPONENT(Loot, amount);
|
||||
ENROLL_COMPONENT(Position, location.x, location.y);
|
||||
ENROLL_COMPONENT(EnemyConfig, hearing_distance,
|
||||
ai_script, ai_start_name, ai_goal_name);
|
||||
ENROLL_COMPONENT(EnemyAI, ai_script, ai_start_name, ai_goal_name);
|
||||
ENROLL_COMPONENT(Personality, hearing_distance, tough);
|
||||
ENROLL_COMPONENT(Motion, dx, dy, random);
|
||||
ENROLL_COMPONENT(Combat, hp, max_hp, damage, dead);
|
||||
ENROLL_COMPONENT(Device, config, events);
|
||||
|
|
30
systems.cpp
30
systems.cpp
|
@ -48,20 +48,24 @@ void System::enemy_ai_initialize(GameLevel &level) {
|
|||
auto &world = *level.world;
|
||||
auto &map = *level.map;
|
||||
|
||||
world.query<Position, EnemyConfig>([&](const auto ent, auto& pos, auto& config) {
|
||||
world.query<Position, EnemyAI>([&](const auto ent, auto& pos, auto& config) {
|
||||
if(world.has<ai::EntityAI>(ent)) {
|
||||
auto&enemy = world.get<ai::EntityAI>(ent);
|
||||
enemy.set_state("detect_enemy", map.distance(pos.location) < config.hearing_distance);
|
||||
auto&personality = world.get<Personality>(ent);
|
||||
|
||||
enemy.set_state("detect_enemy", map.distance(pos.location) < personality.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);
|
||||
|
||||
ai::EntityAI enemy(config.ai_script, ai_start, ai_goal);
|
||||
enemy.set_state("detect_enemy", map.distance(pos.location) < config.hearing_distance);
|
||||
auto&personality = world.get<Personality>(ent);
|
||||
|
||||
enemy.set_state("tough_personality", personality.tough);
|
||||
enemy.set_state("detect_enemy", map.distance(pos.location) < personality.hearing_distance);
|
||||
enemy.update();
|
||||
|
||||
ai::dump_script("\n\n\n-----ENEMY SCRIPT", enemy.start, enemy.plan.script);
|
||||
world.set<ai::EntityAI>(ent, enemy);
|
||||
}
|
||||
});
|
||||
|
@ -77,18 +81,18 @@ void System::enemy_pathing(GameLevel &level) {
|
|||
world.query<Position, Motion>([&](auto ent, auto &position, auto &motion) {
|
||||
if(ent != player.entity) {
|
||||
auto& enemy_ai = world.get<ai::EntityAI>(ent);
|
||||
Point out = position.location; // copy
|
||||
|
||||
if(enemy_ai.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)};
|
||||
map.neighbors(out, motion.random, PATHING_TOWARD);
|
||||
}
|
||||
|
||||
fmt::println("------- ARE THEY SCARED? {}", ent);
|
||||
enemy_ai.dump();
|
||||
if(enemy_ai.wants_to("run_away")) {
|
||||
dbc::log("ENEMY IS SCARED");
|
||||
fmt::println("ENEMY {} wants to run away", ent);
|
||||
map.neighbors(out, motion.random, PATHING_AWAY);
|
||||
}
|
||||
|
||||
motion = { int(out.x - position.location.x), int(out.y - position.location.y)};
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -173,7 +177,8 @@ void System::death(GameLevel &level, components::ComponentMap& components) {
|
|||
// remove their enemy setting
|
||||
world.remove<Motion>(ent);
|
||||
world.remove<Combat>(ent);
|
||||
world.remove<EnemyConfig>(ent);
|
||||
world.remove<EnemyAI>(ent);
|
||||
world.remove<Personality>(ent);
|
||||
world.remove<ai::EntityAI>(ent);
|
||||
world.remove<Animation>(ent);
|
||||
|
||||
|
@ -214,12 +219,13 @@ void System::combat(GameLevel &level) {
|
|||
player_combat.attack(enemy_combat), 0
|
||||
};
|
||||
|
||||
if(!enemy_combat.dead && world.has<ai::EntityAI>(entity)) {
|
||||
if(world.has<ai::EntityAI>(entity)) {
|
||||
auto& enemy_ai = world.get<ai::EntityAI>(entity);
|
||||
enemy_ai.set_state("in_combat", true);
|
||||
enemy_ai.update();
|
||||
}
|
||||
|
||||
enemy_ai.dump();
|
||||
if(enemy_ai.wants_to("kill_enemy")) {
|
||||
result.enemy_did = enemy_combat.attack(player_combat);
|
||||
|
||||
|
|
21
tests/ai.cpp
21
tests/ai.cpp
|
@ -187,14 +187,25 @@ TEST_CASE("Confirm EntityAI behaves as expected", "[ai-enemy]") {
|
|||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("kill_enemy"));
|
||||
|
||||
enemy.set_state("have_item", true);
|
||||
enemy.set_state("have_healing", true);
|
||||
enemy.set_state("in_combat", false);
|
||||
enemy.set_state("health_good", false);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("use_healing"));
|
||||
|
||||
enemy.set_state("have_healing", false);
|
||||
enemy.set_state("tough_personality", true);
|
||||
enemy.set_state("in_combat", true);
|
||||
enemy.set_state("health_good", true);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("kill_enemy"));
|
||||
|
||||
enemy.set_state("have_healing", false);
|
||||
enemy.set_state("tough_personality", false);
|
||||
enemy.set_state("in_combat", true);
|
||||
enemy.set_state("health_good", false);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("run_away"));
|
||||
|
||||
enemy.set_state("have_item", true);
|
||||
enemy.set_state("have_healing", true);
|
||||
enemy.set_state("in_combat", false);
|
||||
enemy.update();
|
||||
REQUIRE(enemy.wants_to("use_healing"));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue