summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/FanwoodText.otfbin0 -> 127932 bytes
-rw-r--r--assets/LiberationMono-Regular.ttfbin313408 -> 0 bytes
-rw-r--r--src/common.hpp43
-rw-r--r--src/game/game.cpp3
-rw-r--r--src/game/game.hpp18
-rw-r--r--src/game/interface.cpp31
-rw-r--r--src/game/units.cpp63
-rw-r--r--src/main.cpp3
-rw-r--r--src/render.cpp20
-rw-r--r--src/text.cpp61
10 files changed, 192 insertions, 50 deletions
diff --git a/assets/FanwoodText.otf b/assets/FanwoodText.otf
new file mode 100644
index 0000000..73d559f
--- /dev/null
+++ b/assets/FanwoodText.otf
Binary files differ
diff --git a/assets/LiberationMono-Regular.ttf b/assets/LiberationMono-Regular.ttf
deleted file mode 100644
index 1a39bc7..0000000
--- a/assets/LiberationMono-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/src/common.hpp b/src/common.hpp
index 2d2af4a..49166d3 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -5,6 +5,7 @@
#include <map>
#include <list>
#include <unordered_set>
+#include <sstream>
#include <stack>
#include <SFML/Graphics.hpp>
#include "math.hpp"
@@ -177,6 +178,10 @@ namespace world {
};
}
+namespace interface {
+ class state_t;
+}
+
namespace game {
bool load_assets(void);
@@ -189,6 +194,7 @@ namespace game {
public:
world::world_t world;
+ interface::state_t *interface;
double now, dt;
void start(void);
@@ -206,6 +212,8 @@ namespace interface {
sf::RenderWindow *window;
game::state_t *game;
+ float em;
+
struct {
sf::Vector2f center;
int target_zoom = 3;
@@ -219,10 +227,19 @@ namespace interface {
rectf_t rect;
} select;
+ typedef struct {
+ double time;
+ std::string text;
+ } log_entry_t;
+
+ std::list<log_entry_t> log;
+
public:
state_t(sf::RenderWindow *window_, game::state_t *game);
void tick(double dt);
void render_to(render::state_t *render);
+
+ void print(std::string str);
};
}
@@ -263,6 +280,7 @@ namespace render {
};
typedef enum {
+ ALIGN_LEFT_TOP,
ALIGN_CENTER_BOTTOM
} text_align_t;
@@ -285,7 +303,7 @@ namespace render {
void render(animated_texture_t *anim, rectf_t bounds, bool mirror = false);
void render(oriented_sprite_t *sprite, rectf_t bounds, float angle);
- void render_text(v2f_t x, float height, const wchar_t *wstr, text_align_t align, sf::Color color);
+ void render_text(v2f_t x, float height, std::string str, text_align_t align, sf::Color color);
void render_rect(rectf_t rect, sf::Color color);
void render_hlrect(rectf_t rect, sf::Color color);
void render_arrow(v2f_t x0, v2f_t x1, sf::Color color);
@@ -297,10 +315,27 @@ namespace render {
extern render::state_t *debug_render;
namespace text {
- extern const wchar_t *unit_no_path;
- extern const wchar_t *unit_blocked;
+ typedef enum {
+ LANG_ENGLISH
+ } language_t;
- void load_strings(std::string lang);
+ extern language_t language;
+
+ typedef enum {
+ SAY_NO_PATH,
+ SAY_BLOCKED,
+ UNIT_ALIEN,
+ UNIT_HUMAN,
+ UNIT_DEATH,
+ UNIT_ATTACK,
+ UNIT_MISS,
+ UNIT_CRITICAL_MISS,
+ UNIT_HIT,
+ UNIT_CRITICAL_HIT,
+ UNIT_DAMAGE
+ } index_t;
+
+ std::string get(index_t index);
}
// Divide and round to minus infinity.
diff --git a/src/game/game.cpp b/src/game/game.cpp
index c2c2c19..f5f57bf 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -70,7 +70,8 @@ void state_t::command(v2f_t x)
if (unit->dead)
continue;
- unit->start_moving(snap);
+ if (!unit->start_moving(snap))
+ unit->say(text::get(text::SAY_NO_PATH));
}
}
diff --git a/src/game/game.hpp b/src/game/game.hpp
index 107bfbf..55e3c45 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -59,6 +59,7 @@ namespace game {
} type_t;
type_t type;
+ std::string name;
unit_t(game::state_t *game_, type_t type_);
@@ -88,12 +89,13 @@ namespace game {
bool dead = false;
double death_time = -INFINITY;
int health = 1, max_health = 1;
- void damage(int points);
+ void damage(int points, unit_t *attacker);
+ void try_attack(unit_t *target);
virtual void die() = 0;
- const wchar_t *say_text;
+ std::string say_text;
double say_time = -INFINITY;
- void say(const wchar_t *wstr);
+ void say(std::string str);
void render_to(render::state_t *render);
@@ -124,4 +126,14 @@ namespace game {
void think(void);
void die(void);
};
+
+ static inline size_t roll(size_t count, size_t sides)
+ {
+ size_t total = 0;
+
+ for (size_t i = 0; i < count; i++)
+ total += rand() % sides + 1;
+
+ return total;
+ }
};
diff --git a/src/game/interface.cpp b/src/game/interface.cpp
index 854b620..a367607 100644
--- a/src/game/interface.cpp
+++ b/src/game/interface.cpp
@@ -131,4 +131,35 @@ void state_t::tick(double dt)
select.rect[1] = wmouse;
}
+void state_t::print(std::string str)
+{
+ log.push_back((log_entry_t){game->now, str});
+ std::cout << str << std::endl;
+}
+
+void state_t::render_to(render::state_t *render)
+{
+ size_t w = window->getSize().x, h = window->getSize().y;
+ v2f_t x;
+
+ window->setView(sf::View(sf::FloatRect(0, 0, w, h)));
+ em = std::min(w, h) * 0.04;
+
+ if (select.selecting)
+ render->render_hlrect(select.rect, sf::Color(200, 100, 50));
+
+ for (auto i = log.begin(); i != log.end(); ) {
+ if (i->time + 3 < game->now)
+ i = log.erase(i);
+ else
+ i++;
+ }
+
+ x = v2f_t(0.0f, 0.0f);
+ for (log_entry_t &entry : log) {
+ render->render_text(x, em, entry.text, render::ALIGN_LEFT_TOP, sf::Color::White);
+ x[1] += em;
+ }
+}
+
} // namespace interface
diff --git a/src/game/units.cpp b/src/game/units.cpp
index 5563124..5efab65 100644
--- a/src/game/units.cpp
+++ b/src/game/units.cpp
@@ -60,11 +60,11 @@ void unit_t::render_to(render::state_t *render)
render->debug_path(&move.path);
}
-void unit_t::say(const wchar_t *wstr)
+void unit_t::say(std::string str)
{
- say_text = wstr;
+ say_text = str;
say_time = game->now;
- std::cout << (void*)this << ": " << wstr << "\n";
+ game->interface->print(name + ": " + str);
}
void unit_t::place(world::world_t *world_, v2f_t x_)
@@ -82,12 +82,13 @@ void unit_t::place(world::world_t *world_, v2f_t x_)
bool unit_t::keep_moving(double speed)
{
float time;
+ bool rv = true;
if (!move.moving)
- return false;
+ return true;
if (move.blocked && game->now < move.next_attempt)
- return false;
+ return true;
time = game->dt * speed;
@@ -126,7 +127,7 @@ bool unit_t::keep_moving(double speed)
move.next_attempt = game->now + 0.2f;
} else {
if ((x - move.dst).len() > 1.5f)
- say(text::unit_blocked);
+ rv = false;
move.moving = false;
}
break;
@@ -135,7 +136,7 @@ bool unit_t::keep_moving(double speed)
unlink();
compute_bounds();
link(world);
- return true;
+ return rv;
}
bool unit_t::start_moving(v2f_t dst)
@@ -149,7 +150,6 @@ bool unit_t::start_moving(v2f_t dst)
move.path.clear();
if (!world->find_path(x, move.dst, &cmodel, this, &move.path)) {
- say(text::unit_no_path);
move.moving = false;
return false;
}
@@ -159,15 +159,14 @@ bool unit_t::start_moving(v2f_t dst)
move.blocked = false;
move.attempts_left = 10;
move.next_attempt = -INFINITY;
-
return true;
}
-void unit_t::damage(int points)
+void unit_t::damage(int points, unit_t *attacker)
{
health -= points;
if (health < 0) {
- printf("%p died\n", this);
+ game->interface->print(name + ": " + text::get(text::UNIT_DEATH) + ".");
dead = true;
death_time = game->now;
cflags = 0;
@@ -175,6 +174,38 @@ void unit_t::damage(int points)
}
}
+void unit_t::try_attack(unit_t *target)
+{
+ std::stringstream ss;
+ int hit_roll, hit_dc = 10 /* FIXME */;
+ int dmg_roll, dmg_bonus, dmg_total;
+
+ ss << name << " " << text::get(text::UNIT_ATTACK) << " " << target->name << ": ";
+
+ hit_roll = roll(1, 20);
+ ss << hit_roll << " vs " << hit_dc;
+
+ if (hit_roll == 1 || hit_roll < hit_dc) {
+ ss << " (" << text::get((hit_roll == 1 ? text::UNIT_CRITICAL_MISS : text::UNIT_MISS)) << ")";
+ game->interface->print(ss.str());
+ return;
+ }
+
+ ss << " (" << text::get((hit_roll == 20 ? text::UNIT_CRITICAL_HIT : text::UNIT_HIT)) << ")";
+ game->interface->print(ss.str());
+
+ dmg_roll = roll(2, 6);
+ dmg_bonus = 2;
+ dmg_total = dmg_roll + dmg_bonus;
+
+ ss.str(std::string());
+ ss << name << " " << text::get(text::UNIT_DAMAGE) << " " << target->name << ": ";
+ ss << dmg_roll << " + " << dmg_bonus << " = " << dmg_total;
+ game->interface->print(ss.str());
+
+ target->damage(dmg_total, this);
+}
+
human_t::human_t(game::state_t *game) : unit_t(game, UNIT_HUMAN)
{
cflags = CF_HUMAN;
@@ -184,6 +215,7 @@ human_t::human_t(game::state_t *game) : unit_t(game, UNIT_HUMAN)
render_size[0] = v2f_t(-0.5f, -1.0f);
render_size[1] = v2f_t(+0.5f, +0.5f);
render_layer = -1;
+ name = text::get(text::UNIT_HUMAN);
}
void human_t::wake(unit_t *by_whom)
@@ -196,7 +228,8 @@ void human_t::sleep(void)
void human_t::think(void)
{
- keep_moving(4.0);
+ if (!keep_moving(4.0))
+ say(text::get(text::SAY_BLOCKED));
}
void human_t::die(void)
@@ -244,6 +277,7 @@ alien_t::alien_t(game::state_t *game) : unit_t(game, UNIT_ALIEN)
size[1] = v2f_t(+0.2f, +0.2f);
render_size[0] = v2f_t(-0.3f, -0.3f);
render_size[1] = v2f_t(+0.3f, +0.3f);
+ name = text::get(text::UNIT_ALIEN);
}
void alien_t::wake(unit_t *by_whom)
@@ -258,7 +292,6 @@ void alien_t::sleep(void)
void alien_t::die(void)
{
-
}
static unit_t *find_target(world::world_t *world, v2f_t x, float r,
@@ -308,9 +341,9 @@ void alien_t::attack(unit_t *target, float range)
trace = world->trace(x, target->x, CF_SOLID);
if (!trace.hit)
- target->damage(rand() % 6 + rand() % 6 + 2);
+ try_attack(target);
- next_attack = game->now + 0.4;
+ next_attack = game->now + 1.0;
}
void alien_t::think(void)
diff --git a/src/main.cpp b/src/main.cpp
index 3bac2dc..e503cad 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -24,12 +24,11 @@ int main()
size_t frame = 0;
double before = NAN;
+ game.interface = &interface;
debug_render = &render;
window.setVerticalSyncEnabled(true);
- text::load_strings("pl");
game::load_assets();
-
game.start();
while (1) {
diff --git a/src/render.cpp b/src/render.cpp
index cddb6c9..2f1890e 100644
--- a/src/render.cpp
+++ b/src/render.cpp
@@ -1,23 +1,15 @@
#include "common.hpp"
-#include <list>
-#include <sstream>
static sf::RectangleShape wot_rect;
static sf::Font font;
-void interface::state_t::render_to(render::state_t *render)
-{
- if (select.selecting)
- render->render_hlrect(select.rect, sf::Color(200, 100, 50));
-}
-
namespace render {
state_t::state_t(sf::RenderWindow *window_)
{
window = window_;
- font.loadFromFile("assets/LiberationMono-Regular.ttf");
+ font.loadFromFile("assets/FanwoodText.otf");
}
// FIXME: rename
@@ -190,18 +182,22 @@ void state_t::render(oriented_sprite_t *sprite, rectf_t bounds, float angle)
render(sprite->textures + index, bounds, mirror);
}
-void state_t::render_text(v2f_t x, float height, const wchar_t *wstr,
+void state_t::render_text(v2f_t x, float height, std::string str,
text_align_t align, sf::Color color)
{
- sf::Text text(wstr, font, 20);
+ sf::Text text(str, font, 40);
sf::FloatRect rect;
float scale;
v2f_t offset;
rect = text.getGlobalBounds();
- scale = height / 20.0f;
+ scale = height / 40.0f;
switch (align) {
+ case ALIGN_LEFT_TOP:
+ offset = v2f_t(0, 0);
+ break;
+
case ALIGN_CENTER_BOTTOM:
offset[0] = -rect.width / 2;
offset[1] = -rect.height;
diff --git a/src/text.cpp b/src/text.cpp
index b689c99..ad6af83 100644
--- a/src/text.cpp
+++ b/src/text.cpp
@@ -2,24 +2,59 @@
namespace text {
-const wchar_t *unit_no_path;
-const wchar_t *unit_blocked;
+language_t language = LANG_ENGLISH;
-static void load_strings_pl(void)
+static const char *human_names[] =
{
- unit_no_path = L"Nie wiem, jak się tam dostać.";
- unit_blocked = L"Coś stoi na mojej drodze.";
-}
+ "Kowalski", "Jackson", "Carter", "O'Neill", "Hammond", "Mitchell"
+};
+
+#define count(A) (sizeof(A) / sizeof((A)[0]))
-void load_strings(std::string lang)
+static std::string get_english(index_t index)
{
- if (lang == "pl") {
- load_strings_pl();
- return;
- }
+ switch (index) {
+ case SAY_BLOCKED:
+ return "Something is in my way.";
+
+ case SAY_NO_PATH:
+ return "I can't get there.";
+
+ case UNIT_ALIEN:
+ return "Alien";
+
+ case UNIT_HUMAN:
+ return human_names[rand() % count(human_names)];
+
+ case UNIT_DEATH:
+ return "Death";
+
+ case UNIT_ATTACK:
+ return "attacks";
- unit_no_path = L"I don't know how to get there.";
- unit_blocked = L"Something's in my way.";
+ case UNIT_MISS:
+ return "miss";
+
+ case UNIT_CRITICAL_MISS:
+ return "critical miss";
+
+ case UNIT_HIT:
+ return "hit";
+
+ case UNIT_CRITICAL_HIT:
+ return "critical hit";
+
+ case UNIT_DAMAGE:
+ return "deals damage to";
+ }
}
+std::string get(index_t index)
+{
+ switch (language) {
+ default:
+ return get_english(index);
+ }
}
+
+} // namespace text