From 820e2e9d428c273e065cfe18b48c183a5cbedd75 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Sat, 16 Dec 2017 19:06:43 +0100 Subject: Major refactor of game's entity code. --- src/common.hpp | 14 +++--- src/game/effects.cpp | 10 +++- src/game/game.cpp | 137 ++++++++++++++++++++++++++++----------------------- src/game/game.hpp | 80 +++++++++++++++++------------- src/game/units.cpp | 114 +++++++++++++++--------------------------- src/math.hpp | 10 ++++ src/world.cpp | 9 +++- 7 files changed, 194 insertions(+), 180 deletions(-) diff --git a/src/common.hpp b/src/common.hpp index f543ca5..eaed590 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -129,7 +129,6 @@ namespace world { }; class entity_t { - world_t *parent_world; std::vector parents; void link_to_sector(sector_t *sector); @@ -139,12 +138,14 @@ namespace world { size_t cookie = 0; public: + world_t *world = 0; int type; cmodel_t cmodel; rectf_t render_bounds; int render_layer = 0; entity_t(int type_); + ~entity_t(void) {}; void link(world_t *world); void unlink(); @@ -189,6 +190,7 @@ namespace interface { namespace game { bool load_assets(void); + class entity_t; class unit_t; class effect_t; @@ -203,11 +205,11 @@ namespace game { }; class state_t { - std::unordered_set units; - std::unordered_set selected_units; - std::unordered_set awake_units; + protected: + friend entity_t; - std::unordered_set effects; + std::unordered_set awake_entities; + std::unordered_set selected_units; procgen::prng_t dice_prng; public: @@ -225,7 +227,7 @@ namespace game { size_t roll(roll_params_t *P); - void add_effect(effect_t *effect); + void wake_everything(v2f_t x, float range); }; } diff --git a/src/game/effects.cpp b/src/game/effects.cpp index d9215e3..8e7d2d7 100644 --- a/src/game/effects.cpp +++ b/src/game/effects.cpp @@ -2,9 +2,15 @@ namespace game { -effect_t::effect_t(state_t *game_) : world::entity_t(ET_EFFECT) +effect_t::effect_t(state_t *game_) : game::entity_t(game_, ET_EFFECT) { - game = game_; + always_awake = true; +} + +void effect_t::on_think(void) +{ + if (game->now >= ttl) + destroy(); } fx_tracer_t::fx_tracer_t(state_t *game_, v2f_t x0_, v2f_t x1_) : effect_t(game_) diff --git a/src/game/game.cpp b/src/game/game.cpp index 993f450..35cb90c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4,6 +4,59 @@ namespace game { size_t selection_cookie = 1; +entity_t::entity_t(game::state_t *game_, int type_) : world::entity_t(type_) +{ + game = game_; +} + +void entity_t::destroy(void) +{ + unlink(); + game->awake_entities.erase(this); + delete this; +} + +void entity_t::place(world::world_t *world_) +{ + bool do_spawn = false; + + if (!world) + do_spawn = true; + + link(world_); + + if (do_spawn) + on_spawn(); + + if (always_awake) + wake(); +} + +void entity_t::place(world::world_t *world, v2f_t x_) +{ + x = x_; + cmodel.bounds = size + x; + render_bounds = render_size + x; + + place(world); +} + +void entity_t::wake(void) +{ + awake = true; + wake_time = game->now; + game->awake_entities.insert(this); + + if (!always_awake) + on_wake(); +} + +void entity_t::sleep(void) +{ + awake = false; + game->awake_entities.erase(this); +} + void state_t::start(void) { human_t *human; @@ -14,13 +67,10 @@ void state_t::start(void) human = new human_t(this); human->place(&world, v2f_t(0.5, 0.5)); - units.insert(human); human = new human_t(this); human->place(&world, v2f_t(1.5, 0.5)); - units.insert(human); human = new human_t(this); human->place(&world, v2f_t(2.5, 0.5)); - units.insert(human); alien = new alien_t(this); alien->place(&world, v2f_t(15.5, -2.5)); @@ -32,11 +82,6 @@ void state_t::start(void) void state_t::stop(void) { - // FIXME - /* - for (unit_t *unit : units) - delete unit; - */ } void state_t::select(rectf_t x) @@ -83,12 +128,6 @@ void state_t::command(v2f_t x) } } -void state_t::add_effect(effect_t *effect) -{ - effects.insert(effect); - effect->link(&world); -} - void state_t::tick(double now_, double dt_) { union { @@ -105,61 +144,33 @@ void state_t::tick(double now_, double dt_) u.d = dt; dice_prng.seed(dice_prng.next() ^ u.i); - for (unit_t *unit : units) { - rectf_t wake_range; - - if (unit->dead) - continue; - - wake_range[0] = unit->x - v2f_t(10, 10); - wake_range[1] = unit->x + v2f_t(10, 10); - - for (world::entity_t *ent : world.get_entities(wake_range, -1)) { - unit_t *enemy; - - if (ent->type != ET_UNIT) - continue; - - enemy = (unit_t*)ent; - - if (enemy->dead) - continue; - - enemy->wake_time = now; + // NOTE: on_think can erase the entity from awake_entities. + for (auto i = awake_entities.begin(); i != awake_entities.end(); ) { + auto next = i; + next++; + (*i)->on_think(); + i = next; + } +} - if (enemy->awake) - continue; +void state_t::wake_everything(v2f_t x, float range) +{ + rectf_t wake_range; - if (enemy->type == unit_t::UNIT_HUMAN) - continue; + wake_range[0] = x - v2f_t(range, range); + wake_range[1] = x + v2f_t(range, range); - enemy->awake = true; - awake_units.insert(enemy); - enemy->wake(unit); - } + for (world::entity_t *went : world.get_entities(wake_range, -1)) { + auto ent = dynamic_cast(went); - unit->think(); - } + // WTF? + if (!ent) + continue; - for (auto i = std::begin(awake_units); i != std::end(awake_units);) { - if (now - (*i)->wake_time >= 5.0) { - (*i)->awake = false; - (*i)->sleep(); - i = awake_units.erase(i); - } else { - if (!(*i)->dead) - (*i)->think(); - i++; - } - } + if (ent->always_awake || ent->awake) + continue; - for (auto i = std::begin(effects); i != std::end(effects);) { - if (now > (*i)->ttl) { - (*i)->unlink(); - delete *i; - i = effects.erase(i); - } else - i++; + ent->wake(); } } diff --git a/src/game/game.hpp b/src/game/game.hpp index 4cc1077..b9a426b 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -55,21 +55,36 @@ namespace game { void load(void); } - class unit_t : public world::entity_t { - protected: - game::state_t *game; - world::world_t *world; + class entity_t : public world::entity_t { + public: + game::state_t *game = 0; + v2f_t x; + rectf_t size, render_size; + + entity_t(game::state_t *game, int type_); + ~entity_t(void) {}; + void destroy(void); - world::cmodel_t make_cmodel(v2f_t at); - void compute_bounds(); + void place(world::world_t *world); + void place(world::world_t *world, v2f_t x_); + bool always_awake = false; + bool awake = false; + double wake_time = -INFINITY; + void wake(void); + void sleep(void); + + virtual void on_think(void) = 0; + virtual void on_spawn(void) = 0; + virtual void on_wake(void) = 0; + }; + + class unit_t : public entity_t { + protected: double next_targetting = -INFINITY; double last_attack = -INFINITY; public: - v2f_t x; - rectf_t size, render_size; - world::cflags_t cflags; size_t selected = 0; typedef enum { @@ -82,8 +97,6 @@ namespace game { unit_t(game::state_t *game_, type_t type_); - virtual void think(void) = 0; - struct { bool moving = false; v2f_t dst; @@ -96,15 +109,9 @@ namespace game { float next_attempt; } move; - void place(world::world_t *world_, v2f_t x_); bool keep_moving(double speed); bool start_moving(v2f_t dst, world::cflags_t cflags); - bool awake = false; - double wake_time = -INFINITY; - virtual void wake(unit_t *by_whom) = 0; - virtual void sleep(void) = 0; - struct { size_t armor_class; roll_params_t hit_roll; @@ -116,13 +123,13 @@ namespace game { int health = 1, max_health = 1; void damage(int points, unit_t *attacker); void try_attack(unit_t *target); - virtual void die() = 0; + void die(unit_t *killer); + virtual void on_death() = 0; std::string say_text; double say_time = -INFINITY; void say(std::string str); - void render_to(render::state_t *render); }; @@ -133,34 +140,37 @@ namespace game { public: human_t(game::state_t *game_); + ~human_t(void) {}; void render_to(render::state_t *render); - void wake(unit_t *by_whom); - void sleep(void); - void think(void); - void die(void); + void on_think(void); + void on_spawn(void) {}; + void on_wake(void) {}; + void on_death(void); }; class alien_t : public unit_t { - double next_targetting = -INFINITY; - public: alien_t(game::state_t *game_); + ~alien_t(void) {}; void render_to(render::state_t *render); - void wake(unit_t *by_whom); - void sleep(void); - void think(void); - void die(void); + void on_think(void); + void on_spawn(void) {}; + void on_wake(void); + void on_death(void); }; - class effect_t : public world::entity_t { + class effect_t : public game::entity_t { public: - game::state_t *game; - double ttl = +INFINITY; + effect_t(game::state_t *game_); virtual ~effect_t() {}; + + void on_think(void); + void on_spawn(void) {}; + void on_wake(void) {}; }; class fx_tracer_t : public effect_t { @@ -168,7 +178,8 @@ namespace game { public: fx_tracer_t(game::state_t *game_, v2f_t x0_, v2f_t x1_); - ~fx_tracer_t(void) = default; + ~fx_tracer_t(void) {}; + void render_to(render::state_t *render); }; @@ -178,7 +189,8 @@ namespace game { public: fx_blood_t(game::state_t *game_, v2f_t x_, bool alien_); - ~fx_blood_t(void) = default; + ~fx_blood_t(void) {}; + void render_to(render::state_t *render); }; diff --git a/src/game/units.cpp b/src/game/units.cpp index 73e1cf0..8eb11c7 100644 --- a/src/game/units.cpp +++ b/src/game/units.cpp @@ -2,27 +2,9 @@ namespace game { -world::cmodel_t unit_t::make_cmodel(v2f_t at) -{ - world::cmodel_t cmodel; - - cmodel.bounds[0] = at + size[0]; - cmodel.bounds[1] = at + size[1]; - cmodel.cflags = cflags; - - return cmodel; -} - -void unit_t::compute_bounds() -{ - render_bounds[0] = x + render_size[0]; - render_bounds[1] = x + render_size[1]; -} - -unit_t::unit_t(game::state_t *game_, unit_t::type_t type_) : entity_t(ET_UNIT) +unit_t::unit_t(game::state_t *game_, unit_t::type_t type_) : game::entity_t(game_, ET_UNIT) { type = type_; - game = game_; } void unit_t::render_to(render::state_t *render) @@ -78,18 +60,6 @@ void unit_t::say(std::string str) game->interface->print(name + ": " + str); } -void unit_t::place(world::world_t *world_, v2f_t x_) -{ - world = world_; - x = x_; - move.moving = false; - - unlink(); - cmodel = make_cmodel(x); - compute_bounds(); - link(world); -} - bool unit_t::keep_moving(double speed) { float time; @@ -127,7 +97,8 @@ bool unit_t::keep_moving(double speed) move.path.pop_front(); } - cmodel_next = make_cmodel(x_new); + cmodel_next.bounds = size + x_new; + cmodel_next.cflags = cmodel.cflags; if (!world->test_rect(&cmodel_next, this)) { x = x_new; cmodel = cmodel_next; @@ -146,9 +117,7 @@ bool unit_t::keep_moving(double speed) break; } - unlink(); - compute_bounds(); - link(world); + place(world, x); return rv; } @@ -185,16 +154,20 @@ void unit_t::damage(int points, unit_t *attacker) fx_blood_t *blood; blood = new fx_blood_t(game, x, type == UNIT_ALIEN); - game->add_effect(blood); + blood->place(&game->world); health -= points; - if (health < 0) { - game->interface->print(name + " " + text::get(text::UNIT_DEATH) + "."); - dead = true; - death_time = game->now; - cflags = 0; - die(); - } + if (health < 0) + die(attacker); +} + +void unit_t::die(unit_t *killer) +{ + game->interface->print(name + " " + text::get(text::UNIT_DEATH) + "."); + dead = true; + death_time = game->now; + cmodel.cflags = 0; + on_death(); } void unit_t::try_attack(unit_t *target) @@ -266,7 +239,9 @@ static unit_t *find_target(world::world_t *world, v2f_t x, float r, human_t::human_t(game::state_t *game) : unit_t(game, UNIT_HUMAN) { - cflags = CF_BODY; + always_awake = true; + + cmodel.cflags = CF_BODY; health = max_health = 20; size[0] = v2f_t(-0.4f, -0.4f); size[1] = v2f_t(+0.4f, +0.4f); @@ -279,16 +254,13 @@ human_t::human_t(game::state_t *game) : unit_t(game, UNIT_HUMAN) cs.damage_roll = roll_params_t(4); } -void human_t::wake(unit_t *by_whom) +void human_t::on_think(void) { -} + if (dead) + return; -void human_t::sleep(void) -{ -} + game->wake_everything(x, 10); -void human_t::think(void) -{ if (game->now > next_targetting) { unit_t *target; @@ -304,7 +276,7 @@ void human_t::think(void) trace = world->trace(x, target->x, CF_SOLID); if (!trace.hit) { tracer = new fx_tracer_t(game, x, target->x); - game->add_effect(tracer); + tracer->place(&game->world); last_attack = game->now; try_attack(target); @@ -321,15 +293,13 @@ void human_t::think(void) say(text::get(text::SAY_BLOCKED)); } -void human_t::die(void) +void human_t::on_death(void) { render_size[0] = v2f_t(-0.75f, -0.5f); render_size[1] = v2f_t(+0.75f, +0.5f); render_layer = -1; cmodel.cflags = CF_BACKGROUND; - unlink(); - compute_bounds(); - link(world); + place(world, x); } void human_t::render_to(render::state_t *render) @@ -367,7 +337,7 @@ void human_t::render_to(render::state_t *render) alien_t::alien_t(game::state_t *game) : unit_t(game, UNIT_ALIEN) { - cflags = CF_BODY_SMALL; + cmodel.cflags = CF_BODY_SMALL; health = max_health = 4; size[0] = v2f_t(-0.2f, -0.3f); size[1] = v2f_t(+0.2f, +0.3f); @@ -380,24 +350,11 @@ alien_t::alien_t(game::state_t *game) : unit_t(game, UNIT_ALIEN) cs.damage_roll = roll_params_t(3, 6, 1); } -void alien_t::wake(unit_t *by_whom) -{ - start_moving(by_whom->x, CF_SOLID | CF_WATER); - next_targetting = game->now + 0.2; -} - -void alien_t::sleep(void) -{ -} - -void alien_t::die(void) +void alien_t::on_think(void) { - render_layer = -1; - cmodel.cflags = CF_BACKGROUND; -} + if (dead) + return; -void alien_t::think(void) -{ if (game->now > next_targetting) { unit_t *target; @@ -424,6 +381,17 @@ void alien_t::think(void) keep_moving(7.0); } +void alien_t::on_wake(void) +{ + next_targetting = game->now; +} + +void alien_t::on_death(void) +{ + render_layer = -1; + cmodel.cflags = CF_BACKGROUND; +} + void alien_t::render_to(render::state_t *render) { bool moving; diff --git a/src/math.hpp b/src/math.hpp index 5e117fd..e393015 100644 --- a/src/math.hpp +++ b/src/math.hpp @@ -352,6 +352,16 @@ public: return r; } + friend rect_t operator+(const rect_t &a, const vec_t &b) + { + rect_t r; + + r[0] = a[0] + b; + r[1] = a[1] + b; + + return r; + } + friend std::ostream& operator<<(std::ostream& stream, rect_t r) { stream << "(" << r[0] << " x " << r[1] << ")"; diff --git a/src/world.cpp b/src/world.cpp index 1975b4e..a5e8869 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -375,7 +375,7 @@ void entity_t::link_to_sector(sector_t *sector) sector->ents.insert(this); } -void entity_t::link(world_t *world) +void entity_t::link(world_t *world_) { rectf_t total_bounds; float fx, fy; @@ -383,6 +383,11 @@ void entity_t::link(world_t *world) float xlip, ylip; size_t xsecs, ysecs; + if (world) + unlink(); + + world = world_; + total_bounds = cmodel.bounds | render_bounds; fx = floor(total_bounds[0][0]); @@ -425,7 +430,7 @@ void entity_t::unlink(void) } parents.clear(); - parent_world = nullptr; + world = 0; } } // namespace world -- cgit