From 30c5c419aedd0e5aae92be6e0eee83f0b6690c6e Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Sun, 1 Apr 2018 15:03:23 +0200 Subject: Add the replicator (not finished). Placeholder sounds from Tremulous, as usual. --- src/game/assets.cpp | 9 ++- src/game/game.cpp | 170 +++++++++++++++++++++++----------------------- src/game/game.hpp | 40 +++++++---- src/game/interface.cpp | 4 -- src/game/text.cpp | 12 +--- src/game/unit_nest.cpp | 2 +- src/game/unit_repl.cpp | 85 +++++++++++++++++++++++ src/game/unit_soldier.cpp | 19 ++++-- src/game/units.cpp | 1 + 9 files changed, 226 insertions(+), 116 deletions(-) create mode 100644 src/game/unit_repl.cpp (limited to 'src/game') diff --git a/src/game/assets.cpp b/src/game/assets.cpp index 46c1b81..c4e9f41 100644 --- a/src/game/assets.cpp +++ b/src/game/assets.cpp @@ -22,6 +22,7 @@ namespace game::assets { soldier_assets_t soldier; spider_assets_t spider; nest_assets_t nest; +repl_assets_t repl; fx_assets_t fx; deco_assets_t deco; audio::ambient_t ambients[AMBIENT_COUNT]; @@ -39,10 +40,12 @@ void load(void) 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); + soldier.gibbing.load("assets/units/soldier/gibbing_", 3); soldier.fire.load("assets/units/soldier/fire.ogg"); soldier.fire.volume = 4.0f; - + soldier.gib_sound.load("assets/units/soldier/gibbing.ogg"); + soldier.step_stone.load("assets/units/soldier/step_stone_1.ogg"); soldier.step_stone.load("assets/units/soldier/step_stone_2.ogg"); soldier.step_stone.load("assets/units/soldier/step_stone_3.ogg"); @@ -57,6 +60,10 @@ void load(void) nest.idle.load("assets/units/nest/idle_", 5); nest.dead.load("assets/units/nest/dead_", 1); + repl.idle.load("assets/units/repl/idle_", 1); + repl.dead.load("assets/units/repl/dead_", 1); + repl.sound.load("assets/units/repl/sound.ogg"); + fx.blood.load("assets/units/blood_", 4); fx.flash.load("assets/units/flash_", 1); diff --git a/src/game/game.cpp b/src/game/game.cpp index d82cff0..8deadd6 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -81,16 +81,15 @@ void entity_t::sleep(void) void state_t::start(void) { unit_soldier_t *soldier; + unit_repl_t *repl; world.generator = worldgen; world.generator_data = (void*)this; soldier = new unit_soldier_t(this); soldier->place(&world, v2f_t(0.5, 0.5)); - soldier = new unit_soldier_t(this); - soldier->place(&world, v2f_t(1.5, 0.5)); - soldier = new unit_soldier_t(this); - soldier->place(&world, v2f_t(2.5, 0.5)); + repl = new unit_repl_t(this); + repl->place(&world, v2f_t(1.5, 0.5)); resume(); } @@ -99,18 +98,13 @@ void state_t::stop(void) { } -void state_t::group_say(std::string text) -{ - interface->print(text::get(text::SAY_GROUP) + ": " + text); -} - -void state_t::select_unit(unit_t *unit, int type) +bool state_t::select_unit(unit_t *unit, int type) { switch (type) { case SELECT_NEW: case SELECT_OR: if (unit->selected == selection_cookie) - return; + return false; else goto do_select; @@ -124,21 +118,19 @@ void state_t::select_unit(unit_t *unit, int type) do_select: unit->selected = selection_cookie; selected_units.insert(unit); - return; + return true; do_deselect: unit->selected = selection_cookie - 1; selected_units.erase(unit); + return false; } void state_t::select(rectf_t rect, int type) { - size_t before; bool select_one; std::list ents; - before = selected_units.size(); - if (type == SELECT_NEW) { selection_cookie++; selected_units.clear(); @@ -161,119 +153,127 @@ void state_t::select(rectf_t rect, int type) if (!unit->controllable) continue; - select_unit(unit, type); + if (select_unit(unit, type)) { + if (unit->type == unit_t::UNIT_SOLDIER) + unit->say(text::get(text::SAY_READY)); + } + if (select_one) break; } - - if (selected_units.size() > before) { - if (selected_units.size() == 1) - (*selected_units.begin())->say(text::get(text::SAY_READY)); - else if (selected_units.size() > 1) - group_say(text::get(text::SAY_READY_GROUP)); - } } enum { COMMAND_MOVE, COMMAND_FIRE, - COMMAND_STOP + COMMAND_STOP, + + COMMAND_REPL }; bool state_t::populate_pie_menu(std::vector &items) { + bool soldiers = false, repls = false; + items.clear(); if (selected_units.size() == 0) return false; - items.push_back((interface::pie_item_t){"Move", COMMAND_MOVE}); - items.push_back((interface::pie_item_t){"Fire", COMMAND_FIRE}); - items.push_back((interface::pie_item_t){"Stop", COMMAND_STOP}); - return true; -} + for (unit_t *unit : selected_units) { + if (unit->dead || !unit->controllable) + continue; -void state_t::command(v2f_t x, int number) -{ - v2f_t snap; - bool group; + switch (unit->type) { + case unit_t::UNIT_SOLDIER: + soldiers = true; + break; + case unit_t::UNIT_REPL: + repls = true; + break; - if (!selected_units.size()) - return; + default:; + } + } - snap[0] = std::round(x[0] - 0.5f) + 0.5f; - snap[1] = std::round(x[1] - 0.5f) + 0.5f; + if (soldiers) { + items.push_back((interface::pie_item_t){"Move", COMMAND_MOVE}); + items.push_back((interface::pie_item_t){"Fire", COMMAND_FIRE}); + items.push_back((interface::pie_item_t){"Stop", COMMAND_STOP}); + } + + if (repls) { + items.push_back((interface::pie_item_t){"Replicate", COMMAND_REPL}); + } - group = selected_units.size() > 1; - if (group) switch (number) { + return true; +} + +static void command_soldier(unit_soldier_t *soldier, v2f_t x, int number) +{ + switch (number) { case COMMAND_MOVE: - group_say(text::get(text::SAY_MOVING_GROUP)); + if (!soldier->start_moving(x)) + soldier->say(text::get(text::SAY_NO_PATH)); + else { + soldier->move_marker = std::make_unique(soldier->game, soldier->move.path.back()); + soldier->say(text::get(text::SAY_MOVING)); + } break; + case COMMAND_STOP: - group_say(text::get(text::SAY_STOPPING_GROUP)); + soldier->stop_moving(); + soldier->manual_firing = false; + soldier->say(text::get(text::SAY_STOPPING)); break; + case COMMAND_FIRE: - group_say(text::get(text::SAY_FIRING_GROUP)); + soldier->manual_firing = true; + soldier->manual_firing_target = x; + soldier->say(text::get(text::SAY_FIRING)); break; } +} - for (unit_t *unit : selected_units) { - unit_soldier_t *soldier; +static void command_repl(unit_repl_t *repl, v2f_t x, int number) +{ + switch (number) { + case COMMAND_REPL: + repl->activate(); + break; + } +} - if (unit->type != unit_t::UNIT_SOLDIER) - continue; +void state_t::command(v2f_t x, int number) +{ + v2f_t snap; - soldier = dynamic_cast(unit); + if (!selected_units.size()) + return; - if (soldier->dead) - continue; + snap[0] = std::round(x[0] - 0.5f) + 0.5f; + snap[1] = std::round(x[1] - 0.5f) + 0.5f; - if (!soldier->controllable) + for (unit_t *unit : selected_units) { + if (unit->dead || !unit->controllable) continue; - switch (number) { - case COMMAND_MOVE: - if (!soldier->start_moving(snap)) - soldier->say(text::get(text::SAY_NO_PATH)); - else { - soldier->move_marker = std::make_unique(this, soldier->move.path.back()); - if (!group) - soldier->say(text::get(text::SAY_MOVING)); - } + switch (unit->type) { + case unit_t::UNIT_SOLDIER: + command_soldier(dynamic_cast(unit), + snap, number); break; - case COMMAND_STOP: - soldier->stop_moving(); - soldier->manual_firing = false; - if (!group) - soldier->say(text::get(text::SAY_STOPPING)); + case unit_t::UNIT_REPL: + command_repl(dynamic_cast(unit), + snap, number); break; - case COMMAND_FIRE: - soldier->manual_firing = true; - soldier->manual_firing_target = x; - if (!group) - soldier->say(text::get(text::SAY_FIRING)); - break; + default:; } } } -void state_t::spawn_soldier(v2f_t x) -{ - unit_soldier_t *soldier; - world::cmodel_t cmodel; - - soldier = new unit_soldier_t(this); - soldier->place(&world, x); - - cmodel.bounds = soldier->cmodel.bounds; - cmodel.cflags = soldier->move.cflags; - - if (world.test_rect(&cmodel, soldier)) - soldier->destroy(); -} - void state_t::pause(void) { paused = true; diff --git a/src/game/game.hpp b/src/game/game.hpp index 29b3d3b..5cf4494 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -60,9 +60,9 @@ namespace game { render::oriented_sprite_4M_t head_idle, body_idle; render::oriented_sprite_4M_t body_aiming, body_firing; render::oriented_sprite_4M2_t legs_idle, legs_walking; - render::animated_texture_t dead; + render::animated_texture_t dead, gibbing; - audio::sound_t fire, step_stone, step_dirt; + audio::sound_t fire, step_stone, step_dirt, gib_sound; } soldier_assets_t; typedef struct { @@ -74,6 +74,11 @@ namespace game { render::animated_texture_t idle, dead; } nest_assets_t; + typedef struct { + render::animated_texture_t idle, dead; + audio::sound_t sound; + } repl_assets_t; + typedef struct { render::animated_texture_t blood, flash; } fx_assets_t; @@ -87,6 +92,7 @@ namespace game { extern soldier_assets_t soldier; extern spider_assets_t spider; extern nest_assets_t nest; + extern repl_assets_t repl; extern fx_assets_t fx; extern deco_assets_t deco; extern audio::ambient_t ambients[AMBIENT_COUNT]; @@ -109,19 +115,15 @@ namespace game { UNPAUSED, FOLLOWING_ON, FOLLOWING_OFF, - SAY_GROUP, SAY_NO_PATH, SAY_READY, - SAY_READY_GROUP, SAY_MOVING, - SAY_MOVING_GROUP, SAY_STOPPING, - SAY_STOPPING_GROUP, SAY_FIRING, - SAY_FIRING_GROUP, UNIT_NAME_SPIDER, UNIT_NAME_SOLDIER, UNIT_NAME_NEST, + UNIT_NAME_REPL, UNIT_DEATH } index_t; @@ -167,9 +169,11 @@ namespace game { typedef enum { UNIT_SOLDIER, UNIT_SPIDER, - UNIT_NEST + UNIT_NEST, + UNIT_REPL } type_t; + game::state_t *game; type_t type; std::string name; @@ -214,9 +218,7 @@ namespace game { }; class unit_soldier_t : public unit_t { - friend state_t; - - protected: + public: double last_target_time = -INFINITY; v2f_t last_target_x; @@ -231,7 +233,7 @@ namespace game { void shoot(v2f_t aim); void target_and_attack(void); - public: + unit_soldier_t(game::state_t *game_); ~unit_soldier_t(void) {}; void render_to(render::state_t *render); @@ -273,6 +275,20 @@ namespace game { void on_wake(void) {}; }; + class unit_repl_t : public unit_t { + public: + unit_repl_t(game::state_t *game_); + ~unit_repl_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_death(void); + void on_wake(void) {}; + void activate(void); + }; + class effect_t : public game::entity_t { public: double ttl = +INFINITY; diff --git a/src/game/interface.cpp b/src/game/interface.cpp index 8a02e98..b187d42 100644 --- a/src/game/interface.cpp +++ b/src/game/interface.cpp @@ -259,10 +259,6 @@ void state_t::tick(double dt) start_following(); break; - case sf::Keyboard::Key::H: - game->spawn_soldier(wmouse); - break; - case sf::Keyboard::Key::F1: debug_draw_cmodels ^= 1; print("debug_draw_cmodels = " + std::to_string(debug_draw_cmodels)); diff --git a/src/game/text.cpp b/src/game/text.cpp index dfb2aef..9b5503b 100644 --- a/src/game/text.cpp +++ b/src/game/text.cpp @@ -40,28 +40,19 @@ static std::string get_english(index_t index) case FOLLOWING_OFF: return "Following: off."; - case SAY_GROUP: - return "Group"; - case SAY_NO_PATH: return "I can't get there."; case SAY_READY: - case SAY_READY_GROUP: return "Ready for orders."; case SAY_MOVING: return "On my way."; - case SAY_MOVING_GROUP: - return "On our way."; - case SAY_STOPPING: - case SAY_STOPPING_GROUP: return "Stopping."; case SAY_FIRING: - case SAY_FIRING_GROUP: return "Firing!"; case UNIT_NAME_SPIDER: @@ -73,6 +64,9 @@ static std::string get_english(index_t index) case UNIT_NAME_NEST: return "Nest"; + case UNIT_NAME_REPL: + return "Replicator"; + case UNIT_DEATH: return "died"; diff --git a/src/game/unit_nest.cpp b/src/game/unit_nest.cpp index 9b3e1e1..a46aa79 100644 --- a/src/game/unit_nest.cpp +++ b/src/game/unit_nest.cpp @@ -37,7 +37,7 @@ unit_nest_t::unit_nest_t(game::state_t *game_) : unit_t(game_, UNIT_NEST) next_spawning = game->now + 5.0; } -void spawn_spider(game::state_t *game, v2f_t nest) +static void spawn_spider(game::state_t *game, v2f_t nest) { for (size_t i = 0; i < 5; i++) { // Try a few times before giving up. v2f_t offset, x; diff --git a/src/game/unit_repl.cpp b/src/game/unit_repl.cpp new file mode 100644 index 0000000..fbefcd2 --- /dev/null +++ b/src/game/unit_repl.cpp @@ -0,0 +1,85 @@ +/* +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 { + +unit_repl_t::unit_repl_t(game::state_t *game_) : unit_t(game_, UNIT_REPL) +{ + size[0] = {-0.4f, -0.2f}; + size[1] = {+0.4f, +0.6f}; + render_size = size; + render_layer = -2; + cmodel.cflags = CF_BACKGROUND; + + name = text::get(text::UNIT_NAME_REPL); + ignore_waking = false; + health = max_health = 35; + + friendly = true; + controllable = true; +} + +void unit_repl_t::on_think(void) +{ +} + +void unit_repl_t::on_spawn(void) +{ +} + +void unit_repl_t::on_death(void) +{ +} + +void unit_repl_t::render_to(render::state_t *render) +{ + if (!dead) + render->render(game->now, &assets::repl.idle, render_bounds); + else + render->render(game->now, &assets::repl.dead, render_bounds); + + unit_t::render_to(render); +} + +void unit_repl_t::activate(void) +{ + 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)) + { + unit_t *unit; + + if (ent == soldier) + continue; + + if (ent->type != ET_UNIT) + continue; + + unit = dynamic_cast(ent); + unit->damage(200, NULL); + } + + assets::repl.sound.play_3d(x); +} + +} diff --git a/src/game/unit_soldier.cpp b/src/game/unit_soldier.cpp index e7d3849..4112d9a 100644 --- a/src/game/unit_soldier.cpp +++ b/src/game/unit_soldier.cpp @@ -147,8 +147,13 @@ void unit_soldier_t::on_think(void) void unit_soldier_t::on_death(void) { - render_size[0] = v2f_t(-0.75f, -0.5f); - render_size[1] = v2f_t(+0.75f, +0.5f); + if (health >= -10) { + render_size[0] = v2f_t(-0.75f, -0.5f); + render_size[1] = v2f_t(+0.75f, +0.5f); + } else { + assets::soldier.gib_sound.play_3d(x); + } + render_layer = -1; cmodel.cflags = CF_BACKGROUND; place(world, x); @@ -200,8 +205,14 @@ void unit_soldier_t::render_to(render::state_t *render) 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); + } else { + float phase = clamp((game->now - death_time) * 5, 0, 0.9); + + if (health < -10) + render->render(phase, &assets::soldier.gibbing, render_bounds); + else + render->render(phase, &assets::soldier.dead, render_bounds); + } unit_t::render_to(render); } diff --git a/src/game/units.cpp b/src/game/units.cpp index bbddd93..e266782 100644 --- a/src/game/units.cpp +++ b/src/game/units.cpp @@ -21,6 +21,7 @@ namespace game { unit_t::unit_t(game::state_t *game_, unit_t::type_t type_) : game::entity_t(game_, ET_UNIT) { + game = game_; type = type_; } -- cgit