summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/assets.cpp9
-rw-r--r--src/game/game.cpp170
-rw-r--r--src/game/game.hpp40
-rw-r--r--src/game/interface.cpp4
-rw-r--r--src/game/text.cpp12
-rw-r--r--src/game/unit_nest.cpp2
-rw-r--r--src/game/unit_repl.cpp85
-rw-r--r--src/game/unit_soldier.cpp19
-rw-r--r--src/game/units.cpp1
9 files changed, 226 insertions, 116 deletions
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<world::entity_t*> 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<interface::pie_item_t> &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<fx_move_marker_t>(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_soldier_t*>(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<fx_move_marker_t>(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_soldier_t*>(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_repl_t*>(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 {
@@ -75,6 +75,11 @@ namespace game {
} 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<unit_t*>(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<float>((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_;
}