You can now take damage to your head.
This commit is contained in:
parent
cbd4b858ac
commit
d22eaa554d
7 changed files with 64 additions and 49 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
"foreground": "enemies/fg:player",
|
"foreground": "enemies/fg:player",
|
||||||
"background": "color:transparent"
|
"background": "color:transparent"
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 200, "max_hp": 200, "ap": 0, "max_ap": 12, "ap_delta": 6, "damage": 50, "dead": false},
|
{"_type": "Combat", "ap": 0, "max_ap": 12, "ap_delta": 6, "damage": 50, "dead": false},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||||
{"_type": "Collision", "has": true},
|
{"_type": "Collision", "has": true},
|
||||||
{"_type": "EnemyConfig", "ai_script": "Host::actions", "ai_start_name": "Host::initial_state", "ai_goal_name": "Host::final_state"},
|
{"_type": "EnemyConfig", "ai_script": "Host::actions", "ai_start_name": "Host::initial_state", "ai_goal_name": "Host::final_state"},
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
"foreground": "enemies/fg:rat_giant",
|
"foreground": "enemies/fg:rat_giant",
|
||||||
"background": "color:transparent"
|
"background": "color:transparent"
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 50, "max_hp": 50, "ap": 0, "max_ap": 12, "ap_delta": 6,"damage": 2, "dead": false},
|
{"_type": "Combat", "ap": 0, "max_ap": 12, "ap_delta": 6,"damage": 2, "dead": false},
|
||||||
{"_type": "Collision", "has": true},
|
{"_type": "Collision", "has": true},
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
||||||
{"_type": "EnemyConfig", "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"},
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,30 @@ namespace components {
|
||||||
|
|
||||||
if(attack) {
|
if(attack) {
|
||||||
my_dmg = Random::uniform<int>(1, damage);
|
my_dmg = Random::uniform<int>(1, damage);
|
||||||
target.hp -= my_dmg;
|
target.hit_limb(my_dmg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return my_dmg;
|
return my_dmg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Combat::hit_limb(int my_dmg) {
|
||||||
|
body_parts["head"] -= my_dmg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Combat::is_dead() {
|
||||||
|
return body_parts["head"] < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Combat::almost_dead() {
|
||||||
|
return body_parts["head"] < 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Combat::can_heal() {
|
||||||
|
return body_parts["head"] < 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Combat::apply_healing(Curative& cure) {
|
||||||
|
int new_hp = body_parts["head"] + cure.hp;
|
||||||
|
body_parts["head"] = std::min(new_hp, 50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,10 +83,6 @@ namespace components {
|
||||||
std::string ai_goal_name;
|
std::string ai_goal_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Curative {
|
|
||||||
int hp = 10;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
string name;
|
string name;
|
||||||
float scale;
|
float scale;
|
||||||
|
|
@ -106,12 +102,17 @@ namespace components {
|
||||||
std::vector<std::array<std::string, 4>> beats;
|
std::vector<std::array<std::string, 4>> beats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Curative {
|
||||||
|
int hp = 10;
|
||||||
|
};
|
||||||
|
|
||||||
struct Combat {
|
struct Combat {
|
||||||
int hp;
|
|
||||||
int max_hp;
|
|
||||||
int ap_delta;
|
int ap_delta;
|
||||||
int max_ap;
|
int max_ap;
|
||||||
int damage;
|
int damage;
|
||||||
|
std::unordered_map<std::string, int> body_parts{
|
||||||
|
{"head", 50},
|
||||||
|
};
|
||||||
|
|
||||||
// everyone starts at 0 but ap_delta is added each round
|
// everyone starts at 0 but ap_delta is added each round
|
||||||
int ap = 0;
|
int ap = 0;
|
||||||
|
|
@ -120,6 +121,11 @@ namespace components {
|
||||||
bool dead = false;
|
bool dead = false;
|
||||||
|
|
||||||
int attack(Combat &target);
|
int attack(Combat &target);
|
||||||
|
void hit_limb(int my_dmg);
|
||||||
|
bool is_dead();
|
||||||
|
bool almost_dead();
|
||||||
|
bool can_heal();
|
||||||
|
void apply_healing(Curative& cure);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LightSource {
|
struct LightSource {
|
||||||
|
|
@ -156,7 +162,7 @@ namespace components {
|
||||||
ENROLL_COMPONENT(EnemyConfig, 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, ap_delta, max_ap, damage, dead);
|
ENROLL_COMPONENT(Combat, ap_delta, max_ap, damage, dead);
|
||||||
ENROLL_COMPONENT(Device, config, events);
|
ENROLL_COMPONENT(Device, config, events);
|
||||||
ENROLL_COMPONENT(Storyboard, image, audio, layout, beats);
|
ENROLL_COMPONENT(Storyboard, image, audio, layout, beats);
|
||||||
ENROLL_COMPONENT(Sound, attack, death);
|
ENROLL_COMPONENT(Sound, attack, death);
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ void System::death() {
|
||||||
|
|
||||||
world.query<Combat>([&](auto ent, auto &combat) {
|
world.query<Combat>([&](auto ent, auto &combat) {
|
||||||
// bring out yer dead
|
// bring out yer dead
|
||||||
if(combat.hp <= 0 && !combat.dead) {
|
if(combat.is_dead() && !combat.dead) {
|
||||||
combat.dead = true;
|
combat.dead = true;
|
||||||
if(ent != player.entity) {
|
if(ent != player.entity) {
|
||||||
// we won't change out the player's components later
|
// we won't change out the player's components later
|
||||||
|
|
@ -196,7 +196,7 @@ void System::death() {
|
||||||
}
|
}
|
||||||
// we need to send this event for everything that dies
|
// we need to send this event for everything that dies
|
||||||
world.send<game::Event>(game::Event::DEATH, ent, {});
|
world.send<game::Event>(game::Event::DEATH, ent, {});
|
||||||
} else if(float(combat.hp) / float(combat.max_hp) < 0.5f) {
|
} else if(combat.almost_dead()) {
|
||||||
// if enemies are below 50% health they are marked with bad health
|
// 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);
|
||||||
|
|
@ -479,23 +479,14 @@ void System::use_item(const string& slot_name) {
|
||||||
auto& inventory = world.get<inventory::Model>(level.player);
|
auto& inventory = world.get<inventory::Model>(level.player);
|
||||||
auto& player_combat = world.get<Combat>(level.player);
|
auto& player_combat = world.get<Combat>(level.player);
|
||||||
|
|
||||||
if(player_combat.hp >= player_combat.max_hp) return;
|
if(!player_combat.can_heal()) return;
|
||||||
if(!inventory.has(slot_name)) return;
|
if(!inventory.has(slot_name)) return;
|
||||||
|
|
||||||
auto what = inventory.get(slot_name);
|
auto what = inventory.get(slot_name);
|
||||||
|
|
||||||
if(auto curative = world.get_if<Curative>(what)) {
|
if(auto curative = world.get_if<Curative>(what)) {
|
||||||
inventory.remove(what);
|
inventory.remove(what);
|
||||||
|
player_combat.apply_healing(*curative);
|
||||||
player_combat.hp += curative->hp;
|
|
||||||
|
|
||||||
if(player_combat.hp > player_combat.max_hp) {
|
|
||||||
player_combat.hp = player_combat.max_hp;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbc::log($F("player health now {}",
|
|
||||||
player_combat.hp));
|
|
||||||
|
|
||||||
world.remove<Curative>(what);
|
world.remove<Curative>(what);
|
||||||
} else {
|
} else {
|
||||||
dbc::log($F("no usable item at {}", what));
|
dbc::log($F("no usable item at {}", what));
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@ namespace gui {
|
||||||
void BodyUI::init(size_t x, size_t y, size_t width, size_t height) {
|
void BodyUI::init(size_t x, size_t y, size_t width, size_t height) {
|
||||||
$gui.position(x, y, width, height);
|
$gui.position(x, y, width, height);
|
||||||
$gui.layout(
|
$gui.layout(
|
||||||
"[body_head]"
|
"[head]"
|
||||||
"[body_chest]"
|
"[chest]"
|
||||||
"[body_right_arm]"
|
"[right_arm]"
|
||||||
"[body_left_arm]"
|
"[left_arm]"
|
||||||
"[body_stomach]"
|
"[stomach]"
|
||||||
"[body_left_leg]"
|
"[left_leg]"
|
||||||
"[body_right_leg]");
|
"[right_leg]");
|
||||||
|
|
||||||
$gui.set<Background>($gui.MAIN, {$gui.$parser, });
|
$gui.set<Background>($gui.MAIN, {$gui.$parser, });
|
||||||
|
|
||||||
|
|
@ -42,7 +42,16 @@ namespace gui {
|
||||||
|
|
||||||
void BodyUI::update() {
|
void BodyUI::update() {
|
||||||
auto world = GameDB::current_world();
|
auto world = GameDB::current_world();
|
||||||
auto player = world->get_the<components::Player>();
|
auto& player = world->get_the<components::Player>();
|
||||||
|
auto& player_combat = world->get<components::Combat>(player.entity);
|
||||||
|
|
||||||
|
for(auto& [key, value] : player_combat.body_parts) {
|
||||||
|
auto gui_id = $gui.entity(key);
|
||||||
|
|
||||||
|
if(auto meter = $gui.get_if<Meter>(gui_id)) {
|
||||||
|
meter->percent = float(value) / 50.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BodyUI::render(sf::RenderWindow &window) {
|
void BodyUI::render(sf::RenderWindow &window) {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ namespace gui {
|
||||||
auto map = level.map;
|
auto map = level.map;
|
||||||
|
|
||||||
std::wstring stats = $F(L"STATS\n"
|
std::wstring stats = $F(L"STATS\n"
|
||||||
L"HP: {}\n"
|
|
||||||
L"mean:{:>8.5}\n"
|
L"mean:{:>8.5}\n"
|
||||||
L"sdev: {:>8.5}\n"
|
L"sdev: {:>8.5}\n"
|
||||||
L"min: {:>8.5}\n"
|
L"min: {:>8.5}\n"
|
||||||
|
|
@ -58,7 +57,7 @@ namespace gui {
|
||||||
L"VSync? {}\n"
|
L"VSync? {}\n"
|
||||||
L"FR Limit: {}\n"
|
L"FR Limit: {}\n"
|
||||||
L"Debug? {}\n\n",
|
L"Debug? {}\n\n",
|
||||||
player_combat.hp, $stats.mean(), $stats.stddev(), $stats.min,
|
$stats.mean(), $stats.stddev(), $stats.min,
|
||||||
$stats.max, $stats.n, level.index, map->width(), map->height(),
|
$stats.max, $stats.n, level.index, map->width(), map->height(),
|
||||||
VSYNC, FRAME_LIMIT, DEBUG_BUILD);
|
VSYNC, FRAME_LIMIT, DEBUG_BUILD);
|
||||||
|
|
||||||
|
|
@ -74,9 +73,6 @@ namespace gui {
|
||||||
if(active) {
|
if(active) {
|
||||||
auto& level = GameDB::current_level();
|
auto& level = GameDB::current_level();
|
||||||
// it's on now, enable things
|
// it's on now, enable things
|
||||||
auto player = level.world->get_the<components::Player>();
|
|
||||||
auto& player_combat = level.world->get<components::Combat>(player.entity);
|
|
||||||
player_combat.hp = player_combat.max_hp;
|
|
||||||
$gui.show_text("debug_text", L"STATS");
|
$gui.show_text("debug_text", L"STATS");
|
||||||
} else {
|
} else {
|
||||||
// it's off now, close it
|
// it's off now, close it
|
||||||
|
|
|
||||||
|
|
@ -25,20 +25,17 @@ TEST_CASE("battle operations fantasy", "[combat-battle]") {
|
||||||
|
|
||||||
DinkyECS::Entity host = 0;
|
DinkyECS::Entity host = 0;
|
||||||
ai::EntityAI host_ai("Host::actions", host_start, host_goal);
|
ai::EntityAI host_ai("Host::actions", host_start, host_goal);
|
||||||
components::Combat host_combat{
|
components::Combat host_combat{.ap_delta=6, .max_ap=12, .damage=20};
|
||||||
.hp=100, .max_hp=100, .ap_delta=6, .max_ap=12, .damage=20};
|
|
||||||
battle.add_enemy({host, &host_ai, &host_combat, true});
|
battle.add_enemy({host, &host_ai, &host_combat, true});
|
||||||
|
|
||||||
DinkyECS::Entity axe_ranger = 1;
|
DinkyECS::Entity axe_ranger = 1;
|
||||||
ai::EntityAI axe_ai("Enemy::actions", ai_start, ai_goal);
|
ai::EntityAI axe_ai("Enemy::actions", ai_start, ai_goal);
|
||||||
components::Combat axe_combat{
|
components::Combat axe_combat{.ap_delta=8, .max_ap=12, .damage=20};
|
||||||
.hp=20, .max_hp=20, .ap_delta=8, .max_ap=12, .damage=20};
|
|
||||||
battle.add_enemy({axe_ranger, &axe_ai, &axe_combat});
|
battle.add_enemy({axe_ranger, &axe_ai, &axe_combat});
|
||||||
|
|
||||||
DinkyECS::Entity rat = 2;
|
DinkyECS::Entity rat = 2;
|
||||||
ai::EntityAI rat_ai("Enemy::actions", ai_start, ai_goal);
|
ai::EntityAI rat_ai("Enemy::actions", ai_start, ai_goal);
|
||||||
components::Combat rat_combat{
|
components::Combat rat_combat{.ap_delta=2, .max_ap=10, .damage=10};
|
||||||
.hp=10, .max_hp=10, .ap_delta=2, .max_ap=10, .damage=10};
|
|
||||||
battle.add_enemy({rat, &rat_ai, &rat_combat});
|
battle.add_enemy({rat, &rat_ai, &rat_combat});
|
||||||
|
|
||||||
battle.set_all("enemy_found", true);
|
battle.set_all("enemy_found", true);
|
||||||
|
|
@ -50,8 +47,8 @@ TEST_CASE("battle operations fantasy", "[combat-battle]") {
|
||||||
battle.set(host, "have_healing", false);
|
battle.set(host, "have_healing", false);
|
||||||
battle.set(host, "tough_personality", false);
|
battle.set(host, "tough_personality", false);
|
||||||
|
|
||||||
while(host_combat.hp > 0) {
|
while(!host_combat.is_dead()) {
|
||||||
battle.set(host, "health_good", host_combat.hp > 20);
|
battle.set(host, "health_good", host_combat.almost_dead());
|
||||||
|
|
||||||
battle.player_request("use_healing");
|
battle.player_request("use_healing");
|
||||||
battle.player_request("kill_enemy");
|
battle.player_request("kill_enemy");
|
||||||
|
|
@ -62,11 +59,6 @@ TEST_CASE("battle operations fantasy", "[combat-battle]") {
|
||||||
while(auto act = battle.next()) {
|
while(auto act = battle.next()) {
|
||||||
auto& [enemy, wants_to, cost, enemy_state] = *act;
|
auto& [enemy, wants_to, cost, enemy_state] = *act;
|
||||||
|
|
||||||
// fmt::println(">>>>> entity: {} wants to {} cost={}; has {} HP; {} ap",
|
|
||||||
// enemy.entity, wants_to,
|
|
||||||
// cost, enemy.combat->hp,
|
|
||||||
// enemy.combat->ap);
|
|
||||||
|
|
||||||
switch(enemy_state) {
|
switch(enemy_state) {
|
||||||
case BattleHostState::agree:
|
case BattleHostState::agree:
|
||||||
// fmt::println("HOST and PLAYER requests match {}, doing it.", wants_to);
|
// fmt::println("HOST and PLAYER requests match {}, doing it.", wants_to);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue