summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/assets.cpp20
-rw-r--r--src/game/game.cpp270
-rw-r--r--src/game/game.hpp14
-rw-r--r--src/game/interface.cpp134
4 files changed, 438 insertions, 0 deletions
diff --git a/src/game/assets.cpp b/src/game/assets.cpp
new file mode 100644
index 0000000..32eff81
--- /dev/null
+++ b/src/game/assets.cpp
@@ -0,0 +1,20 @@
+#include "game.hpp"
+
+namespace assets {
+
+human_assets_t human;
+sf::Texture tile_dirt;
+sf::Texture tile_wall;
+
+void load(void)
+{
+ human.head_idle.load("assets/units/human/head_idle", 1, 1, 1);
+ human.body_idle.load("assets/units/human/body_idle", 2, 2, 2);
+ human.legs_idle.load("assets/units/human/legs_idle", 2, 2);
+ human.legs_walking.load("assets/units/human/legs_walking", 2, 2);
+
+ tile_dirt.loadFromFile("assets/tiles/dirt.png");
+ tile_wall.loadFromFile("assets/tiles/wall.png");
+}
+
+} // namespace assets
diff --git a/src/game/game.cpp b/src/game/game.cpp
new file mode 100644
index 0000000..eb3966d
--- /dev/null
+++ b/src/game/game.cpp
@@ -0,0 +1,270 @@
+#include "game.hpp"
+
+namespace game {
+
+static size_t selection_cookie = 1;
+
+enum {
+ ET_UNIT
+};
+
+class unit_t : public world::entity_t {
+protected:
+ world::world_t *world;
+
+ world::cmodel_t make_cmodel(v2f_t at)
+ {
+ world::cmodel_t cmodel;
+
+ cmodel.bounds[0] = at + size[0];
+ cmodel.bounds[1] = at + size[1];
+ cmodel.cflags = cflags;
+
+ return cmodel;
+ }
+
+ void compute_bounds()
+ {
+ render_bounds[0] = x + render_size[0];
+ render_bounds[1] = x + render_size[1];
+ }
+
+public:
+ v2f_t x;
+ rectf_t size, render_size;
+ world::cflags_t cflags;
+ size_t selected = 0;
+
+ unit_t() : entity_t(ET_UNIT)
+ {
+ }
+
+ struct {
+ bool moving = false;
+ v2f_t dst;
+ float angle = 0.0f;
+
+ std::list<v2f_t> path;
+
+ bool blocked;
+ size_t attempts_left;
+ float next_attempt;
+ } move;
+
+ const wchar_t *say_text;
+ double say_time = -INFINITY;
+
+ void say(const wchar_t *wstr, double now)
+ {
+ say_text = wstr;
+ say_time = now;
+ std::cout << (void*)this << ": " << wstr << "\n";
+ }
+
+ void place(world::world_t *world_, v2f_t x_)
+ {
+ world = world_;
+ x = x_;
+ move.moving = false;
+ cflags = 1;
+
+ unlink();
+ cmodel = make_cmodel(x);
+ compute_bounds();
+ link(world);
+ }
+
+ void keep_moving(double now, double dt)
+ {
+ float time;
+
+ if (!move.moving)
+ return;
+
+ if (move.blocked && now < move.next_attempt)
+ return;
+
+ time = dt * 10;
+
+ while (time > 0.0f) {
+ v2f_t delta, next, x_new;
+ world::cmodel_t cmodel_next;
+
+ if (!move.path.size()) {
+ move.moving = false;
+ break;
+ }
+
+ next = *move.path.begin();
+ delta = next - x;
+ move.angle = delta.angle();
+
+ if (delta.len() >= time) {
+ x_new = x + delta * time / delta.len();
+ time -= delta.len();
+ } else {
+ x_new = next;
+ time -= delta.len();
+ move.path.pop_front();
+ }
+
+ cmodel_next = make_cmodel(x_new);
+ if (!world->test_rect(&cmodel_next, this)) {
+ x = x_new;
+ cmodel = cmodel_next;
+ continue;
+ }
+
+ if (move.attempts_left) {
+ move.blocked = true;
+ move.attempts_left--;
+ move.next_attempt = now + 0.2f;
+ } else {
+ if ((x - move.dst).len() > 1.5f)
+ say(text::unit_blocked, now);
+ move.moving = false;
+ }
+ break;
+ }
+
+
+ unlink();
+ compute_bounds();
+ link(world);
+ }
+
+ bool start_moving(v2f_t dst_, double now)
+ {
+ if (!world) {
+ printf("unit_t::start_moving: entity is not linked\n");
+ return false;
+ }
+
+ move.dst = dst_;
+ move.path.clear();
+
+ if (!world->find_path(x, move.dst, &cmodel, this, &move.path)) {
+ say(text::unit_no_path, now);
+ move.moving = false;
+ return false;
+ }
+
+ move.moving = true;
+
+ move.blocked = false;
+ move.attempts_left = 10;
+ move.next_attempt = -INFINITY;
+
+ return true;
+ }
+};
+
+class human_t : public unit_t {
+public:
+ human_t()
+ {
+ size[0] = v2f_t(-0.4f, -0.4f);
+ size[1] = v2f_t(+0.4f, +0.4f);
+ render_size[0] = v2f_t(-0.5f, -1.0f);
+ render_size[1] = v2f_t(+0.5f, +0.5f);
+ }
+
+ void render_to(render::state_t *render)
+ {
+ bool moving;
+
+ if (selected == selection_cookie)
+ render->render_hlrect(render_bounds, sf::Color::Blue);
+
+ moving = move.moving && !move.blocked;
+
+ render->render((moving ? &assets::human.legs_walking :
+ &assets::human.legs_idle), render_bounds, move.angle);
+ render->render(&assets::human.body_idle, render_bounds, move.angle);
+ render->render(&assets::human.head_idle, render_bounds, move.angle);
+
+ {
+ v2f_t x1;
+ static float t = 0;
+ world::trace_t trace;
+
+ t += 0.02f;
+
+ x1[0] = x[0] + cos(t) * 5;
+ x1[1] = x[1] + sin(t) * 5;
+ trace = world->trace(x, x1, 1);
+ render->render_arrow(x, trace.end, sf::Color::Green);
+ }
+
+ if (move.moving && debug_draw_paths)
+ render->debug_path(&move.path);
+
+ if (say_time + 5.0 > render->now) {
+ v2f_t text_pos;
+ float height;
+
+ text_pos = render_bounds[0] + v2f_t(render_bounds.dim(0) / 2, -render_bounds.dim(1) * 0.1);
+ height = size.dim_min() * 0.20f;
+ render->render_text(text_pos, height, say_text,
+ render::ALIGN_CENTER_BOTTOM,
+ sf::Color::White);
+ }
+ }
+};
+
+void state_t::start(void)
+{
+ human_t *human;
+
+ human = new human_t;
+ human->place(&world, v2f_t(0.5, 0.5));
+ units.insert(human);
+}
+
+void state_t::stop(void)
+{
+ // FIXME
+ /*
+ for (unit_t *unit : units)
+ delete unit;
+ */
+}
+
+void state_t::select(rectf_t x)
+{
+ selection_cookie++;
+ selected_units.clear();
+
+ for (world::entity_t *ent : world.get_render_entities(x)) {
+ unit_t *unit;
+
+ if (ent->type != ET_UNIT)
+ continue;
+
+ unit = (unit_t*)ent;
+ unit->selected = selection_cookie;
+ selected_units.insert(unit);
+ }
+}
+
+void state_t::command(v2f_t x)
+{
+ v2f_t snap;
+
+ snap[0] = std::round(x[0] - 0.5f) + 0.5f;
+ snap[1] = std::round(x[1] - 0.5f) + 0.5f;
+
+ for (unit_t *unit : selected_units)
+ unit->start_moving(snap, now);
+}
+
+void state_t::tick(double now_, double dt_)
+{
+ now = now_;
+ dt = dt_;
+
+ for (unit_t *unit : units)
+ unit->keep_moving(now, dt);
+}
+
+} //namespace game
diff --git a/src/game/game.hpp b/src/game/game.hpp
new file mode 100644
index 0000000..3e10bbb
--- /dev/null
+++ b/src/game/game.hpp
@@ -0,0 +1,14 @@
+#include "../common.hpp"
+
+namespace assets {
+ typedef struct {
+ render::oriented_sprite_4M_t head_idle, body_idle;
+ render::oriented_sprite_4M2_t legs_idle, legs_walking;
+ } human_assets_t;
+
+ extern human_assets_t human;
+ extern sf::Texture tile_dirt;
+ extern sf::Texture tile_wall;
+
+ void load(void);
+};
diff --git a/src/game/interface.cpp b/src/game/interface.cpp
new file mode 100644
index 0000000..c89cdad
--- /dev/null
+++ b/src/game/interface.cpp
@@ -0,0 +1,134 @@
+#include "game.hpp"
+
+namespace interface {
+
+state_t::state_t(sf::RenderWindow *window_, game::state_t *game_)
+{
+ window = window_;
+ game = game_;
+}
+
+static sf::Vector2f compute_pan(sf::RenderWindow *window, sf::Vector2f pan_ref)
+{
+ sf::Vector2i mouse = sf::Mouse::getPosition(*window);
+ sf::Vector2f vmouse = window->mapPixelToCoords(mouse);
+ return -(vmouse - pan_ref);
+}
+
+void state_t::tick(double dt)
+{
+ sf::Vector2u size;
+ sf::Event event;
+ sf::Vector2f view_size;
+ v2f_t wmouse; // Mouse position in world space;
+
+ size = window->getSize();
+
+ camera.zoom = expfade(camera.zoom, camera.target_zoom, 15, dt);
+
+ {
+ float view_scale;
+
+ view_scale = 3 * exp(camera.zoom * 0.3);
+ if (size.x > size.y) {
+ view_size.y = view_scale;
+ view_size.x = view_scale * size.x / size.y;
+ } else {
+ view_size.x = view_scale;
+ view_size.y = view_scale * size.y / size.x;
+ }
+
+ window->setView(sf::View(camera.center, view_size));
+ }
+
+
+ while (window->pollEvent(event)) {
+ // FIXME: refactor this nested switch clusterfuck
+ switch (event.type) {
+ case sf::Event::Closed:
+ window->close();
+ return;
+
+ case sf::Event::MouseButtonPressed:
+ wmouse = window->mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y));
+
+ switch (event.mouseButton.button) {
+ case sf::Mouse::Button::Left:
+ select.selecting = true;
+ select.rect[0] = wmouse;
+ select.rect[1] = wmouse;
+ break;
+
+ case sf::Mouse::Button::Right:
+ game->command(wmouse);
+ break;
+
+ case sf::Mouse::Button::Middle:
+ camera.panning = true;
+ camera.pan_ref = wmouse;
+ break;
+
+ default:;
+ }
+ break;
+
+ case sf::Event::MouseButtonReleased:
+ switch (event.mouseButton.button) {
+ case sf::Mouse::Button::Left:
+ if (select.selecting)
+ game->select(select.rect);
+ select.selecting = false;
+ break;
+
+ case sf::Mouse::Button::Middle:
+ if (camera.panning)
+ camera.center += compute_pan(window, camera.pan_ref);
+ camera.panning = false;
+ break;
+
+ default:;
+ }
+ break;
+
+ case sf::Event::MouseWheelScrolled:
+ camera.target_zoom -= event.mouseWheelScroll.delta;
+ if (camera.target_zoom < 0)
+ camera.target_zoom = 0;
+ if (camera.target_zoom > 11)
+ camera.target_zoom = 11;
+ break;
+
+ case sf::Event::KeyPressed:
+ switch (event.key.code) {
+ case sf::Keyboard::Key::F1:
+ debug_draw_cmodels ^= 1;
+ break;
+
+ case sf::Keyboard::Key::F2:
+ debug_draw_paths ^= 1;
+ break;
+
+ case sf::Keyboard::Key::F3:
+ debug_draw_tile_coords ^= 1;
+ break;
+
+ default:;
+ }
+ default:;
+ }
+ }
+
+ if (camera.panning) {
+ sf::Vector2f delta = compute_pan(window, camera.pan_ref);
+ window->setView(sf::View(camera.center + delta, view_size));
+ } else
+ window->setView(sf::View(camera.center, view_size));
+
+ // Compute this _after_ the setView above.
+ wmouse = window->mapPixelToCoords(sf::Mouse::getPosition(*window));
+
+ if (select.selecting)
+ select.rect[1] = wmouse;
+}
+
+} // namespace interface