summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2018-04-21 16:45:40 +0200
committerPaweł Redman <pawel.redman@gmail.com>2018-04-21 16:45:40 +0200
commitf43f52903f29d010a72183de24ae8f56ab6702bb (patch)
tree3e4ef5c81644086891ad9fb78fce0cb7e5c1858e /src
parentb84b39f099e8a1f66fb95d371b60b1546fb3142a (diff)
Add the scientist.
Diffstat (limited to 'src')
-rw-r--r--src/common.hpp1
-rw-r--r--src/game/assets.cpp6
-rw-r--r--src/game/decos.cpp2
-rw-r--r--src/game/game.cpp57
-rw-r--r--src/game/game.hpp35
-rw-r--r--src/game/interface.cpp4
-rw-r--r--src/game/unit_repl.cpp30
-rw-r--r--src/game/unit_scientist.cpp115
-rw-r--r--src/game/unit_soldier.cpp35
-rw-r--r--src/game/units.cpp28
10 files changed, 262 insertions, 51 deletions
diff --git a/src/common.hpp b/src/common.hpp
index a3ba6db..54d83f8 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -323,6 +323,7 @@ namespace game {
void pause(void);
void resume(void);
+ void wake_area(v2f_t x);
void explosion(v2f_t x);
void hivemind_alert(v2f_t x, float r, bool do_move, v2f_t move_to);
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 <http://www.gnu.org/licenses/>.
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<interface::pie_item_t> &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<interface::pie_item_t> &items)
switch (unit->type) {
case unit_t::UNIT_SOLDIER:
+ can_move = true;
soldiers = true;
if (!dynamic_cast<unit_soldier_t*>(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<interface::pie_item_t> &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<fx_move_marker_t>(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_scientist_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 1cd61aa..908d73b 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -19,6 +19,7 @@ along with Minitrem. If not, see <http://www.gnu.org/licenses/>.
namespace game {
enum {
+ ET_NONE,
ET_UNIT,
ET_EFFECT,
ET_DECO,
@@ -71,6 +72,12 @@ namespace game {
} 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<fx_move_marker_t> move_marker;
+ std::unique_ptr<fx_aim_marker_t> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<game::entity_t*>(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<game::entity_t*>(went);
+
+ // WTF?
+ if (!ent)
+ continue;
+
+ // Wake everything around.
+ if (!ent->ignore_waking) {
+ ent->wake_time = now;
+
+ if (!ent->awake)
+ ent->wake();
+ }
+ }
+}
+
} // namespace game