From f43f52903f29d010a72183de24ae8f56ab6702bb Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Sat, 21 Apr 2018 16:45:40 +0200 Subject: Add the scientist. --- src/game/assets.cpp | 6 +++ src/game/decos.cpp | 2 +- src/game/game.cpp | 57 +++++++++++++++++++--- src/game/game.hpp | 35 +++++++++++++- src/game/interface.cpp | 4 ++ src/game/unit_repl.cpp | 30 ++++++++---- src/game/unit_scientist.cpp | 115 ++++++++++++++++++++++++++++++++++++++++++++ src/game/unit_soldier.cpp | 35 ++------------ src/game/units.cpp | 28 ++++++++++- 9 files changed, 261 insertions(+), 51 deletions(-) create mode 100644 src/game/unit_scientist.cpp (limited to 'src/game') diff --git a/src/game/assets.cpp b/src/game/assets.cpp index 106387d..8278e6d 100644 --- a/src/game/assets.cpp +++ b/src/game/assets.cpp @@ -20,6 +20,7 @@ along with Minitrem. If not, see . namespace game::assets { soldier_assets_t soldier; +scientist_assets_t scientist; spider_assets_t spider; nest_assets_t nest; repl_assets_t repl; @@ -57,6 +58,11 @@ void load(void) soldier.step_stone.load("assets/units/soldier/step_stone_5.ogg"); soldier.step_stone.volume = 1.0f; + scientist.body_idle.load("assets/units/scientist/body_idle", 1, 1, 1); + scientist.body_walking.load("assets/units/scientist/body_walking", 2, 2, 2); + scientist.head_idle.load("assets/units/scientist/head_idle", 1, 1, 1); + scientist.avatar.load("assets/units/scientist/avatar_", 1); + spider.idle.load("assets/units/spider/idle", 2, 2, 2); spider.walking.load("assets/units/spider/walking", 2, 2, 2); spider.dead.load("assets/units/spider/dead_", 1); diff --git a/src/game/decos.cpp b/src/game/decos.cpp index eaa6c6b..e250f72 100644 --- a/src/game/decos.cpp +++ b/src/game/decos.cpp @@ -126,7 +126,7 @@ void deco_t::damage(int points, unit_t *attacker) break; default: - delete this; + game->deletion_list.insert(this); return; } diff --git a/src/game/game.cpp b/src/game/game.cpp index 361fa83..084ac38 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -167,12 +167,13 @@ enum { COMMAND_FIRE_ROCKET, COMMAND_STOP, - COMMAND_REPL, + COMMAND_REPL_SOLDIER, + COMMAND_REPL_SCIENTIST }; bool state_t::populate_pie_menu(std::vector &items) { - bool soldiers = false, repls = false, rockets = false; + bool can_move = false, soldiers = false, repls = false, rockets = false; items.clear(); @@ -185,10 +186,16 @@ bool state_t::populate_pie_menu(std::vector &items) switch (unit->type) { case unit_t::UNIT_SOLDIER: + can_move = true; soldiers = true; if (!dynamic_cast(unit)->rocket_fired) rockets = true; break; + + case unit_t::UNIT_SCIENTIST: + can_move = true; + break; + case unit_t::UNIT_REPL: repls = true; break; @@ -197,16 +204,20 @@ bool state_t::populate_pie_menu(std::vector &items) } } - if (soldiers) { + if (can_move) { items.push_back((interface::pie_item_t){"Move", COMMAND_MOVE}); + items.push_back((interface::pie_item_t){"Stop", COMMAND_STOP}); + } + + if (soldiers) { items.push_back((interface::pie_item_t){"Fire", COMMAND_FIRE}); if (rockets) items.push_back((interface::pie_item_t){"Fire a rocket", COMMAND_FIRE_ROCKET}); - items.push_back((interface::pie_item_t){"Stop", COMMAND_STOP}); } if (repls) { - items.push_back((interface::pie_item_t){"Replicate", COMMAND_REPL}); + items.push_back((interface::pie_item_t){"Spawn a soldier", COMMAND_REPL_SOLDIER}); + items.push_back((interface::pie_item_t){"Spawn a scientist", COMMAND_REPL_SCIENTIST}); } return true; @@ -256,11 +267,35 @@ static void command_soldier(unit_soldier_t *soldier, v2f_t x, int number) } } +static void command_scientist(unit_scientist_t *scientist, v2f_t x, int number) +{ + switch (number) { + case COMMAND_MOVE: + if (!scientist->start_moving(x)) + scientist->say(text::get(text::SAY_NO_PATH), false); + else { + scientist->move_marker = std::make_unique(scientist->game, scientist->move.path.back()); + scientist->say(text::get(text::SAY_MOVING), false); + } + break; + + case COMMAND_STOP: + scientist->stop_moving(); + scientist->say(text::get(text::SAY_STOPPING), false); + scientist->aim_marker.reset(); + break; + } +} + static void command_repl(unit_repl_t *repl, v2f_t x, int number) { switch (number) { - case COMMAND_REPL: - repl->activate(); + case COMMAND_REPL_SOLDIER: + repl->activate(unit_t::UNIT_SOLDIER); + break; + + case COMMAND_REPL_SCIENTIST: + repl->activate(unit_t::UNIT_SCIENTIST); break; } } @@ -277,7 +312,8 @@ void state_t::command(v2f_t x, int number) for (unit_t *unit : selected_units) unit->cmodel.ignore = true; - for (unit_t *unit : selected_units) { + auto copy = selected_units; + for (unit_t *unit : copy) { if (unit->dead || !unit->controllable) continue; @@ -287,6 +323,11 @@ void state_t::command(v2f_t x, int number) x, number); break; + case unit_t::UNIT_SCIENTIST: + command_scientist(dynamic_cast(unit), + x, number); + break; + case unit_t::UNIT_REPL: command_repl(dynamic_cast(unit), x, number); diff --git a/src/game/game.hpp b/src/game/game.hpp index 1cd61aa..908d73b 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -19,6 +19,7 @@ along with Minitrem. If not, see . namespace game { enum { + ET_NONE, ET_UNIT, ET_EFFECT, ET_DECO, @@ -70,6 +71,12 @@ namespace game { audio::sound_t fire, step_stone, step_dirt, pain, death; } soldier_assets_t; + typedef struct { + render::oriented_sprite_4M_t body_idle, body_walking; + render::oriented_sprite_4M_t head_idle; + render::animated_texture_t avatar; + } scientist_assets_t; + typedef struct { render::oriented_sprite_4M_t idle, walking; render::animated_texture_t dead; @@ -102,6 +109,7 @@ namespace game { } deco_assets_t; extern soldier_assets_t soldier; + extern scientist_assets_t scientist; extern spider_assets_t spider; extern nest_assets_t nest; extern repl_assets_t repl; @@ -182,6 +190,7 @@ namespace game { typedef enum { UNIT_SOLDIER, + UNIT_SCIENTIST, UNIT_SPIDER, UNIT_NEST, UNIT_REPL @@ -269,6 +278,30 @@ namespace game { void on_death(void); }; + class unit_scientist_t : public unit_t { + public: + std::unique_ptr move_marker; + std::unique_ptr aim_marker; + + sf::Color selection_color; + + void check_area(void); + void shoot(v2f_t from, v2f_t at, int damage); + void fire_shotgun(v2f_t aim); + void target_and_attack(void); + + unit_scientist_t(game::state_t *game_); + ~unit_scientist_t(void) {}; + void render_to(render::state_t *render); + void render_late_to(render::state_t *render); + + void on_think(void); + void on_spawn(void) {}; + void on_wake(void) {}; + void on_damage(unit_t *attacker); + void on_death(void); + }; + class unit_spider_t : public unit_t { public: unit_spider_t(game::state_t *game_); @@ -314,7 +347,7 @@ namespace game { void on_damage(unit_t *attacker); void on_death(void); - void activate(void); + void activate(unit_t::type_t type); }; class effect_t : public game::entity_t { diff --git a/src/game/interface.cpp b/src/game/interface.cpp index 1119367..00cdd7b 100644 --- a/src/game/interface.cpp +++ b/src/game/interface.cpp @@ -358,6 +358,10 @@ static void render_avatar(render::state_t *render, game::unit_t *unit, v2f_t at, image = &assets::soldier.avatar; break; + case game::unit_t::UNIT_SCIENTIST: + image = &assets::scientist.avatar; + break; + case game::unit_t::UNIT_REPL: image = &assets::repl.avatar; break; diff --git a/src/game/unit_repl.cpp b/src/game/unit_repl.cpp index 08a43d2..bad5ab0 100644 --- a/src/game/unit_repl.cpp +++ b/src/game/unit_repl.cpp @@ -43,7 +43,7 @@ void unit_repl_t::on_damage(unit_t *attacker) void unit_repl_t::on_death(void) { game->explosion(x); - delete this; + game->deletion_list.insert(this); } void unit_repl_t::render_to(render::state_t *render) @@ -52,19 +52,31 @@ void unit_repl_t::render_to(render::state_t *render) unit_t::render_to(render); } -void unit_repl_t::activate(void) +void unit_repl_t::activate(unit_t::type_t type) { - unit_soldier_t *soldier; world::cmodel_t cmodel; - - soldier = new unit_soldier_t(game); - soldier->place(&game->world, x); - - for (world::entity_t *ent : game->world.get_entities(soldier->cmodel.bounds, soldier->move.cflags)) + world::entity_t *spawned; + + if (type == UNIT_SOLDIER) { + unit_soldier_t *soldier; + + soldier = new unit_soldier_t(game); + soldier->place(&game->world, x); + spawned = soldier; + } else if (type == UNIT_SCIENTIST) { + unit_scientist_t *scientist; + + scientist = new unit_scientist_t(game); + scientist->place(&game->world, x); + spawned = scientist; + } else + abort(); + + for (world::entity_t *ent : game->world.get_entities(spawned->cmodel.bounds, CF_BODY|CF_BODY_SMALL)) { unit_t *unit; - if (ent == soldier) + if (ent == spawned) continue; if (ent->type != ET_UNIT) diff --git a/src/game/unit_scientist.cpp b/src/game/unit_scientist.cpp new file mode 100644 index 0000000..36be1d5 --- /dev/null +++ b/src/game/unit_scientist.cpp @@ -0,0 +1,115 @@ +/* +This file is part of Minitrem. + +Minitrem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +Minitrem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Minitrem. If not, see . +*/ + +#include "game.hpp" + +namespace game { + +static std::string scientist_name(procgen::prng_t *prng) +{ + std::stringstream ss; + static const char *names[] = { + "Сергей", "Александер", "Василий", "Владимир", + "Дмитрий", "Иосиф", "Иван", "Пётр", "Юрий" + }; + + ss << "проф. "; + ss << names[prng->next() % (sizeof(names) / sizeof(names[0]))]; + + return ss.str(); +} + +unit_scientist_t::unit_scientist_t(game::state_t *game) : unit_t(game, UNIT_SCIENTIST) +{ + size[0] = v2f_t(-0.3f, +0.0f); + size[1] = v2f_t(+0.3f, +0.3f); + render_size[0] = v2f_t(-0.5f, -1.2f); + render_size[1] = v2f_t(+0.5f, +0.3f); + cmodel.cflags = CF_BODY; + move.cflags = CF_SOLID | CF_BODY | CF_WATER; + + name = scientist_name(&game->prng); + + wake(); + friendly = true; + controllable = true; + + health = max_health = 15; +} + +void unit_scientist_t::on_think(void) +{ + keep_moving(2.0); + game->wake_area(x); + + if (!move.moving) + move_marker.reset(); + + if (move.moving && (x - move.last_step).len() > 0.5f) { + move.last_step = x; + assets::soldier.step_stone.play_3d(x); + } +} + +void unit_scientist_t::on_damage(unit_t *attacker) +{ + +} + +void unit_scientist_t::on_death(void) +{ + cmodel.ignore = true; + game->deletion_list.insert(this); + game->explosion(x); +} + +void unit_scientist_t::render_to(render::state_t *render) +{ + sf::Color selection_color; + render::oriented_sprite_t *body; + + if (selected == selection_cookie) { + if (health == max_health) + selection_color = sf::Color::Green; + else if (health >= max_health / 2) + selection_color = sf::Color::Yellow; + else if (dead) + selection_color = sf::Color::Black; + else + selection_color = sf::Color::Red; + + render->render(0.0, &assets::unit_selected, cmodel.bounds, selection_color); + } + + if (move.moving && !move.blocked) + body = &assets::scientist.body_walking; + else + body = &assets::scientist.body_idle; + + render->render(game->now * 4, body, render_bounds, move.angle); + render->render(game->now * 4, &assets::scientist.head_idle, render_bounds, move.angle); + + unit_t::render_to(render); +} + +void unit_scientist_t::render_late_to(render::state_t *render) +{ + if (selected == selection_cookie) + render->render(0.0, &assets::unit_selected_halo, cmodel.bounds, selection_color); +} + +} diff --git a/src/game/unit_soldier.cpp b/src/game/unit_soldier.cpp index e41278e..886e09d 100644 --- a/src/game/unit_soldier.cpp +++ b/src/game/unit_soldier.cpp @@ -29,9 +29,9 @@ static std::string soldier_name(procgen::prng_t *prng) { std::stringstream ss; static const char *names[] = { - "Kowalski", "Jackson", "Carter", "O'Neill", "Hammond", - "Mitchell", "Hitler", "Stalin", "Wyspiański", "Bush", - "Washington" + "Kowalski", "O'Neill", "Hammond", "Mitchell", "Hitler", + "Stalin", "Wyspiański", "Bush", "Washington", "Jaruzelski", + "Jackson", "Carter", }; ss << initials[prng->next() % (sizeof(initials) / sizeof(initials[0]))]; @@ -59,33 +59,6 @@ unit_soldier_t::unit_soldier_t(game::state_t *game) : unit_t(game, UNIT_SOLDIER) health = max_health = 20; } -void unit_soldier_t::check_area(void) -{ - rectf_t bounds; - - bounds[0] = x - v2f_t(10, 10); - bounds[1] = x + v2f_t(10, 10); - - for (world::entity_t *went : game->world.get_entities(bounds, -1)) { - auto ent = dynamic_cast(went); - - // WTF? - if (!ent) - continue; - - if (ent == this) - continue; - - // Wake everything around. - if (!ent->ignore_waking) { - ent->wake_time = game->now; - - if (!ent->awake) - ent->wake(); - } - } -} - static v2f_t spread_aim(v2f_t x, v2f_t aim, float cof, procgen::prng_t *prng) { float r, r_, dth; @@ -196,6 +169,7 @@ skip_targetting: void unit_soldier_t::on_think(void) { keep_moving(2.0); + game->wake_area(x); if (!move.moving) move_marker.reset(); @@ -205,7 +179,6 @@ void unit_soldier_t::on_think(void) assets::soldier.step_stone.play_3d(x); } - check_area(); target_and_attack(); } diff --git a/src/game/units.cpp b/src/game/units.cpp index c4bc8de..2b3b081 100644 --- a/src/game/units.cpp +++ b/src/game/units.cpp @@ -67,7 +67,8 @@ void unit_t::render_to(render::state_t *render) sf::Color::White); } - if (selected == selection_cookie && type != UNIT_SOLDIER) + if (selected == selection_cookie && type != UNIT_SOLDIER && + type != UNIT_SCIENTIST) render->render_hlrect(render_bounds, sf::Color::Green); if (debug_AI) { @@ -217,6 +218,7 @@ void unit_t::die(unit_t *killer) { switch (type) { case UNIT_SOLDIER: + case UNIT_SCIENTIST: case UNIT_SPIDER: game->interface->print(name + " " + text::get(text::UNIT_DEATH) + "."); break; @@ -336,4 +338,28 @@ void state_t::hivemind_alert(v2f_t x, float r, bool do_move, v2f_t move_to) } } +void state_t::wake_area(v2f_t x) +{ + rectf_t bounds; + + bounds[0] = x - v2f_t(10, 10); + bounds[1] = x + v2f_t(10, 10); + + for (world::entity_t *went : world.get_entities(bounds, -1)) { + auto ent = dynamic_cast(went); + + // WTF? + if (!ent) + continue; + + // Wake everything around. + if (!ent->ignore_waking) { + ent->wake_time = now; + + if (!ent->awake) + ent->wake(); + } + } +} + } // namespace game -- cgit