summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/assets.cpp7
-rw-r--r--src/game/game.cpp46
-rw-r--r--src/game/game.hpp28
-rw-r--r--src/game/interface.cpp4
-rw-r--r--src/game/unit_builder.cpp153
-rw-r--r--src/game/unit_repl.cpp44
-rw-r--r--src/game/unit_soldier.cpp32
-rw-r--r--src/game/units.cpp2
8 files changed, 280 insertions, 36 deletions
diff --git a/src/game/assets.cpp b/src/game/assets.cpp
index 1a84b62..96666a3 100644
--- a/src/game/assets.cpp
+++ b/src/game/assets.cpp
@@ -21,6 +21,7 @@ namespace game::assets {
soldier_assets_t soldier;
scientist_assets_t scientist;
+builder_assets_t builder;
spider_assets_t spider;
nest_assets_t nest;
repl_assets_t repl;
@@ -67,6 +68,12 @@ void load(void)
scientist.laugh.load("assets/units/scientist/laugh2.ogg");
scientist.gather.load("assets/units/scientist/gather.ogg");
+ builder.body_idle.load("assets/units/builder/body_idle", 1, 1, 1);
+ builder.head_idle.load("assets/units/builder/head_idle", 1, 1, 1);
+ builder.dead.load("assets/units/builder/dead_", 1);
+ builder.gibbing.load("assets/units/builder/gibbing_", 3);
+ builder.avatar.load("assets/units/builder/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/game.cpp b/src/game/game.cpp
index a74a906..086b926 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -161,6 +161,10 @@ void state_t::select(rectf_t rect, int type)
unit->say("Я готов.", false);
break;
+ case unit_t::UNIT_BUILDER:
+ unit->say("Gotowy na rozkaz.", false);
+ break;
+
default:
break;
}
@@ -179,13 +183,15 @@ enum {
COMMAND_REPL_SOLDIER,
COMMAND_REPL_SCIENTIST,
+ COMMAND_REPL_BUILDER,
COMMAND_GATHER
};
bool state_t::populate_pie_menu(std::vector<interface::pie_item_t> &items)
{
- bool soldiers = false, repls = false, rockets = false, scientists = false;
+ bool soldiers = false, repls = false, rockets = false,
+ scientists = false, builders = false;
items.clear();
@@ -207,6 +213,10 @@ bool state_t::populate_pie_menu(std::vector<interface::pie_item_t> &items)
scientists = true;
break;
+ case unit_t::UNIT_BUILDER:
+ builders = true;
+ break;
+
case unit_t::UNIT_REPL:
repls = true;
break;
@@ -215,7 +225,7 @@ bool state_t::populate_pie_menu(std::vector<interface::pie_item_t> &items)
}
}
- if (soldiers || scientists) {
+ if (soldiers || scientists || builders) {
items.push_back((interface::pie_item_t){"Move", COMMAND_MOVE});
items.push_back((interface::pie_item_t){"Stop", COMMAND_STOP});
}
@@ -229,9 +239,13 @@ bool state_t::populate_pie_menu(std::vector<interface::pie_item_t> &items)
if (scientists)
items.push_back((interface::pie_item_t){"Gather", COMMAND_GATHER});
+ if (builders)
+ items.push_back((interface::pie_item_t){"TODO", -1});
+
if (repls) {
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});
+ items.push_back((interface::pie_item_t){"Spawn a builder", COMMAND_REPL_BUILDER});
}
return true;
@@ -308,6 +322,25 @@ static void command_scientist(unit_scientist_t *scientist, v2f_t x, int number)
}
}
+static void command_builder(unit_builder_t *builder, v2f_t x, int number)
+{
+ switch (number) {
+ case COMMAND_MOVE:
+ if (!builder->start_moving(x))
+ builder->say("Nie ma tam drogi.", false);
+ else {
+ builder->move_marker = std::make_unique<fx_move_marker_t>(builder->game, builder->move.path.back());
+ builder->say("W drodze.", false);
+ }
+ break;
+
+ case COMMAND_STOP:
+ builder->stop_moving();
+ builder->say("Stop.", false);
+ break;
+ }
+}
+
static void command_repl(unit_repl_t *repl, v2f_t x, int number)
{
switch (number) {
@@ -318,6 +351,10 @@ static void command_repl(unit_repl_t *repl, v2f_t x, int number)
case COMMAND_REPL_SCIENTIST:
repl->activate(unit_t::UNIT_SCIENTIST);
break;
+
+ case COMMAND_REPL_BUILDER:
+ repl->activate(unit_t::UNIT_BUILDER);
+ break;
}
}
@@ -349,6 +386,11 @@ void state_t::command(v2f_t x, int number)
x, number);
break;
+ case unit_t::UNIT_BUILDER:
+ command_builder(dynamic_cast<unit_builder_t*>(unit),
+ x, number);
+ break;
+
case unit_t::UNIT_REPL:
command_repl(dynamic_cast<unit_repl_t*>(unit),
x, number);
diff --git a/src/game/game.hpp b/src/game/game.hpp
index bc9c249..e48e2a3 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -210,6 +210,12 @@ namespace game {
} scientist_assets_t;
typedef struct {
+ render::oriented_sprite_4M_t head_idle, body_idle;
+ render::animated_texture_t dead, gibbing;
+ render::animated_texture_t avatar;
+ } builder_assets_t;
+
+ typedef struct {
render::oriented_sprite_4M_t idle, walking;
render::animated_texture_t dead;
@@ -242,6 +248,7 @@ namespace game {
extern soldier_assets_t soldier;
extern scientist_assets_t scientist;
+ extern builder_assets_t builder;
extern spider_assets_t spider;
extern nest_assets_t nest;
extern repl_assets_t repl;
@@ -295,6 +302,7 @@ namespace game {
typedef enum {
UNIT_SOLDIER,
UNIT_SCIENTIST,
+ UNIT_BUILDER,
UNIT_SPIDER,
UNIT_NEST,
UNIT_REPL
@@ -408,6 +416,26 @@ namespace game {
void on_death(void);
};
+ class unit_builder_t : public unit_t {
+ bool gibbed = false;
+
+ public:
+ std::unique_ptr<fx_move_marker_t> move_marker;
+
+ sf::Color selection_color;
+
+ unit_builder_t(game::state_t *game_);
+ ~unit_builder_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_);
diff --git a/src/game/interface.cpp b/src/game/interface.cpp
index fbde982..5b89f58 100644
--- a/src/game/interface.cpp
+++ b/src/game/interface.cpp
@@ -356,6 +356,10 @@ static void render_avatar(render::state_t *render, game::unit_t *unit, v2f_t at,
image = &assets::scientist.avatar;
break;
+ case game::unit_t::UNIT_BUILDER:
+ image = &assets::builder.avatar;
+ break;
+
case game::unit_t::UNIT_REPL:
image = &assets::repl.avatar;
break;
diff --git a/src/game/unit_builder.cpp b/src/game/unit_builder.cpp
new file mode 100644
index 0000000..1f5b81c
--- /dev/null
+++ b/src/game/unit_builder.cpp
@@ -0,0 +1,153 @@
+/*
+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 {
+
+static std::string builder_name(procgen::prng_t *prng)
+{
+ std::stringstream ss;
+ static const char *names[] = {
+ "Janusz", "Henryk", "Wiesław", "Bolesław", "Edward",
+ "Roman", "Mirosław", "Zbigniew", "Jerzy", "Józef", "Jan"
+ };
+
+ ss << "Pan ";
+ ss << names[prng->next() % (sizeof(names) / sizeof(names[0]))];
+
+ return ss.str();
+}
+
+unit_builder_t::unit_builder_t(game::state_t *game) : unit_t(game, UNIT_BUILDER)
+{
+ 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 = builder_name(&game->prng);
+
+ wake();
+ friendly = true;
+ controllable = true;
+
+ health = max_health = 45;
+}
+
+void unit_builder_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_builder_t::on_damage(unit_t *attacker)
+{
+ if (health < -30) {
+ assets::fx.gibbing.play_3d(x);
+ game->deletion_list.insert(this);
+ return;
+ } else if (health < -10) {
+ render_size[0] = v2f_t(-0.5f, -1.2f);
+ render_size[1] = v2f_t(+0.5f, +0.3f);
+
+ if (!gibbed)
+ assets::fx.gibbing.play_3d(x);
+ else
+ assets::fx.corpse_hit.play_3d(x);
+
+ place(world, x);
+ gibbed = true;
+ } else if (health <= 0) {
+ render_size[0] = v2f_t(-0.75f, -0.5f);
+ render_size[1] = v2f_t(+0.75f, +0.5f);
+ if (!dead)
+ assets::soldier.death.play_3d(x);
+ else
+ assets::fx.corpse_hit.play_3d(x);
+
+ place(world, x);
+ } else
+ assets::soldier.pain.play_3d(x);
+}
+
+void unit_builder_t::on_death(void)
+{
+ render_layer = render::LAYER_FLAT;
+ cmodel.cflags = CF_BACKGROUND;
+ place(world, x);
+ controllable = false;
+ move_marker.reset();
+}
+
+void unit_builder_t::render_to(render::state_t *render)
+{
+ sf::Color selection_color;
+
+ 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 (!dead) {
+ render::oriented_sprite_t *legs;
+
+ if (move.moving && !move.blocked)
+ legs = &assets::soldier.legs_walking;
+ else
+ legs = &assets::soldier.legs_idle;
+
+ render->render(game->now * 10, legs, render_bounds, move.angle);
+ render->render(game->now * 10, &assets::builder.body_idle, render_bounds, move.angle);
+ render->render(game->now * 10, &assets::builder.head_idle, render_bounds, move.angle);
+ } else {
+ float phase = clamp<float>((game->now - death_time) * 5, 0, 0.9);
+
+ if (gibbed)
+ render->render(phase, &assets::builder.gibbing, render_bounds);
+ else
+ render->render(phase, &assets::builder.dead, render_bounds);
+ }
+
+ unit_t::render_to(render);
+}
+
+void unit_builder_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_repl.cpp b/src/game/unit_repl.cpp
index 57e18f8..6dfa005 100644
--- a/src/game/unit_repl.cpp
+++ b/src/game/unit_repl.cpp
@@ -55,35 +55,39 @@ void unit_repl_t::render_to(render::state_t *render)
void unit_repl_t::activate(unit_t::type_t type)
{
world::cmodel_t cmodel;
- 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
+ unit_t *unit;
+
+ switch (type) {
+ case UNIT_SOLDIER:
+ unit = new unit_soldier_t(game);
+ break;
+
+ case UNIT_SCIENTIST:
+ unit = new unit_scientist_t(game);
+ break;
+
+ case UNIT_BUILDER:
+ unit = new unit_builder_t(game);
+ break;
+
+ default:
abort();
+ }
+
+ unit->place(world, x);
- for (world::entity_t *ent : game->world.get_entities(spawned->cmodel.bounds, CF_BODY|CF_BODY_SMALL))
+ for (world::entity_t *ent : game->world.get_entities(unit->cmodel.bounds, CF_BODY|CF_BODY_SMALL))
{
- unit_t *unit;
+ unit_t *other;
- if (ent == spawned)
+ if (ent == unit)
continue;
if (ent->type != ET_UNIT)
continue;
- unit = dynamic_cast<unit_t*>(ent);
- unit->damage(200, NULL);
+ other = dynamic_cast<unit_t*>(ent);
+ other->damage(200, NULL);
}
assets::repl.sound.play_3d(x);
diff --git a/src/game/unit_soldier.cpp b/src/game/unit_soldier.cpp
index 221667b..d511553 100644
--- a/src/game/unit_soldier.cpp
+++ b/src/game/unit_soldier.cpp
@@ -19,33 +19,33 @@ along with Minitrem. If not, see <http://www.gnu.org/licenses/>.
namespace game {
-static const char *initials[] = {
- "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
- "N", "O", "Ö", "P", "R", "S", "Ś", "T", "U", "Ü", "V", "W", "X",
- "Z"
-};
-
static std::string soldier_name(procgen::prng_t *prng)
{
std::stringstream ss;
static const char *names[] = {
- "Kowalski", "O'Neill", "Hammond", "Mitchell", "Hitler",
- "Stalin", "Wyspiański", "Bush", "Washington", "Jaruzelski",
- "Jackson", "Carter",
+ "Heinrich", "Hans", "Ernst", "Dieter", "Werner", "Wolfgang",
+ "Uwe", "Klaus", "Adolf"
+ };
+ static const char *surnames[] = {
+ "Kloss", "Schmidt", "Göring", "Fischer", "Müller", "Weber",
+ "Schulz", "Schäfer", "Klein", "Wofl", "Schröder", "Neumann",
+ "Zimmermann"
};
- ss << initials[prng->next() % (sizeof(initials) / sizeof(initials[0]))];
- ss << ". ";
ss << names[prng->next() % (sizeof(names) / sizeof(names[0]))];
+ ss << " ";
+ ss << surnames[prng->next() % (sizeof(surnames) / sizeof(surnames[0]))];
return ss.str();
}
+const float soldier_head_offset = 0.10f;
+
unit_soldier_t::unit_soldier_t(game::state_t *game) : unit_t(game, UNIT_SOLDIER)
{
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[0] = v2f_t(-0.5f, -1.2f - soldier_head_offset);
render_size[1] = v2f_t(+0.5f, +0.3f);
cmodel.cflags = CF_BODY;
move.cflags = CF_SOLID | CF_BODY | CF_WATER;
@@ -240,9 +240,13 @@ void unit_soldier_t::render_to(render::state_t *render)
}
if (!dead) {
+ rectf_t rect_body;
render::oriented_sprite_t *legs, *body;
float body_angle;
+ rect_body = render_bounds;
+ rect_body[0][1] += soldier_head_offset;
+
if (move.moving && !move.blocked)
legs = &assets::soldier.legs_walking;
else
@@ -261,8 +265,8 @@ void unit_soldier_t::render_to(render::state_t *render)
body_angle = move.angle;
}
- render->render(game->now * 10, legs, render_bounds, move.angle);
- render->render(game->now * 10, body, render_bounds, body_angle);
+ render->render(game->now * 10, legs, rect_body, move.angle);
+ render->render(game->now * 10, body, rect_body, body_angle);
render->render(game->now * 10, &assets::soldier.head_idle, render_bounds, body_angle);
} else {
diff --git a/src/game/units.cpp b/src/game/units.cpp
index 26cf808..e9abda4 100644
--- a/src/game/units.cpp
+++ b/src/game/units.cpp
@@ -199,6 +199,7 @@ void unit_t::damage(int points, unit_t *attacker)
switch (type) {
case UNIT_SOLDIER:
case UNIT_SCIENTIST:
+ case UNIT_BUILDER:
alien = false;
break;
@@ -220,6 +221,7 @@ void unit_t::die(unit_t *killer)
switch (type) {
case UNIT_SOLDIER:
case UNIT_SCIENTIST:
+ case UNIT_BUILDER:
case UNIT_SPIDER:
game->interface.print(name + " died.");
break;