summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.hpp5
-rw-r--r--src/game/assets.cpp4
-rw-r--r--src/game/effects.cpp38
-rw-r--r--src/game/game.cpp60
-rw-r--r--src/game/game.hpp36
-rw-r--r--src/game/interface.cpp4
-rw-r--r--src/game/unit_nest.cpp1
-rw-r--r--src/game/unit_repl.cpp2
-rw-r--r--src/game/unit_spider.cpp27
-rw-r--r--src/game/units.cpp36
-rw-r--r--src/render.cpp3
11 files changed, 173 insertions, 43 deletions
diff --git a/src/common.hpp b/src/common.hpp
index e6879ca..f4ce9ff 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -44,6 +44,7 @@ extern bool debug_AI;
typedef uint64_t ntime_t;
ntime_t nclock(void);
+#define SEC(x) ((ntime_t)x * 1000000000)
#define MSEC(x) ((ntime_t)x * 1000000)
class freq_counter_t {
@@ -306,6 +307,8 @@ namespace game {
procgen::prng_t prng;
std::unordered_set<entity_t*> awake_entities;
std::unordered_set<unit_t*> selected_units;
+ // Some deletes have to be deferred to the next frame.
+ std::unordered_set<entity_t*> deletion_list;
ntime_t time = 0; // game time
double now, dt; // FIXME: refactor the code, use ntime_t everywhere
@@ -322,6 +325,8 @@ namespace game {
void pause(void);
void resume(void);
+ void explosion(v2f_t x);
+
// These are called by the interface.
void select(rectf_t rect, int type);
bool populate_pie_menu(std::vector<interface::pie_item_t> &items);
diff --git a/src/game/assets.cpp b/src/game/assets.cpp
index 553cafd..2009672 100644
--- a/src/game/assets.cpp
+++ b/src/game/assets.cpp
@@ -67,6 +67,10 @@ void load(void)
fx.blood.load("assets/units/blood_", 4);
fx.flash.load("assets/units/flash_", 1);
+ fx.explosion.load("assets/fx/explosion_", 7);
+ fx.explosion_sound.load("assets/fx/explosion.ogg");
+ fx.explosion_sound.volume = 3.0f;
+
deco.stone.load("assets/deco/stone_", 1);
deco.eyething.load("assets/deco/eyething_", 2);
deco.spike.load("assets/deco/spike_", 1);
diff --git a/src/game/effects.cpp b/src/game/effects.cpp
index a398527..6f92f17 100644
--- a/src/game/effects.cpp
+++ b/src/game/effects.cpp
@@ -26,7 +26,7 @@ effect_t::effect_t(state_t *game_) : game::entity_t(game_, ET_EFFECT)
void effect_t::on_think(void)
{
if (game->now >= ttl)
- destroy();
+ delete this;
}
fx_tracer_t::fx_tracer_t(state_t *game_, v2f_t x0_, v2f_t x1_) : effect_t(game_)
@@ -39,6 +39,7 @@ fx_tracer_t::fx_tracer_t(state_t *game_, v2f_t x0_, v2f_t x1_) : effect_t(game_)
render_bounds[0] = x0;
render_bounds[1] = x1;
render_bounds = render_bounds.norm();
+ render_layer = render::LAYER_TOP;
cmodel.bounds = render_bounds;
cmodel.cflags = 0;
@@ -72,6 +73,7 @@ fx_flash_t::fx_flash_t(state_t *game_, v2f_t x_, float radius_) : effect_t(game_
render_bounds[0] = x - v2f_t(radius, radius);
render_bounds[1] = x + v2f_t(radius, radius);
render_bounds = render_bounds.norm();
+ render_layer = render::LAYER_TOP;
cmodel.bounds = render_bounds;
cmodel.cflags = 0;
@@ -127,12 +129,6 @@ fx_move_marker_t::fx_move_marker_t(state_t *game_, v2f_t x_) : effect_t(game_)
link(&game->world);
}
-fx_move_marker_t::~fx_move_marker_t(void)
-{
- unlink();
- sleep();
-}
-
void fx_move_marker_t::render_to(render::state_t *render)
{
render->render(game->now * 2, &assets::move_marker, render_bounds, sf::Color::White);
@@ -151,15 +147,33 @@ fx_aim_marker_t::fx_aim_marker_t(state_t *game_, v2f_t x_) : effect_t(game_)
link(&game->world);
}
-fx_aim_marker_t::~fx_aim_marker_t(void)
+void fx_aim_marker_t::render_to(render::state_t *render)
{
- unlink();
- sleep();
+ render->render(game->now * 2, &assets::aim_marker, render_bounds, sf::Color::White);
}
-void fx_aim_marker_t::render_to(render::state_t *render)
+fx_explosion_t::fx_explosion_t(state_t *game_, v2f_t x_) : effect_t(game_)
{
- render->render(game->now * 2, &assets::aim_marker, render_bounds, sf::Color::White);
+ ttl = game->now + 1.3;
+
+ x = x_;
+
+ render_bounds[0] = x + v2f_t(-1.0, -2.5);
+ render_bounds[1] = x + v2f_t(1.0, 0.5);
+ render_bounds = render_bounds.norm();
+ cmodel.bounds = render_bounds;
+ cmodel.cflags = 0;
+
+ ignore_waking = true;
+ wake();
+
+ assets::fx.explosion_sound.play_3d(x);
+}
+
+void fx_explosion_t::render_to(render::state_t *render)
+{
+ double phase = (game->now - ttl) / 1.3f;
+ render->render(phase, &assets::fx.explosion, render_bounds, sf::Color::White);
}
} // namespace game
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 66bf237..6e8ea78 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -19,6 +19,12 @@ along with Minitrem. If not, see <http://www.gnu.org/licenses/>.
namespace game {
+bool load_assets(void)
+{
+ assets::load();
+ return true;
+}
+
size_t selection_cookie = 1;
entity_t::entity_t(game::state_t *game_, int type_) : world::entity_t(type_)
@@ -28,13 +34,8 @@ entity_t::entity_t(game::state_t *game_, int type_) : world::entity_t(type_)
entity_t::~entity_t(void)
{
-}
-
-void entity_t::destroy(void)
-{
+ sleep();
unlink();
- game->awake_entities.erase(this);
- delete this;
}
void entity_t::place(world::world_t *world_)
@@ -48,9 +49,6 @@ void entity_t::place(world::world_t *world_)
if (do_spawn)
on_spawn();
-
- if (!ignore_waking)
- wake();
}
void entity_t::place(world::world_t *world, v2f_t x_)
@@ -168,7 +166,7 @@ enum {
COMMAND_FIRE,
COMMAND_STOP,
- COMMAND_REPL
+ COMMAND_REPL,
};
bool state_t::populate_pie_menu(std::vector<interface::pie_item_t> &items)
@@ -303,11 +301,16 @@ void state_t::tick(ntime_t time_)
now = time * 1.0e-9;
dt = TIME_DELTA * 1.0e-9;
+ for (entity_t *ent : deletion_list)
+ delete ent;
+ deletion_list.clear();
+
// on_think can insert/erase elements of awake_entities so iterate
// over a copy of it.
auto copy = awake_entities;
for (entity_t *ent : copy)
- ent->on_think();
+ if (awake_entities.find(ent) != awake_entities.end())
+ ent->on_think();
frames++;
frames_since_t0++;
@@ -384,10 +387,39 @@ void state_t::compute_ambience(render::state_t *render)
}
}
-bool load_assets(void)
+void state_t::explosion(v2f_t x)
{
- assets::load();
- return true;
+ fx_explosion_t *explosion;
+
+ explosion = new fx_explosion_t(this, x);
+ explosion->place(&world);
+
+ for (size_t i = 0; i < 30; i++) {
+ v2f_t end;
+ world::trace_t trace;
+ unit_t *unit;
+ int damage;
+
+ end = x + prng.unit_vec2() * 6.0f;
+ trace = world.ray_v_all(x, end, CF_SOLID|CF_BODY|CF_BODY_SMALL, NULL);
+ if (!trace.hit)
+ continue;
+
+ if (!trace.ent)
+ continue;
+
+ if (trace.ent->type != ET_UNIT)
+ continue;
+
+ unit = dynamic_cast<unit_t*>(trace.ent);
+
+ if (unit->dead)
+ continue;
+
+ damage = pow(1 - trace.frac, 2.0f) * 40.0f;
+ printf("frac=%f, damage=%i\n", trace.frac, damage);
+ unit->damage(damage, nullptr);
+ }
}
} //namespace game
diff --git a/src/game/game.hpp b/src/game/game.hpp
index 4773f9c..6c71736 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -80,7 +80,8 @@ namespace game {
} repl_assets_t;
typedef struct {
- render::animated_texture_t blood, flash;
+ render::animated_texture_t blood, flash, explosion;
+ audio::sound_t explosion_sound;
} fx_assets_t;
typedef struct {
@@ -138,7 +139,6 @@ namespace game {
entity_t(game::state_t *game, int type_);
virtual ~entity_t(void) = 0;
- void destroy(void);
void place(world::world_t *world);
void place(world::world_t *world, v2f_t x_);
@@ -158,6 +158,7 @@ namespace game {
class fx_aim_marker_t;
unit_t *find_target(world::world_t *world, v2f_t x, float r, bool friendly);
+ void hivemind_attack(unit_t *source, v2f_t target);
class unit_t : public entity_t {
protected:
@@ -212,7 +213,11 @@ namespace game {
void damage(int points, unit_t *attacker);
void try_attack(unit_t *target);
void die(unit_t *killer);
- virtual void on_death() = 0;
+
+ virtual void on_damage(unit_t *attacker) = 0;
+ virtual void on_death(void) = 0;
+
+ bool have_target = false;
std::string say_text;
double say_time = -INFINITY;
@@ -247,6 +252,7 @@ namespace game {
void on_think(void);
void on_spawn(void) {};
void on_wake(void) {};
+ void on_damage(unit_t *attacker) {};
void on_death(void);
};
@@ -261,7 +267,8 @@ namespace game {
void on_think(void);
void on_spawn(void) {};
- void on_wake(void);
+ void on_wake(void) {};
+ void on_damage(unit_t *attacker);
void on_death(void);
};
@@ -276,8 +283,9 @@ namespace game {
void on_think(void);
void on_spawn(void);
- void on_death(void);
void on_wake(void) {};
+ void on_damage(unit_t *attacker) {};
+ void on_death(void);
};
class unit_repl_t : public unit_t {
@@ -289,8 +297,10 @@ namespace game {
void on_think(void);
void on_spawn(void);
- void on_death(void);
void on_wake(void) {};
+ void on_damage(unit_t *attacker) {};
+ void on_death(void);
+
void activate(void);
};
@@ -345,7 +355,7 @@ namespace game {
public:
fx_move_marker_t(game::state_t *game_, v2f_t x_);
- ~fx_move_marker_t(void);
+ ~fx_move_marker_t(void) {};
void render_to(render::state_t *render);
};
@@ -355,7 +365,17 @@ namespace game {
public:
fx_aim_marker_t(game::state_t *game_, v2f_t x_);
- ~fx_aim_marker_t(void);
+ ~fx_aim_marker_t(void) {};
+
+ void render_to(render::state_t *render);
+ };
+
+ class fx_explosion_t : public effect_t {
+ v2f_t x;
+
+ public:
+ fx_explosion_t(game::state_t *game_, v2f_t x_);
+ ~fx_explosion_t(void) {};
void render_to(render::state_t *render);
};
diff --git a/src/game/interface.cpp b/src/game/interface.cpp
index d249f38..c366fa8 100644
--- a/src/game/interface.cpp
+++ b/src/game/interface.cpp
@@ -129,7 +129,7 @@ void state_t::tick(double dt)
sf::Event event;
v2f_t view_size;
- v2f_t follow_center(0, 0), view_center, pan_delta;
+ v2f_t follow_center(0, 0), view_center, pan_delta = v2f_t(0, 0);
float view_scale;
sf::Vector2i mouse;
v2f_t wmouse; // Mouse position in world space;
@@ -139,8 +139,8 @@ void state_t::tick(double dt)
expfade<float>(&camera.zoom_s, camera.zoom, 15, dt);
view_scale = 4.5 * exp(camera.zoom_s * 0.12);
if (window_size[0] < window_size[1]) {
- view_size[1] = view_scale;
view_size[0] = view_scale * window_size[0] / window_size[1];
+ view_size[1] = view_scale;
} else {
view_size[0] = view_scale;
view_size[1] = view_scale * window_size[1] / window_size[0];
diff --git a/src/game/unit_nest.cpp b/src/game/unit_nest.cpp
index 163a585..cae98f5 100644
--- a/src/game/unit_nest.cpp
+++ b/src/game/unit_nest.cpp
@@ -26,6 +26,7 @@ unit_nest_t::unit_nest_t(game::state_t *game_) : unit_t(game_, UNIT_NEST)
size[1] = {+0.6f, +0.2f};
render_size[0] = {-0.6f, -0.6f};
render_size[1] = {+0.6f, +0.6f};
+ render_layer = render::LAYER_NORMAL;
cmodel.cflags = CF_BODY;
name = text::get(text::UNIT_NAME_NEST);
diff --git a/src/game/unit_repl.cpp b/src/game/unit_repl.cpp
index 8bfd835..e11d204 100644
--- a/src/game/unit_repl.cpp
+++ b/src/game/unit_repl.cpp
@@ -45,6 +45,8 @@ void unit_repl_t::on_spawn(void)
void unit_repl_t::on_death(void)
{
+ game->explosion(x);
+ delete this;
}
void unit_repl_t::render_to(render::state_t *render)
diff --git a/src/game/unit_spider.cpp b/src/game/unit_spider.cpp
index 9ff8fc1..df9073c 100644
--- a/src/game/unit_spider.cpp
+++ b/src/game/unit_spider.cpp
@@ -33,6 +33,8 @@ unit_spider_t::unit_spider_t(game::state_t *game) : unit_t(game, UNIT_SPIDER)
ignore_waking = false;
health = max_health = 4;
+
+ sleep();
}
void unit_spider_t::target_and_attack(void)
@@ -45,12 +47,12 @@ void unit_spider_t::target_and_attack(void)
target = find_target(world, x, 10.0f, true);
if (!target) {
- if (health < max_health)
- random_walk();
-
+ have_target = false;
return;
}
+ have_target = true;
+ wake_time = game->time;
start_moving(target->x);
next_targetting = game->now + game->prng.next_float(0.2f, 0.4f);
@@ -72,18 +74,31 @@ void unit_spider_t::on_think(void)
{
target_and_attack();
- keep_moving(4.0);
+ if (have_target || game->now - wake_time < 10.0) {
+ if (!have_target && !move.moving)
+ random_walk();
- if (!move.moving && wake_time + 5 < game->now)
+ keep_moving(4.0);
+ } else
sleep();
}
-void unit_spider_t::on_wake(void)
+void unit_spider_t::on_damage(unit_t *attacker)
{
+ if (!attacker)
+ return;
+
+ hivemind_attack(this, attacker->x);
}
void unit_spider_t::on_death(void)
{
+ if (health < -5) {
+ assets::soldier.gib_sound.play_3d(x);
+ game->deletion_list.insert(this);
+ return;
+ }
+
render_layer = render::LAYER_FLAT;
cmodel.cflags = CF_BACKGROUND;
}
diff --git a/src/game/units.cpp b/src/game/units.cpp
index fb0ce3c..6bce474 100644
--- a/src/game/units.cpp
+++ b/src/game/units.cpp
@@ -209,8 +209,10 @@ void unit_t::damage(int points, unit_t *attacker)
health -= points;
if (health <= 0)
die(attacker);
- else
+ else {
wake();
+ on_damage(attacker);
+ }
}
void unit_t::die(unit_t *killer)
@@ -273,4 +275,36 @@ void unit_t::random_walk(void)
move.random_walk_time = game->time;
}
+void hivemind_attack(unit_t *source, v2f_t target)
+{
+ rectf_t search;
+ std::list<world::entity_t*> ents;
+
+ search[0] = source->x - v2f_t(5, 5);
+ search[1] = source->x + v2f_t(5, 5);
+
+ ents = source->game->world.get_entities(search, CF_BODY_SMALL);
+
+ for (world::entity_t *ent : ents) {
+ unit_t *unit;
+
+ if (ent->type != ET_UNIT)
+ continue;
+
+ unit = dynamic_cast<unit_t*>(ent);
+
+ if (unit->type != unit_t::UNIT_SPIDER)
+ continue;
+
+ if ((unit->x - target).len() > 25.0f)
+ continue;
+
+ if (unit->have_target)
+ continue;
+
+ unit->wake();
+ unit->start_moving(target);
+ }
+}
+
} // namespace game
diff --git a/src/render.cpp b/src/render.cpp
index 98c12a2..185e5e6 100644
--- a/src/render.cpp
+++ b/src/render.cpp
@@ -390,6 +390,9 @@ void state_t::render_text(v2f_t x, float height, std::string str,
offset[0] = -rect.width;
offset[1] = -rect.height;
break;
+
+ default:
+ abort();
}
offset *= scale;