diff --git a/data/actors.lua b/data/actors.lua
new file mode 100644
index 0000000..e334af0
--- /dev/null
+++ b/data/actors.lua
@@ -0,0 +1,26 @@
+return {
+ hero = { -- actor id
+ name = "Hero", -- default name of the actor
+ player_controlled = true, -- wether it is controlled by the player
+ health = "6", -- starting & max health
+ strength = "2", -- strength
+ glyph_id = '@', -- sprite id for the glyph, chars gets converted to ints
+ glyph_color = {.1, .4, .9, 1}, -- color tint for the glyph
+ },
+ goblin = {
+ name = "Goblin",
+ player_controlled = false,
+ health = "4",
+ strength = "1",
+ glyph_id = 'g',
+ glyph_color = {.1, .7, .2, 1},
+ },
+ shaman = {
+ name = "Shaman",
+ player_controlled = false,
+ health = "2",
+ strength = "1",
+ glyph_id = 'g',
+ glyph_color = {.1, .7, .5, 1},
+ },
+}
\ No newline at end of file
diff --git a/data/tiles.lua b/data/tiles.lua
new file mode 100644
index 0000000..787fd68
--- /dev/null
+++ b/data/tiles.lua
@@ -0,0 +1,70 @@
+local tiles = {
+ dungeon_wall = {
+ glyph = '#', -- texture id for the tile, usually ascii
+ fg = {.1,.1,.3,1}, -- what color to tint the tile with, {R,G,B,A}
+ bg = {.6,.6,.9,1}, -- what color to paint the rect behind the tile
+ passable = false, -- wether entities can pass through the tile
+ opaque = true, -- wether actors can see through the tile
+ wall = true, -- wether we should render the tile as a wall (only used in generation yet)
+ description = "A stone wall", -- description for the tile
+ tags = { "dungeon", "stone", "wall" } -- tags that help generators find the tile
+ },
+ dungeon_door = {
+ glyph = '+',
+ fg = {.1,.05,.05,1},
+ bg = {.4,.2,.1,1},
+ passable = true,
+ opaque = true,
+ wall = true,
+ description = "A wooden door",
+ tags = { "dungeon", "wood", "door" }
+ },
+ dungeon_floor = {
+ glyph = '.',
+ fg = {.8,.8,1,1},
+ bg = {.1,.1,.2,1},
+ passable = true,
+ opaque = false,
+ wall = false,
+ description = "A stone floor",
+ tags = { "dungeon", "stone", "floor" }
+ },
+ dungeon_entrance = {
+ glyph = '>',
+ fg = {.8,.8,1,1},
+ bg = {.1,.1,.2,1},
+ passable = true,
+ opaque = false,
+ wall = false,
+ description = "Stone stairs going upwards",
+ tags = { "dungeon", "stone", "entrance" }
+ },
+ dungeon_exit = {
+ glyph = '<',
+ fg = {.8,.8,1,1},
+ bg = {.1,.1,.2,1},
+ passable = true,
+ opaque = false,
+ wall = false,
+ description = "Stone stairs going downwards",
+ tags = { "dungeon", "stone", "exit" }
+ },
+}
+
+local dijkstra_debug_amount = 100
+
+for i=1,dijkstra_debug_amount do
+ local debugtile = {
+ glyph = tostring(math.floor(i/10)),
+ bg = {0,0,0,1},
+ fg = {i/dijkstra_debug_amount,1-i/dijkstra_debug_amount,0,1},
+ passable = true,
+ opaque = false,
+ wall = false,
+ description = "Debug: "..tostring(i),
+ tags = { "debug", "floor" }
+ }
+ tiles["dijkstra_debug_floor_"..tostring(i)] = debugtile
+end
+
+return tiles;
\ No newline at end of file
diff --git a/dungeon.vcxproj b/dungeon.vcxproj
index d5d31b3..8523db4 100644
--- a/dungeon.vcxproj
+++ b/dungeon.vcxproj
@@ -157,6 +157,7 @@
+
@@ -208,6 +209,7 @@
+
diff --git a/dungeon.vcxproj.filters b/dungeon.vcxproj.filters
index 93f884f..494d655 100644
--- a/dungeon.vcxproj.filters
+++ b/dungeon.vcxproj.filters
@@ -138,6 +138,9 @@
Source Files
+
+ Source Files
+
@@ -287,5 +290,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/src/Actor.cpp b/src/Actor.cpp
index 66655f3..5a4cdfe 100644
--- a/src/Actor.cpp
+++ b/src/Actor.cpp
@@ -4,21 +4,22 @@
int id_counter = 0;
-Actor::Actor(Tilemap *map, vec2i pos) : Entity(map, pos) {
+Actor::Actor(vec2i pos) : Entity(pos) {
id = id_counter++;
name = "Actor";
range = 1.5f;
+ player_controlled = false;
collision = true;
bt = nullptr;
faction = FACTION_NONE;
sprite_id = 1;
}
-void Actor::update() {
+void Actor::update(Tilemap* map) {
if (!alive) return;
if (!player_controlled && bt != nullptr) {
- bt->tick(this);
+ bt->tick(this, map);
}
if (health < health_max) {
healcounter--;
@@ -48,10 +49,10 @@ void Actor::attack(Actor *act) {
}
}
-void Actor::attack(vec2i dpos) {
+void Actor::attack(vec2i dpos, Tilemap* map) {
if (dpos.dist() <= range) {
vec2i pos = get_position();
- auto acts = get_map()->get_actors(pos.x + dpos.x, pos.y + dpos.y, 0);
+ auto acts = map->get_actors(pos.x + dpos.x, pos.y + dpos.y, 0);
for (Entity* ent : acts) {
auto act = (Actor*)ent;
if (act->is_alive() && act->get_actor_faction() != faction) {
diff --git a/src/Actor.h b/src/Actor.h
index 3fff4f9..b13dacd 100644
--- a/src/Actor.h
+++ b/src/Actor.h
@@ -31,7 +31,7 @@ protected:
float range;
bool alive;
ActorFactions faction;
- Actor(Tilemap *map, vec2i pos);
+ Actor(vec2i pos);
public:
int id;
std::string name;
@@ -39,7 +39,7 @@ public:
//Actor(Tilemap *map, vec2i pos, std::string datakey);
bool is_alive(){ return alive; };
- void attack(vec2i dpos); // basic melee attack
+ void attack(vec2i dpos, Tilemap* map); // basic melee attack
void attack(Actor* act);
void heal(int amount);
void damage(int strength);
@@ -49,7 +49,7 @@ public:
ActorFactions get_actor_faction() { return faction; }
float get_range() { return range; }
void kill() { alive = false; health = 0; collision = false; };
- void update();
+ void update(Tilemap* map);
virtual bool is_type_of(Actors actor){ return actor == ACT_BASE; };
virtual Actors type() { return ACT_BASE; };
EntityTypes entity_type() override { return ENTITY_ACTOR; };
diff --git a/src/AttackEnemyNode.cpp b/src/AttackEnemyNode.cpp
index fc84ba5..892da69 100644
--- a/src/AttackEnemyNode.cpp
+++ b/src/AttackEnemyNode.cpp
@@ -13,7 +13,7 @@ BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = tick->map->get_actors(targetpos.x, targetpos.y, 6);
std::vector visibleEnemies;
for (Actor* actor : actors) {
@@ -22,7 +22,7 @@ BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
if (actor->is_type_of(ACT_HERO) != ishero) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(tick->map, tick->target->get_position(), pos)) {
visibleEnemies.push_back(actor);
}
}
@@ -53,11 +53,11 @@ BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
else {
vec2i pos = tick->target->get_position();
vec2i goal = closestActor->get_position();
- auto path = Pathfinder::aStar(tick->target->get_map(), pos, goal);
+ auto path = Pathfinder::a_star(tick->map, pos, goal);
if (!path.empty()) {
//path.pop_back();
vec2i dpos = path.back() - pos;
- if (tick->target->move(dpos.x, dpos.y)) {
+ if (tick->target->move(dpos.x, dpos.y, tick->map)) {
return BT_RUNNING;
}
}
diff --git a/src/BehaviourTree.cpp b/src/BehaviourTree.cpp
index 8f72d29..fac1850 100644
--- a/src/BehaviourTree.cpp
+++ b/src/BehaviourTree.cpp
@@ -13,10 +13,11 @@ BehaviourTree::~BehaviourTree() {
}
}
-void BehaviourTree::tick(Actor * target) {
+void BehaviourTree::tick(Actor * target, Tilemap* map) {
BTTick tick;
tick.target = target;
tick.tree = this;
+ tick.map = map;
root->execute(&tick);
diff --git a/src/BehaviourTree.h b/src/BehaviourTree.h
index c27eaf6..d2bf6a2 100644
--- a/src/BehaviourTree.h
+++ b/src/BehaviourTree.h
@@ -4,9 +4,11 @@
class BehaviourTree;
class BehaviourTreeNode;
class Actor;
+class Tilemap;
struct BTTick {
Actor* target;
+ Tilemap* map;
BehaviourTree* tree;
std::vector openNodes;
};
@@ -17,6 +19,6 @@ class BehaviourTree {
public:
BehaviourTree(BehaviourTreeNode* rootNode);
~BehaviourTree();
- void tick(Actor* target);
+ void tick(Actor* target, Tilemap* map);
};
diff --git a/src/Entity.cpp b/src/Entity.cpp
index 3afd8de..abff1aa 100644
--- a/src/Entity.cpp
+++ b/src/Entity.cpp
@@ -5,8 +5,7 @@
#include "Entity.h"
#include "Tilemap.h"
-Entity::Entity(Tilemap *map, vec2i pos) {
- this->map = map;
+Entity::Entity(vec2i pos) {
position = pos;
collision = false;
sprite_id = '?';
@@ -16,11 +15,11 @@ vec2i Entity::get_position() {
return position;
}
-bool Entity::move(vec2i dpos) {
- return move(dpos.x, dpos.y);
+bool Entity::move(vec2i dpos, Tilemap* map) {
+ return move(dpos.x, dpos.y, map);
}
-bool Entity::move(int dx, int dy) {
+bool Entity::move(int dx, int dy, Tilemap* map) {
vec2i newpos = position + vec2i(dx, dy);
if (!collision || !map->is_blocked(newpos.x, newpos.y)) {
position = newpos;
@@ -33,10 +32,6 @@ void Entity::set_position(vec2i pos) {
position = pos;
}
-Tilemap *Entity::get_map() {
- return map;
-}
-
bool Entity::has_collision() {
return collision;
}
diff --git a/src/Entity.h b/src/Entity.h
index 165edf7..88d8cd6 100644
--- a/src/Entity.h
+++ b/src/Entity.h
@@ -1,10 +1,4 @@
-//
-// Created by Adrian on 2017-09-25.
-//
-
-#ifndef DUNGEON_ENTITY_H
-#define DUNGEON_ENTITY_H
-
+#pragma once
#include "vec2i.h"
#include "Color.h"
@@ -19,24 +13,19 @@ enum EntityTypes {
class Entity {
vec2i position;
- Tilemap* map;
protected:
unsigned int sprite_id;
Color sprite_color;
bool collision;
public:
- Entity(Tilemap* map, vec2i pos);
+ Entity(vec2i pos);
- Tilemap* get_map();
vec2i get_position();
bool has_collision();
- bool move(int dx, int dy); // returns false if movement failed
- bool move(vec2i dpos);
+ bool move(int dx, int dy, Tilemap* map); // returns false if movement failed
+ bool move(vec2i dpos, Tilemap* map);
void set_position(vec2i pos);
unsigned int get_sprite_id() { return sprite_id; };
Color get_sprite_color() { return sprite_color; };
virtual EntityTypes entity_type() { return ENTITY_BASE; };
};
-
-
-#endif //DUNGEON_ENTITY_H
diff --git a/src/FieldOfView.cpp b/src/FieldOfView.cpp
index 25867d3..0e66974 100644
--- a/src/FieldOfView.cpp
+++ b/src/FieldOfView.cpp
@@ -1,7 +1,3 @@
-//
-// Created by Adrian on 2017-09-21.
-//
-
#include
#include "FieldOfView.h"
#include "Tilemap.h"
@@ -12,13 +8,12 @@ FieldOfView::FieldOfView() {
FieldOfView::FieldOfView(Tilemap *map) {
this->map = map;
- seen = Tilemap(map->get_width(), map->get_height());
- counter = 0;
+ seen = std::vector(map->get_width()*map->get_height(),0);
}
void FieldOfView::calc(vec2i pos, float range) {
counter++;
- seen.set_tile(pos.x, pos.y, counter);
+ seen[map->get_index(pos.x, pos.y)] = counter;
// Once for each octant
if (map != nullptr) {
cast_light(1, 1.0f, 0.0f, 0, -1, -1, 0, pos.x, pos.y, range);
@@ -36,11 +31,11 @@ void FieldOfView::calc(vec2i pos, float range) {
}
bool FieldOfView::can_see(vec2i pos) {
- return seen.get_tile(pos.x, pos.y) >= counter;
+ return seen[map->get_index(pos.x, pos.y)] >= counter;
}
bool FieldOfView::has_seen(vec2i pos) {
- return seen.get_tile(pos.x, pos.y) > 0;
+ return seen[map->get_index(pos.x, pos.y)] > seen_cutoff;
}
void FieldOfView::cast_light(int row, float start, float end, int xx, int xy, int yx, int yy, int startX, int startY,
@@ -66,11 +61,11 @@ void FieldOfView::cast_light(int row, float start, float end, int xx, int xy, in
}
if (sqrt(deltaX*deltaX + deltaY*deltaY) <= radius) {
- seen.set_tile(currentX, currentY, counter);
+ seen[map->get_index(currentX, currentY)] = counter;
}
if (blocked) {
- if (map->get_tile(currentX, currentY) == '#') { // TODO: Stop hardcoding tiles
+ if (map->get_tile(currentX, currentY).opaque) { // TODO: Stop hardcoding tiles
newStart = rightSlope;
continue;
}
@@ -80,7 +75,7 @@ void FieldOfView::cast_light(int row, float start, float end, int xx, int xy, in
}
}
else {
- if (map->get_tile(currentX, currentY) == '#' && distance < radius) { // TODO: Get rid of hardcoded tiles
+ if (map->get_tile(currentX, currentY).opaque && distance < radius) { // TODO: Get rid of hardcoded tiles
blocked = true;
cast_light(distance + 1, start, leftSlope, xx, xy, yx, yy, startX, startY, radius);
newStart = rightSlope;
@@ -103,7 +98,7 @@ bool line_of_sight(Tilemap *map, vec2i start, vec2i end) {
// error may go below zero
int error(delta.y - (delta.x >> 1));
- while (start.x != end.x && map->get_tile(start.x, start.y) != '#') // TODO: Hardcoded tiles
+ while (!start.x != end.x && map->get_tile(start.x, start.y).opaque) // TODO: Hardcoded tiles
{
// reduce error, while taking into account the corner case of error == 0
if ((error > 0) || (!error && (ix > 0)))
@@ -122,7 +117,7 @@ bool line_of_sight(Tilemap *map, vec2i start, vec2i end) {
// error may go below zero
int error(delta.x - (delta.y >> 1));
- while (start.y != end.y && map->get_tile(start.x, start.y) != '#') // TODO: Stop hardcoding tiles
+ while (start.y != end.y && !map->get_tile(start.x, start.y).opaque) // TODO: Stop hardcoding tiles
{
// reduce error, while taking into account the corner case of error == 0
if ((error > 0) || (!error && (iy > 0)))
diff --git a/src/FieldOfView.h b/src/FieldOfView.h
index eb8b062..c212831 100644
--- a/src/FieldOfView.h
+++ b/src/FieldOfView.h
@@ -1,20 +1,17 @@
-//
-// Created by Adrian on 2017-09-21.
-//
-
-#ifndef DUNGEON_FIELDOFVIEW_H
-#define DUNGEON_FIELDOFVIEW_H
+#pragma once
#include "vec2i.h"
#include "Tilemap.h"
+#include
class FieldOfView {
Tilemap* map;
- unsigned int counter;
- Tilemap seen;
+ unsigned int counter = 0;
+ std::vector seen;
void cast_light(int row, float start, float end, int xx, int xy, int yx, int yy, int startX, int startY, float radius);
public:
+ unsigned int seen_cutoff = 0;
FieldOfView();
FieldOfView(Tilemap* map);
void calc(vec2i pos, float range);
@@ -22,5 +19,3 @@ public:
bool has_seen(vec2i pos);
};
bool line_of_sight(Tilemap* map, vec2i start, vec2i end);
-
-#endif //DUNGEON_FIELDOFVIEW_H
diff --git a/src/FleeNode.cpp b/src/FleeNode.cpp
index 79e1369..19100f2 100644
--- a/src/FleeNode.cpp
+++ b/src/FleeNode.cpp
@@ -14,15 +14,15 @@ FleeNode::~FleeNode() = default;
BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
Pathfinder::DijkstraMap dijkstra;
- Tilemap * map = tick->target->get_map();
+ Tilemap * map = tick->map;
std::vector enemyPos;
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = map->get_actors(targetpos.x, targetpos.y, 6);
for (Actor* actor : actors) {
if (actor->is_type_of(ACT_HERO) != ishero) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(map, tick->target->get_position(), pos)) {
enemyPos.push_back(pos);
}
}
@@ -30,7 +30,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
if (enemyPos.empty()) {
return BT_FAILED;
}
- Pathfinder::calcDijkstraMap(map, &enemyPos, &dijkstra, 16);
+ Pathfinder::calc_dijkstra_map(*map, enemyPos, dijkstra, 16);
dijkstra.add(-16);
dijkstra.mult(-1);
@@ -38,13 +38,13 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
std::vector safety;
for (int x = 0; x < dijkstra.width; x++) {
for (int y = 0; y < dijkstra.height; y++) {
- if (dijkstra.getValue(x,y) <= 0 && dijkstra.getValue(x, y) >= -10) {
+ if (dijkstra.get_value(x,y) <= 0 && dijkstra.get_value(x, y) >= -10) {
safety.emplace_back(x, y);
}
}
}
- Pathfinder::calcDijkstraMap(map, &safety, &dijkstra);
+ Pathfinder::calc_dijkstra_map(*map, safety, dijkstra);
vec2i pos = tick->target->get_position();
@@ -52,7 +52,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
std::vector options;
float lowestval = 999999;
for (vec2i npos : neigh) {
- float val = dijkstra.getValue(npos.x, npos.y);
+ float val = dijkstra.get_value(npos.x, npos.y);
if (val < lowestval) {
lowestval = val;
options.clear();
@@ -70,7 +70,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
int i = rand() % options.size();
vec2i next = options[i];
vec2i dp = next - pos;
- if (tick->target->move(dp.x, dp.y)) {
+ if (tick->target->move(dp.x, dp.y, map)) {
//printf("FLEEING val:%f\t(%i,%i)\n", lowestval, next.x, next.y);
return BT_RUNNING;
}
diff --git a/src/Goblin.cpp b/src/Goblin.cpp
index 01f6ce7..8868e5b 100644
--- a/src/Goblin.cpp
+++ b/src/Goblin.cpp
@@ -7,7 +7,7 @@
BehaviourTree* gobtree = nullptr;
-Goblin::Goblin(Tilemap* map, vec2i pos) : Actor(map, pos) {
+Goblin::Goblin(vec2i pos) : Actor(pos) {
name = "Goblin";
alive = true;
health = 4;
diff --git a/src/Goblin.h b/src/Goblin.h
index a4a091b..60a7f96 100644
--- a/src/Goblin.h
+++ b/src/Goblin.h
@@ -4,7 +4,7 @@
class Goblin :
public Actor {
public:
- Goblin(Tilemap* map, vec2i pos);
+ Goblin(vec2i pos);
~Goblin();
bool is_type_of(Actors actor) { return actor == ACT_GOBLIN || Actor::is_type_of(actor); };
Actors type() { return ACT_GOBLIN; }
diff --git a/src/HealFriendNode.cpp b/src/HealFriendNode.cpp
index 473dcbd..6aff4b2 100644
--- a/src/HealFriendNode.cpp
+++ b/src/HealFriendNode.cpp
@@ -13,14 +13,14 @@ BehaviourTreeStatus HealFriendNode::tick(BTTick * tick) {
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = tick->map->get_actors(targetpos.x, targetpos.y, 6);
std::vector friends;
for (Actor* actor : actors) {
if (actor == tick->target) continue;
if (actor->is_type_of(ACT_HERO) == ishero && actor->get_health() < actor->get_health_max()) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(tick->map, tick->target->get_position(), pos)) {
friends.push_back(actor);
}
}
diff --git a/src/Hero.cpp b/src/Hero.cpp
index 9d7dd48..a3facc1 100644
--- a/src/Hero.cpp
+++ b/src/Hero.cpp
@@ -8,7 +8,7 @@
#include "RestNode.h"
#include "FleeNode.h"
-Hero::Hero(Tilemap* map, vec2i pos) : Actor(map, pos) {
+Hero::Hero(vec2i pos) : Actor(pos) {
name = "Hero";
alive = true;
player_controlled = true;
diff --git a/src/Hero.h b/src/Hero.h
index f8357ef..c045cc2 100644
--- a/src/Hero.h
+++ b/src/Hero.h
@@ -3,7 +3,7 @@
class Hero : public Actor {
public:
- Hero(Tilemap* map, vec2i pos);
+ Hero(vec2i pos);
~Hero();
bool is_type_of(Actors actor) { return actor == ACT_HERO || Actor::is_type_of(actor); };
Actors type() { return ACT_HERO; }
diff --git a/src/IfNotSeeEnemyNode.cpp b/src/IfNotSeeEnemyNode.cpp
index 31a0536..4a3cd56 100644
--- a/src/IfNotSeeEnemyNode.cpp
+++ b/src/IfNotSeeEnemyNode.cpp
@@ -16,13 +16,13 @@ BehaviourTreeStatus IfNotSeeEnemyNode::tick(BTTick * tick) {
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = tick->map->get_actors(targetpos.x, targetpos.y, 6);
for (Actor* actor : actors) {
if (actor == tick->target) continue;
if (actor->is_type_of(ACT_HERO) != ishero) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(tick->map, tick->target->get_position(), pos)) {
return BT_FAILED;
}
}
diff --git a/src/IfSeeEnemyNode.cpp b/src/IfSeeEnemyNode.cpp
index 8cdae36..cfd2180 100644
--- a/src/IfSeeEnemyNode.cpp
+++ b/src/IfSeeEnemyNode.cpp
@@ -16,13 +16,13 @@ BehaviourTreeStatus IfSeeEnemyNode::tick(BTTick * tick) {
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = tick->map->get_actors(targetpos.x, targetpos.y, 6);
for (Actor* actor : actors) {
if (actor == tick->target) continue;
if (actor->is_type_of(ACT_HERO) != ishero) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(tick->map, tick->target->get_position(), pos)) {
return children[0]->execute(tick);
}
}
diff --git a/src/IfSeeFriendNode.cpp b/src/IfSeeFriendNode.cpp
index 02500ac..dc9fae0 100644
--- a/src/IfSeeFriendNode.cpp
+++ b/src/IfSeeFriendNode.cpp
@@ -17,13 +17,13 @@ BehaviourTreeStatus IfSeeFriendNode::tick(BTTick * tick) {
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = tick->map->get_actors(targetpos.x, targetpos.y, 6);
for (Actor* actor : actors) {
if (actor == tick->target) continue;
if (actor->is_type_of(ACT_HERO) == ishero) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(tick->map, tick->target->get_position(), pos)) {
return children[0]->execute(tick);
}
}
diff --git a/src/Mapgen.cpp b/src/Mapgen.cpp
index db49f9f..e9efe1b 100644
--- a/src/Mapgen.cpp
+++ b/src/Mapgen.cpp
@@ -5,35 +5,35 @@
#include
#include "Rng.h"
#include
+#include
+#include "Goblin.h"
+#include "Hero.h"
+#include "Pathfinder.h"
struct Room {
vec2i pos;
vec2i size;
};
-const char walltile = '#';
-const char floortile = '.';
-const char doortile = '+';
-const char testtile = ' ';
-
bool aabb(Room &a, Room &b) {
return a.pos.x <= b.pos.x + b.size.x && a.pos.x + a.size.x >= b.pos.x &&
a.pos.y <= b.pos.y + b.size.y && a.pos.y + a.size.y >= b.pos.y;
}
-void maze_fill(Tilemap& map, int x, int y, Rng &rng) {
- if (map.get_tile(x, y) != walltile) return;
+void maze_fill(Tilemap& map, int x, int y, std::string wall, std::string floor, Rng &rng) {
+ if (!map.get_tile(x, y).wall) return;
const std::vector dirs { vec2i(0,1), vec2i(1,0), vec2i(0,-1), vec2i(-1,0) };
- std::vector stack { vec2i(x,y) };
+ std::stack stack;
+ stack.emplace(vec2i(x, y));
while (!stack.empty()) {
- vec2i pos = stack.back();
- map.set_tile(pos.x, pos.y, floortile);
+ vec2i pos = stack.top();
+ map.set_tile(pos.x, pos.y, floor);
std::vector options;
for (vec2i dir : dirs) {
vec2i next = { pos.x + dir.x, pos.y + dir.y };
- if (map.get_tile(next.x, next.y) != walltile) continue;
+ if (!map.get_tile(next.x, next.y).wall) continue;
if (next.x == 0 || next.x == map.get_width() - 1 || next.y == 0 || next.y == map.get_height() - 1) continue;
int up = dir.y <= 0 ? 1 : 0;
@@ -44,7 +44,7 @@ void maze_fill(Tilemap& map, int x, int y, Rng &rng) {
std::vector neigh = map.get_neighbours(next.x, next.y, up, down, left, right);
bool enclosed = true;
for (vec2i n : neigh) {
- if (map.get_tile(n.x, n.y) != walltile) {
+ if (!map.get_tile(n.x, n.y).wall) {
enclosed = false;
break;
}
@@ -55,31 +55,42 @@ void maze_fill(Tilemap& map, int x, int y, Rng &rng) {
}
}
if (!options.empty()) {
- stack.emplace_back(options.at(rng.get_int(options.size() - 1)));
+ stack.emplace(options.at(rng.get_int(options.size() - 1)));
}
else {
- stack.pop_back();
+ stack.pop();
}
}
}
-Tilemap generate_dungeon(int width, int height) {
- return generate_dungeon(Rng::get_random_seed(), width, height);
+Tilemap generate_dungeon(int width, int height, TileSet tileset) {
+ return generate_dungeon(Rng::get_random_seed(), width, height, tileset);
}
-Tilemap generate_dungeon(unsigned int seed, int width, int height) {
- Tilemap map = Tilemap(width, height);
+Tilemap generate_dungeon(unsigned int seed, int width, int height, TileSet tileset) {
+ Rng rng = Rng(seed);
+
+ Tilemap map = Tilemap(tileset, width, height);
+
+ std::vector wall_tiles = tileset.find_tiles(false, true, true, { "dungeon", "wall" }, {});
+ std::vector floor_tiles = tileset.find_tiles(true, false, false, { "dungeon", "floor" }, {});
+ std::vector door_tiles = tileset.find_tiles(true, true, true, { "dungeon", "door" }, {});
+ std::string entrance_tile = tileset.find_tiles(true, false, false, { "dungeon", "entrance" }).at(0);
+ std::string exit_tile = tileset.find_tiles(true, false, false, { "dungeon", "exit" }).at(0);
+#ifdef _DEBUG
+ assert(wall_tiles.size() > 0);
+ assert(floor_tiles.size() > 0);
+ assert(door_tiles.size() > 0);
+#endif
// Set the whole map to walls
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
- map.set_tile(x, y, walltile);
+ map.set_tile(x, y, wall_tiles[rng.get_int(0, wall_tiles.size()-1)]);
}
}
- Rng rng = Rng(seed);
-
// Room placement
std::vector rooms;
for (int i = 0; i < sqrt(width*height); i++) {
@@ -104,7 +115,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
for (Room r : rooms) {
for (int x = r.pos.x+1; x < r.pos.x + r.size.x-1; x++) {
for (int y = r.pos.y+1; y < r.pos.y + r.size.y-1; y++) {
- map.set_tile(x, y, floortile);
+ map.set_tile(x, y, floor_tiles[rng.get_int(0, floor_tiles.size() - 1)]);
}
}
}
@@ -116,11 +127,13 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
std::vector neigh = map.get_neighbours(x, y, 1);
int count = 0;
for (vec2i n : neigh) {
- if (map.get_tile(n.x, n.y) == walltile) count++;
+ if (map.get_tile(n.x, n.y).wall) {
+ count++;
+ }
}
// If this tile is a wall and is completely surrounded by other walls, start generating a maze here.
if (count >= 8) {
- maze_fill(map, x, y, rng);
+ maze_fill(map, x, y, wall_tiles[rng.get_int(0, wall_tiles.size() - 1)], floor_tiles[rng.get_int(0, floor_tiles.size() - 1)], rng);
maze_start_points.emplace_back(vec2i(x, y));
}
}
@@ -150,7 +163,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
}
// If there is a floor tile on the other side of this room wall
- if (map.get_tile(r.pos.x+x+dx, r.pos.y+y+dy) == floortile) {
+ if (map.get_tile(r.pos.x+x+dx, r.pos.y+y+dy).passable) {
potential_doors.emplace_back(r.pos.x + x, r.pos.y + y);
}
}
@@ -163,7 +176,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
/*/
if (potential_doors.empty()) continue;
- // Pick up to 3 spots and place doorss
+ // Pick up to 3 spots and place doors
int doors_amount = potential_doors.size() < 3 ? potential_doors.size() : 4;
doors_amount = rng.get_int(2, doors_amount);
@@ -173,7 +186,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
int r = rng.get_int(potential_doors.size()-1);
vec2i pos = potential_doors.at(r);
- map.set_tile(pos.x, pos.y, doortile);
+ map.set_tile(pos.x, pos.y, door_tiles[rng.get_int(0, door_tiles.size() - 1)]);
potential_doors.erase(r + potential_doors.begin());
for (int j = potential_doors.size() - 1; j >= 0; j--) {
if ((pos - potential_doors[j]).dist() <= 4) {
@@ -191,7 +204,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
std::vector neigh{vec2i(x + 1, y), vec2i(x, y + 1), vec2i(x - 1, y), vec2i(x, y - 1) };
int count = 0;
for (vec2i pos : neigh) {
- if (map.get_tile(pos.x, pos.y) == walltile) {
+ if (!map.get_tile(pos.x, pos.y).passable) {
count++;
}
}
@@ -209,7 +222,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
int count = 0;
vec2i next;
for (vec2i n : neigh) {
- if (map.get_tile(n.x, n.y) == walltile) {
+ if (!map.get_tile(n.x, n.y).passable) {
continue;
}
else {
@@ -218,42 +231,90 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
}
}
if (count == 1) {
- map.set_tile(pos.x, pos.y, walltile);
+ map.set_tile(pos.x, pos.y, wall_tiles[rng.get_int(0, wall_tiles.size() - 1)]);
new_dead_ends.emplace_back(next);
}
else if (count == 0) {
- map.set_tile(pos.x, pos.y, walltile);
+ map.set_tile(pos.x, pos.y, wall_tiles[rng.get_int(0, wall_tiles.size() - 1)]);
}
}
dead_ends = new_dead_ends;
}
- /* flood-fill the map to see that you can actually reach everywhere
- bool started = false;
- for (int x = 0; x < map.get_width(); x++) {
- for (int y = 0; y < map.get_height(); y++) {
- if (map.get_tile(x,y) == floortile) {
- std::vector stack{vec2i(x,y)};
- map.set_tile(x, y, testtile);
- while (!stack.empty()) {
- vec2i pos = stack.back();
- stack.pop_back();
+ // Place the entrance in a random room
+ Room& startroom = rooms[rng.get_int(0, rooms.size() - 1)];
+ vec2i startpos = startroom.pos;
+ startpos.x += rng.get_int(1, startroom.size.x - 2);
+ startpos.y += rng.get_int(1, startroom.size.y - 2);
+ map.set_tile(startpos.x, startpos.y, entrance_tile);
- auto neigh = map.get_neighbours(pos.x, pos.y);
- for (vec2i n : neigh) {
- char tile = map.get_tile(n.x, n.y);
- if (tile == floortile || tile == doortile) {
- map.set_tile(pos.x, pos.y, testtile);
- stack.emplace_back(n);
- }
- }
+ // Find the room furthest away from the entrance and make it the exit
+ Pathfinder::DijkstraMap dijk;
+ const float maxv = width+height;
+ Pathfinder::calc_dijkstra_map(map, std::vector{ startpos }, dijk, maxv);
+
+ float exitroomval = 0;
+ Room* exitroom = &startroom;
+ for (Room& room : rooms) {
+ float room_min_v = maxv;
+ for (int x = 0; x < room.size.x; x++) {
+ for (int y = 0; y < room.size.y; y++) {
+ float val = dijk.get_value(room.pos.x + x, room.pos.y + y);
+ if (val < room_min_v) {
+ room_min_v = val;
}
-
- started = true;
- break;
}
}
- if (started) break;
+ if (room_min_v > exitroomval) {
+ exitroom = &room;
+ exitroomval = room_min_v;
+ }
+ }
+
+ vec2i exitpos = exitroom->pos;
+ exitpos.x += rng.get_int(1, exitroom->size.x - 2);
+ exitpos.y += rng.get_int(1, exitroom->size.y - 2);
+ map.set_tile(exitpos.x, exitpos.y, exit_tile);
+ float endval = dijk.get_value(exitpos.x, exitpos.y);
+
+ auto path = Pathfinder::a_star(&map, startpos, exitpos);
+
+ Pathfinder::calc_dijkstra_map(map, path, dijk, maxv);
+
+ map.add_actor(new Hero(startpos));
+
+ for (Room r : rooms) {
+ float room_value = 1;
+ for (int x = 0; x < r.size.x; x++) {
+ for (int y = 0; y < r.size.y; y++) {
+ float val = dijk.get_value(r.pos.x + x, r.pos.y + y)/maxv;
+ if (val < room_value) {
+ val = room_value;
+ }
+ }
+ }
+ if (rng.get_float() < 0.1f + 0.3f*room_value) {
+ int amount = 1 + 3 * (rng.get_float() + room_value);
+ for (int i = 0; i < amount; i++) {
+ vec2i pos = r.pos;
+ pos.x += rng.get_int(1, r.size.x - 2);
+ pos.y += rng.get_int(1, r.size.y - 2);
+ map.add_actor(new Goblin(pos));
+ }
+ }
+ }
+
+ /* dijkstra debug
+ for (int x = 0; x < map.get_width(); x++) {
+ for (int y = 0; y < map.get_height(); y++) {
+ float dv = dijk.get_value(x, y);
+ float a = dv / maxv;
+ int val = (int)(a*99)+1;
+ const Tile& tile = map.get_tile(x, y);
+ if (tile.passable && tile.has_tag("floor")) {
+ map.set_tile(x, y, "dijkstra_debug_floor_" + std::to_string(val));
+ }
+ }
}
//*/
return map;
diff --git a/src/Mapgen.h b/src/Mapgen.h
index 17e777b..bb456cc 100644
--- a/src/Mapgen.h
+++ b/src/Mapgen.h
@@ -1,5 +1,5 @@
#pragma once
#include "Tilemap.h"
-Tilemap generate_dungeon(int width, int height);
-Tilemap generate_dungeon(unsigned int seed, int width, int height);
+Tilemap generate_dungeon(int width, int height, TileSet tileset);
+Tilemap generate_dungeon(unsigned int seed, int width, int height, TileSet tileset);
diff --git a/src/Pathfinder.cpp b/src/Pathfinder.cpp
index df8d78c..fde37ac 100644
--- a/src/Pathfinder.cpp
+++ b/src/Pathfinder.cpp
@@ -1,6 +1,7 @@
#include
#include "Pathfinder.h"
#include "Tilemap.h"
+#include
namespace Pathfinder
{
@@ -11,7 +12,7 @@ namespace Pathfinder
return sqrtf(dx*dx + dy*dy);
}
- std::vector aStar(Tilemap * map, vec2i start, vec2i goal)
+ std::vector a_star(Tilemap * map, vec2i start, vec2i goal)
{
std::vector open;
std::vector closed;
@@ -36,7 +37,7 @@ namespace Pathfinder
auto neighbours = map->get_neighbours(current->pos.x, current->pos.y);
for (auto pos : neighbours) {
- if (map->get_tile(pos.x, pos.y) == '#') {
+ if (!map->get_tile(pos.x, pos.y).passable) {
continue;
}
AStarNode* neighbour = new AStarNode();
@@ -140,53 +141,53 @@ namespace Pathfinder
return path;
}
- void calcDijkstraMap(Tilemap * map, std::vector* goals, DijkstraMap * out, float maxValue) {
- if (out->tilemap != nullptr) {
- delete out->tilemap;
+ void calc_dijkstra_map(Tilemap& map, std::vector& goals, DijkstraMap& out, float maxValue) {
+ if (out.tilemap != nullptr) {
+ delete out.tilemap;
}
- out->tilemap = new float[map->get_width() * map->get_height()];
- for (int i = 0; i < map->get_width() * map->get_height(); i++) {
- out->tilemap[i] = maxValue;
+ out.tilemap = new float[map.get_width() * map.get_height()];
+ for (int i = 0; i < map.get_width() * map.get_height(); i++) {
+ out.tilemap[i] = maxValue;
}
- out->height = map->get_height();
- out->width = map->get_width();
- for (vec2i pos : *goals) {
- out->setValue(pos.x, pos.y, 0);
+ out.height = map.get_height();
+ out.width = map.get_width();
+ for (vec2i& pos : goals) {
+ out.setValue(pos.x, pos.y, 0);
}
- std::vector queue;
+ std::queue queue;
- for (vec2i pos : *goals) {
- auto neigh = map->get_neighbours(pos.x, pos.y);
- for (vec2i npos : neigh) {
- int val = out->getValue(npos.x, npos.y);
- if (map->get_tile(npos.x, npos.y) != '#' && val > 1) {
+ for (vec2i& pos : goals) {
+ auto neigh = map.get_neighbours(pos.x, pos.y);
+ for (vec2i& npos : neigh) {
+ int val = out.get_value(npos.x, npos.y);
+ if (map.get_tile(npos.x, npos.y).passable && val > 1.4f) {
if (npos.x != 0 && npos.y != 0) {
- out->setValue(npos.x, npos.y, 1.4f);
+ out.setValue(npos.x, npos.y, 1.4f);
}
else {
- out->setValue(npos.x, npos.y, 1);
+ out.setValue(npos.x, npos.y, 1);
}
- queue.push_back(npos);
+ queue.push(npos);
}
}
}
while (queue.size() > 0) {
- vec2i current = queue.back();
- queue.pop_back();
+ vec2i current = queue.front();
+ float val = out.get_value(current.x, current.y);
+ queue.pop();
- std::vector neigh = map->get_neighbours(current.x, current.y);
- for (int i = 0; i < neigh.size(); i++) {
- float val = out->getValue(current.x, current.y) + 1;
- vec2i npos = neigh[i];
+ std::vector neigh = map.get_neighbours(current.x, current.y);
+ for (vec2i& npos : neigh) {
vec2i dp = npos - current;
+ float nval = val + 1;
if (dp.x != 0 && dp.y != 0) {
- val += .4f;
+ nval += .4f;
}
- if (map->get_tile(npos.x, npos.y) != '#' && out->getValue(npos.x, npos.y) > val) { // TODO: Remove hardcoded tile
- out->setValue(npos.x, npos.y, val);
- queue.push_back(neigh[i]);
+ if (map.get_tile(npos.x, npos.y).passable && out.get_value(npos.x, npos.y) > nval) {
+ out.setValue(npos.x, npos.y, nval);
+ queue.push(npos);
}
}
}
diff --git a/src/Pathfinder.h b/src/Pathfinder.h
index 8412ca0..3001824 100644
--- a/src/Pathfinder.h
+++ b/src/Pathfinder.h
@@ -18,7 +18,7 @@ namespace Pathfinder
float* tilemap = nullptr;
int width, height;
- float getValue(int x, int y) {
+ float get_value(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height) {
return tilemap[y*width + x];
}
@@ -66,6 +66,6 @@ namespace Pathfinder
};
float distance(vec2i a, vec2i b);
- std::vector aStar(Tilemap* map, vec2i start, vec2i goal);
- void calcDijkstraMap(Tilemap* map, std::vector* goals, DijkstraMap* out, float maxValue = 32);
-}
\ No newline at end of file
+ std::vector a_star(Tilemap* map, vec2i start, vec2i goal);
+ void calc_dijkstra_map(Tilemap& map, std::vector& goals, DijkstraMap& out, float maxValue = 32);
+}
diff --git a/src/PlayState.cpp b/src/PlayState.cpp
index 283d0a6..4d600aa 100644
--- a/src/PlayState.cpp
+++ b/src/PlayState.cpp
@@ -15,10 +15,11 @@
#include "Goblin.h"
#include "Shaman.h"
#include "Rng.h"
-
-const int mapwidth = 32;
+#include "TileSet.h"
+#include
InputAction player_action;
+TileSet tileset;
void PlayState::load() {
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating ascii tileset...\n");
@@ -27,6 +28,14 @@ void PlayState::load() {
app->input->bind_key(SDLK_ESCAPE, ACTION_ESCAPE_MENU);
+ kaguya::State lua;
+ lua["tiles"] = kaguya::NewTable();
+ lua(R"LUA(
+actors = dofile('data/actors.lua')
+tiles = dofile('data/tiles.lua')
+)LUA");
+ tileset.load_from_table(lua.globalTable().getField("tiles"));
+
// Movement: keypad
app->input->bind_key(SDLK_KP_8, ACTION_MOVE_NORTH);
app->input->bind_key(SDLK_KP_7, ACTION_MOVE_NORTHWEST);
@@ -54,6 +63,9 @@ void PlayState::load() {
app->input->bind_key(SDLK_n, ACTION_MOVE_SOUTHWEST);
app->input->bind_key(SDLK_m, ACTION_MOVE_SOUTHEAST);
+ // General
+ app->input->bind_key(SDLK_PERIOD, ACTION_WAIT);
+
// debug
app->input->bind_key(SDLK_F1, ACTION_TOGGLE_DEBUG);
app->input->bind_key(SDLK_r, ACTION_RESET);
@@ -65,37 +77,36 @@ void PlayState::load() {
void PlayState::new_game() {
player_action = ACTION_NONE;
- if (player_actor != nullptr) {
- delete player_actor;
- player_actor = nullptr;
- }
+ tilemap.delete_actors();
+ player_actor = nullptr;
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n");
Rng rng;
- tilemap = generate_dungeon(128, 128);
+ tilemap = generate_dungeon(48, 48, tileset);
vec2i heropos;
+ Tile t;
do {
heropos.x = rng.get_int(1, tilemap.get_width() - 1);
heropos.y = rng.get_int(1, tilemap.get_width() - 1);
- } while (tilemap.get_tile(heropos.x, heropos.y) == '#');
- player_actor = new Hero(&tilemap, vec2i(heropos.x,heropos.y));
- tilemap.add_actor(player_actor);
+ t = tilemap.get_tile(heropos.x, heropos.y);
+ } while (!t.passable);
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Calculating initial FOV...\n");
fov = FieldOfView(&tilemap);
- fov.calc(player_actor->get_position(), 6);
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
current_entity_index = 0;
Actor* actor = tilemap.get_actor_list()->at(current_entity_index);
is_player_turn = actor->player_controlled;
- if (is_player_turn) camera_pos = actor->get_position();
+ if (is_player_turn) {
+ camera_pos = actor->get_position();
+ fov.calc(actor->get_position(), 6);
+ }
}
Gamestate *PlayState::update(double delta) {
- if (!is_player_turn || player_action != ACTION_NONE) {
- SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Starting turn...\n");
+ while (!is_player_turn || player_action != ACTION_NONE) {
std::vector* actors = tilemap.get_actor_list();
Actor* actor = actors->at(current_entity_index);
@@ -114,26 +125,24 @@ Gamestate *PlayState::update(double delta) {
default: player_action = ACTION_NONE; SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: no player action.\n"); return nullptr; // abort turn
}
if (dir != vec2i(0,0)) {
- if (!actor->move(dir.x, dir.y)) {
+ if (!actor->move(dir.x, dir.y, &tilemap)) {
vec2i heropos = actor->get_position();
- auto acts = tilemap.get_actors(heropos.x + dir.x, heropos.y + dir.y, 0);
- if(acts.empty()) {
+ Actor* act = tilemap.get_actor(heropos.x + dir.x, heropos.y + dir.y);
+ if(act == nullptr) {
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: invalid player action.\n");
player_action = ACTION_NONE;
return nullptr; // unable to move and nothing to attack == abort turn
}
- for (auto ent : acts) {
- auto act = (Actor*)ent;
- if (act->is_alive() && act->get_actor_faction() != actor->get_actor_faction()) {
- actor->attack(act);
- break;
- }
+ if (act->is_alive() && act->get_actor_faction() != actor->get_actor_faction()) {
+ actor->attack(act);
}
}
}
- fov.calc(actor->get_position(), 6);
+ vec2i pos = actor->get_position();
+ fov.calc(pos, 6);
+ camera_pos = pos;
}
- actor->update();
+ actor->update(&tilemap);
player_action = ACTION_NONE;
@@ -143,8 +152,8 @@ Gamestate *PlayState::update(double delta) {
if (is_player_turn) {
camera_pos = next->get_position();
player_actor = next;
+ fov.calc(player_actor->get_position(), 6);
}
- SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn finished.\n");
}
return nullptr;
}
@@ -244,7 +253,8 @@ void PlayState::draw(double delta) {
int sprite = var->get_sprite_id();
Color fg = var->get_sprite_color();
- app->renderer->draw_sprite(ascii->get_sprite(sprite), fg, black, margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
+ Color bg = tilemap.get_tile(pos.x, pos.y).bg;
+ app->renderer->draw_sprite(ascii->get_sprite(sprite), fg, bg, margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
}
}
if (player_actor != nullptr) {
diff --git a/src/RangedAttackNode.cpp b/src/RangedAttackNode.cpp
index af693e9..7204b21 100644
--- a/src/RangedAttackNode.cpp
+++ b/src/RangedAttackNode.cpp
@@ -14,14 +14,14 @@ BehaviourTreeStatus RangedAttackNode::tick(BTTick * tick) {
bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
- auto actors = tick->target->get_map()->get_actors(targetpos.x, targetpos.y, 6);
+ auto actors = tick->map->get_actors(targetpos.x, targetpos.y, 6);
std::vector enemies;
for (Actor* actor : actors) {
if (actor == tick->target) continue;
if (actor->is_type_of(ACT_HERO) != ishero) {
vec2i pos = actor->get_position();
- if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
+ if (line_of_sight(tick->map, tick->target->get_position(), pos)) {
enemies.push_back(actor);
}
}
diff --git a/src/Shaman.cpp b/src/Shaman.cpp
index 7df6540..88faf72 100644
--- a/src/Shaman.cpp
+++ b/src/Shaman.cpp
@@ -11,7 +11,7 @@
BehaviourTree* shamtree = nullptr;
-Shaman::Shaman(Tilemap* map, vec2i pos) : Actor(map, pos) {
+Shaman::Shaman(vec2i pos) : Actor(pos) {
name = "Shaman";
alive = true;
health = 2;
diff --git a/src/Shaman.h b/src/Shaman.h
index 64aafb7..43a61e9 100644
--- a/src/Shaman.h
+++ b/src/Shaman.h
@@ -3,7 +3,7 @@
class Shaman :
public Actor {
public:
- Shaman(Tilemap* map, vec2i pos);
+ Shaman(vec2i pos);
~Shaman();
bool is_type_of(Actors actor) override { return actor == ACT_SHAMAN || Actor::is_type_of(actor); };
Actors type() override { return ACT_SHAMAN; }
diff --git a/src/TileSet.cpp b/src/TileSet.cpp
new file mode 100644
index 0000000..ddffb46
--- /dev/null
+++ b/src/TileSet.cpp
@@ -0,0 +1,126 @@
+#include "TileSet.h"
+#include
+#include
+
+Tile null = Tile();
+
+TileSet::TileSet() {}
+
+
+TileSet::~TileSet() {}
+
+void TileSet::load_from_table(kaguya::LuaStackRef table) {
+ for (std::string key : table.keys()) {
+ auto val = table[key];
+ Tile t;
+ if (val["glyph"].type() == LUA_TSTRING) {
+ std::string s = val["glyph"];
+ t.glyph = s.at(0);
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value glyph for tile: %s", key.c_str());
+ }
+ if (val["fg"].type() == LUA_TTABLE) {
+ auto fg = val["fg"];
+ t.fg.r = fg[1];
+ t.fg.g = fg[2];
+ t.fg.b = fg[3];
+ t.fg.a = fg[4];
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value fg for tile: %s", key.c_str());
+ }
+ if (val["bg"].type() == LUA_TTABLE) {
+ auto bg = val["bg"];
+ t.bg.r = bg[1];
+ t.bg.g = bg[2];
+ t.bg.b = bg[3];
+ t.bg.a = bg[4];
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value bg for tile: %s", key.c_str());
+ }
+ if (val["passable"].type() == LUA_TBOOLEAN) {
+ t.passable = val["passable"];
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value passable for tile: %s", key.c_str());
+ }
+ if (val["opaque"].type() == LUA_TBOOLEAN) {
+ t.opaque = val["opaque"];
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value opaque for tile: %s", key.c_str());
+ }
+ if (val["wall"].type() == LUA_TBOOLEAN) {
+ t.wall = val["wall"];
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value wall for tile: %s", key.c_str());
+ }
+ if (val["tags"].type() == LUA_TTABLE) {
+ for (std::string tag : val["tags"].values()) {
+ t.tags.emplace_back(tag);
+ }
+ }
+ else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Missing value wall for tile: %s", key.c_str());
+ }
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Added tile: %s", key.c_str());
+ tiles.insert_or_assign(key, t);
+ if (tiles.count(key) == 0) {
+ SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Tileset: Could not find the tile we just added?!: %s", key.c_str());
+ }
+ }
+}
+
+void TileSet::add_tile(std::string name, Tile tile) {
+ tiles.insert_or_assign(name, tile);
+}
+
+Tile const& TileSet::get_tile(std::string name) {
+ auto it = tiles.find(name);
+ if (it == tiles.end()) {
+ return null;
+ }
+ else {
+ return it->second;
+ }
+}
+
+std::vector TileSet::find_tiles(bool passable, bool opaque, bool wall, std::vector include_tags, std::vector exclude_tags) {
+ std::vector found_tiles;
+
+ // TODO: optimize this stuff, could use less for loops
+ for (auto pair : tiles) {
+ if (pair.second.passable == passable
+ && pair.second.opaque == opaque
+ && pair.second.wall == wall) {
+ bool match = true;
+ for (std::string& inc : include_tags) {
+ if (!match) break;
+ match = false;
+ for (std::string& tag : pair.second.tags) {
+ if (tag == inc) {
+ match = true;
+ break;
+ }
+ }
+ }
+ for (std::string& tag : pair.second.tags) {
+ if (!match) break;
+ for (std::string& excl : exclude_tags) {
+ if (tag == excl) {
+ match = false;
+ break;
+ }
+ }
+ }
+ if (match) {
+ found_tiles.emplace_back(pair.first);
+ }
+ }
+ }
+
+ return found_tiles;
+}
diff --git a/src/TileSet.h b/src/TileSet.h
new file mode 100644
index 0000000..3b298f1
--- /dev/null
+++ b/src/TileSet.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "Renderer.h"
+#include
+#include