From c6489d36f6de7464c555f04d5d4324f50fa6c51f Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Mon, 18 Dec 2017 17:33:49 +0000 Subject: Start experimenting with fear. --- assets/units/soldier/body_panic_0.png | Bin 0 -> 1883 bytes assets/units/soldier/body_panic_1.png | Bin 0 -> 1828 bytes src/common.hpp | 2 - src/game/assets.cpp | 1 + src/game/game.cpp | 31 ++------ src/game/game.hpp | 24 +++++-- src/game/text.cpp | 24 +++++++ src/game/units.cpp | 129 +++++++++++++++++++++++++++++++--- 8 files changed, 167 insertions(+), 44 deletions(-) create mode 100644 assets/units/soldier/body_panic_0.png create mode 100644 assets/units/soldier/body_panic_1.png diff --git a/assets/units/soldier/body_panic_0.png b/assets/units/soldier/body_panic_0.png new file mode 100644 index 0000000..d8f4136 Binary files /dev/null and b/assets/units/soldier/body_panic_0.png differ diff --git a/assets/units/soldier/body_panic_1.png b/assets/units/soldier/body_panic_1.png new file mode 100644 index 0000000..d2baf51 Binary files /dev/null and b/assets/units/soldier/body_panic_1.png differ diff --git a/src/common.hpp b/src/common.hpp index 4b20ced..f136487 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -232,8 +232,6 @@ namespace game { void command(v2f_t x); size_t roll(die_t die); - - void wake_everything(v2f_t x, float range); }; } diff --git a/src/game/assets.cpp b/src/game/assets.cpp index e4d6475..a96a535 100644 --- a/src/game/assets.cpp +++ b/src/game/assets.cpp @@ -14,6 +14,7 @@ void load(void) soldier.body_idle.load("assets/units/soldier/body_idle", 2, 2, 2); soldier.body_aiming.load("assets/units/soldier/body_aiming", 2, 2, 2); soldier.body_firing.load("assets/units/soldier/body_firing", 2, 2, 2); + soldier.body_panic.load("assets/units/soldier/body_panic_", 2); soldier.legs_idle.load("assets/units/soldier/legs_idle", 2, 2); soldier.legs_walking.load("assets/units/soldier/legs_walking", 2, 2); soldier.dead.load("assets/units/soldier/dead_", 1); diff --git a/src/game/game.cpp b/src/game/game.cpp index e5f32f5..9d184ea 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -117,7 +117,10 @@ void state_t::command(v2f_t x) if (unit->dead) continue; - if (!unit->start_moving(snap, CF_SOLID | CF_WATER)) + if (!unit->controllable) + continue; + + if (!unit->start_moving(snap)) unit->say(text::get(text::SAY_NO_PATH)); else unit->say(text::get(text::SAY_MOVING)); @@ -152,32 +155,6 @@ void state_t::tick(double now_, double dt_) } } -void state_t::wake_everything(v2f_t x, float range) -{ - rectf_t wake_range; - - wake_range[0] = x - v2f_t(range, range); - wake_range[1] = x + v2f_t(range, range); - - for (world::entity_t *went : world.get_entities(wake_range, -1)) { - auto ent = dynamic_cast(went); - - // WTF? - if (!ent) - continue; - - if (ent->ignore_waking) - continue; - - ent->wake_time = now; - - if (ent->awake) - continue; - - ent->wake(); - } -} - die_t::die_t(size_t sides_) { sides = sides_; diff --git a/src/game/game.hpp b/src/game/game.hpp index 0bc2da2..aea381c 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -31,6 +31,7 @@ namespace game { typedef struct { render::oriented_sprite_4M_t head_idle, body_idle; render::oriented_sprite_4M_t body_aiming, body_firing; + render::animated_texture_t body_panic; render::oriented_sprite_4M2_t legs_idle, legs_walking; render::animated_texture_t dead; } soldier_assets_t; @@ -77,6 +78,7 @@ namespace game { SAY_BLOCKED, SAY_READY, SAY_MOVING, + SAY_PANIC, UNIT_NAME_SPIDER, UNIT_NAME_SOLDIER, UNIT_NAME_NEST, @@ -85,7 +87,10 @@ namespace game { UNIT_MISS, UNIT_CRITICAL_MISS, UNIT_CRITICAL_HIT, - UNIT_DAMAGE + UNIT_DAMAGE, + UNIT_SAVING_THROW_WILLPOWER, + UNIT_SAVING_THROW_SUCCESS, + UNIT_SAVING_THROW_FAILURE } index_t; std::string get(index_t index); @@ -138,6 +143,8 @@ namespace game { bool controllable = false; struct { + world::cflags_t cflags; + bool moving = false; v2f_t dst; float angle = 0.0f; @@ -150,7 +157,7 @@ namespace game { } move; bool keep_moving(double speed); - bool start_moving(v2f_t dst, world::cflags_t cflags); + bool start_moving(v2f_t dst); struct { size_t armor_class; @@ -177,13 +184,22 @@ namespace game { double last_target_time = -INFINITY; v2f_t last_target_x; + double next_fear_test = -INFINITY; + size_t willpower_bonus; + size_t fear_dc; + + bool panic = false; + double panic_end; + double panic_turn; + + void check_area(void); + void target_and_attack(void); + public: unit_soldier_t(game::state_t *game_); ~unit_soldier_t(void) {}; void render_to(render::state_t *render); - void target_and_attack(void); - void on_think(void); void on_spawn(void) {}; void on_wake(void) {}; diff --git a/src/game/text.cpp b/src/game/text.cpp index 00441f8..3da39ff 100644 --- a/src/game/text.cpp +++ b/src/game/text.cpp @@ -29,6 +29,9 @@ static std::string get_english(index_t index) case SAY_MOVING: return "On my way."; + case SAY_PANIC: + return "I'm not getting paid enough for this."; + case UNIT_NAME_SPIDER: return "Spider"; @@ -56,6 +59,15 @@ static std::string get_english(index_t index) case UNIT_DAMAGE: return "deals damage"; + case UNIT_SAVING_THROW_WILLPOWER: + return "makes a saving throw for willpower"; + + case UNIT_SAVING_THROW_SUCCESS: + return "success"; + + case UNIT_SAVING_THROW_FAILURE: + return "failure"; + default: abort(); } @@ -82,6 +94,9 @@ static std::string get_polish(index_t index) case SAY_MOVING: return "Jestem w drodze."; + case SAY_PANIC: + return "Za mało mi za to płacą."; + case UNIT_NAME_SPIDER: return "Pająk"; @@ -109,6 +124,15 @@ static std::string get_polish(index_t index) case UNIT_DAMAGE: return "zadaje obrażenia"; + case UNIT_SAVING_THROW_WILLPOWER: + return "wykonuje rzut obronny na siłę woli"; + + case UNIT_SAVING_THROW_SUCCESS: + return "sukces"; + + case UNIT_SAVING_THROW_FAILURE: + return "porażka"; + default: abort(); } diff --git a/src/game/units.cpp b/src/game/units.cpp index cbc3f1a..24b9567 100644 --- a/src/game/units.cpp +++ b/src/game/units.cpp @@ -46,7 +46,7 @@ void unit_t::render_to(render::state_t *render) float height; text_pos = render_bounds[0] + v2f_t(render_bounds.dim(0) / 2, -render_bounds.dim(1) * 0.1); - height = size.dim_min() * 0.20f; + height = size.dim_min() * 0.50f; render->render_text(text_pos, height, say_text, render::ALIGN_CENTER_BOTTOM, sf::Color::White); @@ -65,6 +65,9 @@ void unit_t::render_to(render::state_t *render) else ss << "S"; + if (controllable) + ss << "C"; + text_pos = render_bounds[0] + v2f_t(render_bounds.dim(0) / 2, -render_bounds.dim(1) * 0.1); height = size.dim_min() * 0.40f; text_pos[1] -= height; @@ -119,7 +122,7 @@ bool unit_t::keep_moving(double speed) } cmodel_next.bounds = size + x_new; - cmodel_next.cflags = cmodel.cflags; + cmodel_next.cflags = move.cflags; if (!world->test_rect(&cmodel_next, this)) { x = x_new; cmodel = cmodel_next; @@ -142,7 +145,7 @@ bool unit_t::keep_moving(double speed) return rv; } -bool unit_t::start_moving(v2f_t dst, world::cflags_t cflags) +bool unit_t::start_moving(v2f_t dst) { world::cmodel_t rep; @@ -154,7 +157,7 @@ bool unit_t::start_moving(v2f_t dst, world::cflags_t cflags) move.dst = dst; move.path.clear(); - rep.cflags = cflags; + rep.cflags = move.cflags; rep.bounds = cmodel.bounds; if (!world->find_path(x, move.dst, &rep, this, &move.path)) { @@ -270,6 +273,7 @@ unit_soldier_t::unit_soldier_t(game::state_t *game) : unit_t(game, UNIT_SOLDIER) render_size[0] = v2f_t(-0.5f, -1.0f); render_size[1] = v2f_t(+0.5f, +0.5f); cmodel.cflags = CF_BODY; + move.cflags = CF_SOLID | CF_BODY | CF_WATER; name = text::get(text::UNIT_NAME_SOLDIER); @@ -283,6 +287,51 @@ unit_soldier_t::unit_soldier_t(game::state_t *game) : unit_t(game, UNIT_SOLDIER) cs.hit_die = die_t(8); } +void unit_soldier_t::check_area(void) +{ + rectf_t bounds; + + bounds[0] = x - v2f_t(10, 10); + bounds[1] = x + v2f_t(10, 10); + + willpower_bonus = 0; + fear_dc = 0; + + for (world::entity_t *went : game->world.get_entities(bounds, -1)) { + auto ent = dynamic_cast(went); + unit_t *unit; + + // WTF? + if (!ent) + continue; + + // Wake everything around. + if (!ent->ignore_waking) { + ent->wake_time = game->now; + + if (!ent->awake) + ent->wake(); + } + + if (ent->type != ET_UNIT) + continue; + + unit = (unit_t*)ent; + + if (unit->dead) + continue; + + if (unit->friendly) + willpower_bonus += 3; + else { + if (unit->type == UNIT_NEST) + fear_dc += 4; + else + fear_dc += 2; + } + } +} + void unit_soldier_t::target_and_attack(void) { unit_t *target; @@ -317,12 +366,64 @@ void unit_soldier_t::target_and_attack(void) void unit_soldier_t::on_think(void) { - game->wake_everything(x, 10); + check_area(); - target_and_attack(); + if (!panic && fear_dc > 1 && game->now > next_fear_test) { + size_t roll; + bool success; + std::stringstream ss; + + roll = game->roll(die_t(20)); + success = roll + willpower_bonus >= fear_dc; + + ss << name << " " << text::get(text::UNIT_SAVING_THROW_WILLPOWER); + ss << ": " << roll << " + " << willpower_bonus << " = " << roll + willpower_bonus; + ss << " vs " << fear_dc << ": "; + + if (success) + ss << text::get(text::UNIT_SAVING_THROW_SUCCESS); + else + ss << text::get(text::UNIT_SAVING_THROW_FAILURE); + + game->interface->print(ss.str()); + + if (!success) { + say(text::get(text::SAY_PANIC)); + panic = true; + panic_end = game->now + 10; + panic_turn = -INFINITY; + controllable = false; + } + + next_fear_test = game->now + 3; + } + + if (panic && game->now > panic_end) { + move.moving = false; + move.path.clear(); + panic = false; + controllable = true; + } + + if (!panic) { + target_and_attack(); + + if (!keep_moving(4.0)) + say(text::get(text::SAY_BLOCKED)); + } else { + if (!keep_moving(4.0)) + panic_turn = -INFINITY; + + if (game->now >= panic_turn) { + v2f_t t; - if (!keep_moving(4.0)) - say(text::get(text::SAY_BLOCKED)); + t = game->dice_prng.unit_vec2(); + + move.path.clear(); + move.path.push_back(x + t * 10); + panic_turn = game->now + 0.5; + } + } } void unit_soldier_t::on_death(void) @@ -345,7 +446,7 @@ void unit_soldier_t::render_to(render::state_t *render) else legs = &assets::soldier.legs_idle; - if (last_target_time + 3 > game->now) { + if (!panic && last_target_time + 3 > game->now) { if (last_attack + 0.1 > game->now) body = &assets::soldier.body_firing; else @@ -359,7 +460,12 @@ void unit_soldier_t::render_to(render::state_t *render) } render->render(game->now * 10, legs, render_bounds, move.angle); - render->render(game->now * 10, body, render_bounds, body_angle); + + if (panic) + render->render(game->now * 10, &assets::soldier.body_panic, render_bounds); + else + render->render(game->now * 10, body, render_bounds, body_angle); + render->render(game->now * 10, &assets::soldier.head_idle, render_bounds, body_angle); } else render->render(game->now * 10, &assets::soldier.dead, render_bounds); @@ -374,6 +480,7 @@ unit_spider_t::unit_spider_t(game::state_t *game) : unit_t(game, UNIT_SPIDER) render_size[0] = v2f_t(-0.3f, -0.3f); render_size[1] = v2f_t(+0.3f, +0.3f); cmodel.cflags = CF_BODY_SMALL; + move.cflags = CF_SOLID | CF_WATER; name = text::get(text::UNIT_NAME_SPIDER); @@ -394,7 +501,7 @@ void unit_spider_t::target_and_attack(void) if (!target) return; - start_moving(target->x, CF_SOLID | CF_WATER); + start_moving(target->x); next_targetting = game->now + 0.2; if (last_attack + 0.5 > game->now) -- cgit