From 86872106dc9aa960b417b5ff7f4a175c81058411 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Thu, 26 Apr 2018 11:44:23 +0200 Subject: Add the replicator. --- src/game/assets.cpp | 6 +++ src/game/game.cpp | 58 ++++++++++++++++++++++- src/game/game.hpp | 27 ++++++++++- src/game/interface.cpp | 4 ++ src/game/unit_builder.cpp | 18 ++++++-- src/game/unit_replicator.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++ src/game/unit_soldier.cpp | 66 +++++++++++++++++++++++++++ 7 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 src/game/unit_replicator.cpp (limited to 'src') diff --git a/src/game/assets.cpp b/src/game/assets.cpp index 35e9880..e2b9664 100644 --- a/src/game/assets.cpp +++ b/src/game/assets.cpp @@ -25,6 +25,7 @@ builder_assets_t builder; spider_assets_t spider; nest_assets_t nest; teleporter_assets_t teleporter; +replicator_assets_t replicator; fx_assets_t fx; deco_assets_t deco; audio::ambient_t ambients[AMBIENT_COUNT]; @@ -103,6 +104,11 @@ void load(void) teleporter.damage.load("assets/units/teleporter/damage2.ogg"); teleporter.damage.load("assets/units/teleporter/damage3.ogg"); + replicator.idle.load("assets/units/replicator/idle_", 1); + replicator.working.load("assets/units/replicator/working_", 3); + replicator.unfinished.load("assets/units/replicator/unfinished_", 1); + replicator.avatar.load("assets/units/replicator/avatar_", 1); + 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 2e15024..b10bc08 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -182,6 +182,8 @@ enum { COMMAND_FIRE, COMMAND_THROW_GRENADE, COMMAND_STOP, + COMMAND_RESTOCK_SHELLS, + COMMAND_RESTOCK_GRENADES, COMMAND_HIRE_SOLDIER, COMMAND_HIRE_SCIENTIST, @@ -190,13 +192,18 @@ enum { COMMAND_GATHER, COMMAND_REPAIR, - COMMAND_BUILD_TELEPORTER + COMMAND_BUILD_TELEPORTER, + COMMAND_BUILD_REPLICATOR, + + COMMAND_REPLICATE_SHELLS, + COMMAND_REPLICATE_GRENADES, }; bool state_t::populate_pie_menu(std::vector &items) { bool soldiers = false, teleporters = false, grenades = false, - scientists = false, builders = false; + scientists = false, builders = false, replicators = false, + restock_shells = false, restock_grenades = false; items.clear(); @@ -212,6 +219,10 @@ bool state_t::populate_pie_menu(std::vector &items) soldiers = true; if (unit->storage.grenades) grenades = true; + if (unit->storage.shells < unit->storage.max_shells) + restock_shells = true; + if (unit->storage.grenades < unit->storage.max_grenades) + restock_grenades = true; break; case unit_t::UNIT_SCIENTIST: @@ -226,6 +237,10 @@ bool state_t::populate_pie_menu(std::vector &items) teleporters = true; break; + case unit_t::UNIT_REPLICATOR: + replicators = true; + break; + default:; } } @@ -239,6 +254,10 @@ bool state_t::populate_pie_menu(std::vector &items) items.push_back((interface::pie_item_t){"Fire", COMMAND_FIRE}); if (grenades) items.push_back((interface::pie_item_t){"Throw a grenade", COMMAND_THROW_GRENADE}); + if (restock_shells) + items.push_back((interface::pie_item_t){"Restock shells", COMMAND_RESTOCK_SHELLS}); + if (restock_grenades) + items.push_back((interface::pie_item_t){"Restock grenades", COMMAND_RESTOCK_GRENADES}); } if (scientists) @@ -247,6 +266,7 @@ bool state_t::populate_pie_menu(std::vector &items) if (builders) { items.push_back((interface::pie_item_t){"Repair", COMMAND_REPAIR}); items.push_back((interface::pie_item_t){"Build a teleporter", COMMAND_BUILD_TELEPORTER}); + items.push_back((interface::pie_item_t){"Build a replicator", COMMAND_BUILD_REPLICATOR}); } if (teleporters) { @@ -255,6 +275,11 @@ bool state_t::populate_pie_menu(std::vector &items) items.push_back((interface::pie_item_t){"Hire a builder", COMMAND_HIRE_BUILDER}); } + if (replicators) { + items.push_back((interface::pie_item_t){"Replicate shells", COMMAND_REPLICATE_SHELLS}); + items.push_back((interface::pie_item_t){"Replicate grenades", COMMAND_REPLICATE_GRENADES}); + } + return true; } @@ -286,6 +311,14 @@ static void command_soldier(unit_soldier_t *soldier, v2f_t x, int number) case COMMAND_THROW_GRENADE: soldier->command_throw_grenade(x); break; + + case COMMAND_RESTOCK_SHELLS: + soldier->command_restock(false); + break; + + case COMMAND_RESTOCK_GRENADES: + soldier->command_restock(true); + break; } } @@ -339,6 +372,10 @@ static void command_builder(unit_builder_t *builder, v2f_t x, int number) case COMMAND_BUILD_TELEPORTER: builder->command_build(x, unit_t::UNIT_TELEPORTER); break; + + case COMMAND_BUILD_REPLICATOR: + builder->command_build(x, unit_t::UNIT_REPLICATOR); + break; } } @@ -359,6 +396,19 @@ static void command_teleporter(unit_teleporter_t *teleporter, v2f_t x, int numbe } } +static void command_replicator(unit_replicator_t *replicator, v2f_t x, int number) +{ + switch (number) { + case COMMAND_REPLICATE_SHELLS: + replicator->activate(false); + break; + + case COMMAND_REPLICATE_GRENADES: + replicator->activate(true); + break; + } +} + void state_t::command(v2f_t x, int number) { bool unlink; @@ -397,6 +447,10 @@ void state_t::command(v2f_t x, int number) x, number); break; + case unit_t::UNIT_REPLICATOR: + command_replicator(dynamic_cast(unit), + x, number); + default:; } } diff --git a/src/game/game.hpp b/src/game/game.hpp index f630c54..387df1a 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -238,6 +238,10 @@ namespace game { audio::sound_t sound, damage; } teleporter_assets_t; + typedef struct { + render::animated_texture_t idle, working, unfinished, avatar; + } replicator_assets_t; + typedef struct { render::animated_texture_t blood, flash, explosion, ricochet, water_splash; audio::sound_t gibbing, corpse_hit; @@ -265,6 +269,7 @@ namespace game { extern spider_assets_t spider; extern nest_assets_t nest; extern teleporter_assets_t teleporter; + extern replicator_assets_t replicator; extern fx_assets_t fx; extern deco_assets_t deco; extern audio::ambient_t ambients[AMBIENT_COUNT]; @@ -324,7 +329,8 @@ namespace game { UNIT_BUILDER, UNIT_SPIDER, UNIT_NEST, - UNIT_TELEPORTER + UNIT_TELEPORTER, + UNIT_REPLICATOR } type_t; game::state_t *game; @@ -413,6 +419,7 @@ namespace game { void on_death(void); void command_throw_grenade(v2f_t at); + void command_restock(bool grenades); }; class unit_scientist_t : public unit_t { @@ -518,6 +525,24 @@ namespace game { void activate(unit_t::type_t type); }; + class unit_replicator_t : public unit_t { + ntime_t last_activation = 0; + + public: + unit_replicator_t(game::state_t *game_); + ~unit_replicator_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); + + void activate(bool grenades); + }; + class effect_t : public game::entity_t { public: double ttl = +INFINITY; diff --git a/src/game/interface.cpp b/src/game/interface.cpp index 6375a3c..f6106e2 100644 --- a/src/game/interface.cpp +++ b/src/game/interface.cpp @@ -393,6 +393,10 @@ static void render_avatar(render::state_t *render, game::unit_t *unit, v2f_t at, image = &assets::teleporter.avatar; break; + case game::unit_t::UNIT_REPLICATOR: + image = &assets::replicator.avatar; + break; + default: image = &assets::deco.eyething; break; diff --git a/src/game/unit_builder.cpp b/src/game/unit_builder.cpp index 0deae3c..801639e 100644 --- a/src/game/unit_builder.cpp +++ b/src/game/unit_builder.cpp @@ -95,6 +95,11 @@ void unit_builder_t::command_build(v2f_t where, type_t what) price = 250; break; + case UNIT_REPLICATOR: + built = new unit_replicator_t(game); + price = 200; + break; + default: abort(); } @@ -107,7 +112,7 @@ void unit_builder_t::command_build(v2f_t where, type_t what) cmodel.bounds[0] = where + built->size[0]; cmodel.bounds[1] = where + built->size[1]; - cmodel.cflags = CF_SOLID|CF_SURFACE2|CF_WATER|CF_DECOS; + cmodel.cflags = CF_SOLID|CF_SURFACE2|CF_WATER|CF_DECOS|CF_BODY|CF_BODY_SMALL; if (world->test_rect(&cmodel, nullptr)) { say("Nie ma tutaj miejsca."); @@ -131,10 +136,14 @@ void unit_builder_t::repair(void) world::trace_t trace; unit_t *unit; - if (!move.moving) + if (!move.moving) { + world::cflags_t save = move.cflags; + move.cflags &= ~CF_SOLID; start_moving(repairing_at); + move.cflags = save; + } - if ((x - repairing_at).len() > 0.5f) + if ((x - repairing_at).len() > 1.0f) return; if (next_repair && next_repair > game->time) @@ -147,7 +156,8 @@ void unit_builder_t::repair(void) } unit = dynamic_cast(trace.ent); - if (unit->type != UNIT_TELEPORTER || unit->health >= unit->max_health) { + if ((unit->type != UNIT_TELEPORTER && unit->type != UNIT_REPLICATOR) + || unit->health >= unit->max_health) { repairing = false; return; } diff --git a/src/game/unit_replicator.cpp b/src/game/unit_replicator.cpp new file mode 100644 index 0000000..5d2eef5 --- /dev/null +++ b/src/game/unit_replicator.cpp @@ -0,0 +1,106 @@ +/* +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_replicator_t::unit_replicator_t(game::state_t *game_) : unit_t(game_, UNIT_REPLICATOR) +{ + size[0] = {-0.4f, -0.2f}; + size[1] = {+0.4f, +0.4f}; + render_size[0] = {-0.4f, -0.666667f}; + render_size[1] = {+0.4f, +0.4f}; + cmodel.cflags = CF_SOLID; + + name = "Replicator"; + ignore_waking = false; + max_health = 30; + health = 4; + + friendly = true; + controllable = true; + constructed = false; + + storage.shells = 0; + storage.max_shells = 200; + storage.grenades = 0; + storage.max_grenades = 20; +} + +void unit_replicator_t::on_damage(unit_t *attacker) +{ + assets::teleporter.damage.play_3d(x); +} + +void unit_replicator_t::on_death(void) +{ + game->explosion(x); + game->deletion_list.insert(this); +} + +void unit_replicator_t::activate(bool grenades) +{ + size_t amount, price; + + if (grenades) { + amount = 2; + price = 10; + + if (storage.max_grenades - storage.grenades < amount) { + say("Storage full."); + return; + } + } else { + amount = 15; + price = 8; + + if (storage.max_shells - storage.shells < amount) { + say("Storage full."); + return; + } + } + + if (game->crystals < price) { + game->interface.print("Insufficient crystals; " + std::to_string(price - game->crystals) + " more needed."); + return; + } + + game->crystals -= price; + + if (grenades) + storage.grenades += amount; + else + storage.shells += amount; + + assets::teleporter.sound.play_3d(x); + last_activation = game->time; +} + +void unit_replicator_t::render_to(render::state_t *render) +{ + if (!constructed) + render->render(game->now, &assets::replicator.unfinished, render_bounds); + else if (last_activation && game->time - last_activation < MSEC(300)) + render->render(30 * game->now, &assets::replicator.working, render_bounds); + else + render->render(game->now, &assets::replicator.idle, render_bounds); + + unit_t::render_to(render); +} + +} diff --git a/src/game/unit_soldier.cpp b/src/game/unit_soldier.cpp index 4747060..1750e2e 100644 --- a/src/game/unit_soldier.cpp +++ b/src/game/unit_soldier.cpp @@ -250,6 +250,72 @@ void unit_soldier_t::command_throw_grenade(v2f_t at) grenade->place(&game->world); } +void unit_soldier_t::command_restock(bool grenades) +{ + rectf_t rect; + bool replicators = false; + size_t needed, taken = 0; + + if (grenades) + needed = storage.max_grenades - storage.grenades; + else + needed = storage.max_shells - storage.shells; + + if (!needed) { + say("Ich kann nicht mehr tragen."); + return; + } + + rect = rectf_t::around(x, 2.0f); + for (world::entity_t *ent : game->world.get_entities(rect, CF_SOLID)) { + world::trace_t trace; + unit_t *unit; + size_t take; + + if (ent->type != ET_UNIT) + continue; + + unit = dynamic_cast(ent); + if (unit->type != UNIT_REPLICATOR) + continue; + + trace = world->ray_v_all(x, unit->x, CF_SOLID, this); + if (trace.hit && trace.ent != ent) + continue; + + replicators = true; + + if (grenades) { + take = std::min(needed, unit->storage.grenades); + taken += take; + unit->storage.grenades -= take; + } else { + take = std::min(needed, unit->storage.shells); + taken += take; + unit->storage.shells -= take; + } + } + + if (!replicators) { + say("Es gibt keine Replikators."); + return; + } + + if (!taken) { + if (grenades) + say("Ich konnte keine Granaten finden."); + else + say("Ich konnte keine Munition finden."); + + return; + } + + if (grenades) + storage.grenades += taken; + else + storage.shells += taken; +} + void unit_soldier_t::render_to(render::state_t *render) { sf::Color selection_color; -- cgit