Too much stuff once again
This commit is contained in:
parent
d5861948d2
commit
05e8f3d0a3
36 changed files with 558 additions and 231 deletions
26
data/actors.lua
Normal file
26
data/actors.lua
Normal file
|
@ -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},
|
||||
},
|
||||
}
|
70
data/tiles.lua
Normal file
70
data/tiles.lua
Normal file
|
@ -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;
|
|
@ -157,6 +157,7 @@
|
|||
<ClCompile Include="src\SpriteAtlas.cpp" />
|
||||
<ClCompile Include="src\WanderNode.cpp" />
|
||||
<ClCompile Include="src\World.cpp" />
|
||||
<ClCompile Include="src\TileSet.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\Shader.h" />
|
||||
|
@ -208,6 +209,7 @@
|
|||
<ClInclude Include="src\WanderNode.h" />
|
||||
<ClInclude Include="src\wglew.h" />
|
||||
<ClInclude Include="src\World.h" />
|
||||
<ClInclude Include="src\TileSet.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
|
@ -138,6 +138,9 @@
|
|||
<ClCompile Include="src\SpriteAtlas.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\TileSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\Actor.h">
|
||||
|
@ -287,5 +290,8 @@
|
|||
<ClInclude Include="src\SpriteAtlas.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\TileSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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) {
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -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<Actor*> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
class BehaviourTree;
|
||||
class BehaviourTreeNode;
|
||||
class Actor;
|
||||
class Tilemap;
|
||||
|
||||
struct BTTick {
|
||||
Actor* target;
|
||||
Tilemap* map;
|
||||
BehaviourTree* tree;
|
||||
std::vector<BehaviourTreeNode*> openNodes;
|
||||
};
|
||||
|
@ -17,6 +19,6 @@ class BehaviourTree {
|
|||
public:
|
||||
BehaviourTree(BehaviourTreeNode* rootNode);
|
||||
~BehaviourTree();
|
||||
void tick(Actor* target);
|
||||
void tick(Actor* target, Tilemap* map);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
19
src/Entity.h
19
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
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
//
|
||||
// Created by Adrian on 2017-09-21.
|
||||
//
|
||||
|
||||
#include <cmath>
|
||||
#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<unsigned int>(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)))
|
||||
|
|
|
@ -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 <vector>
|
||||
|
||||
class FieldOfView {
|
||||
Tilemap* map;
|
||||
unsigned int counter;
|
||||
Tilemap seen;
|
||||
unsigned int counter = 0;
|
||||
std::vector<unsigned int> 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
|
||||
|
|
|
@ -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<vec2i> 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<vec2i> 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<vec2i> 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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<Actor*> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
167
src/Mapgen.cpp
167
src/Mapgen.cpp
|
@ -5,35 +5,35 @@
|
|||
#include <random>
|
||||
#include "Rng.h"
|
||||
#include <chrono>
|
||||
#include <stack>
|
||||
#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<vec2i> dirs { vec2i(0,1), vec2i(1,0), vec2i(0,-1), vec2i(-1,0) };
|
||||
|
||||
std::vector<vec2i> stack { vec2i(x,y) };
|
||||
std::stack<vec2i> 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<vec2i> 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<vec2i> 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<std::string> wall_tiles = tileset.find_tiles(false, true, true, { "dungeon", "wall" }, {});
|
||||
std::vector<std::string> floor_tiles = tileset.find_tiles(true, false, false, { "dungeon", "floor" }, {});
|
||||
std::vector<std::string> 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<Room> 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<vec2i> 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<vec2i> 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<vec2i> 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<vec2i>{ 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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <math.h>
|
||||
#include "Pathfinder.h"
|
||||
#include "Tilemap.h"
|
||||
#include <queue>
|
||||
|
||||
namespace Pathfinder
|
||||
{
|
||||
|
@ -11,7 +12,7 @@ namespace Pathfinder
|
|||
return sqrtf(dx*dx + dy*dy);
|
||||
}
|
||||
|
||||
std::vector<vec2i> aStar(Tilemap * map, vec2i start, vec2i goal)
|
||||
std::vector<vec2i> a_star(Tilemap * map, vec2i start, vec2i goal)
|
||||
{
|
||||
std::vector<AStarNode*> open;
|
||||
std::vector<AStarNode*> 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<vec2i>* goals, DijkstraMap * out, float maxValue) {
|
||||
if (out->tilemap != nullptr) {
|
||||
delete out->tilemap;
|
||||
void calc_dijkstra_map(Tilemap& map, std::vector<vec2i>& 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<vec2i> queue;
|
||||
std::queue<vec2i> 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<vec2i> 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<vec2i> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<vec2i> aStar(Tilemap* map, vec2i start, vec2i goal);
|
||||
void calcDijkstraMap(Tilemap* map, std::vector<vec2i>* goals, DijkstraMap* out, float maxValue = 32);
|
||||
}
|
||||
std::vector<vec2i> a_star(Tilemap* map, vec2i start, vec2i goal);
|
||||
void calc_dijkstra_map(Tilemap& map, std::vector<vec2i>& goals, DijkstraMap& out, float maxValue = 32);
|
||||
}
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
#include "Goblin.h"
|
||||
#include "Shaman.h"
|
||||
#include "Rng.h"
|
||||
|
||||
const int mapwidth = 32;
|
||||
#include "TileSet.h"
|
||||
#include <kaguya\kaguya.hpp>
|
||||
|
||||
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<Actor*>* 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) {
|
||||
|
|
|
@ -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<Actor*> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
126
src/TileSet.cpp
Normal file
126
src/TileSet.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include "TileSet.h"
|
||||
#include <kaguya\kaguya.hpp>
|
||||
#include <SDL2\SDL_log.h>
|
||||
|
||||
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<std::string> TileSet::find_tiles(bool passable, bool opaque, bool wall, std::vector<std::string> include_tags, std::vector<std::string> exclude_tags) {
|
||||
std::vector<std::string> 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;
|
||||
}
|
39
src/TileSet.h
Normal file
39
src/TileSet.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "Renderer.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <kaguya/kaguya.hpp>
|
||||
|
||||
struct Tile {
|
||||
char glyph = 0;
|
||||
Color fg = Color(1, 1, 1, 1);
|
||||
Color bg = Color(0, 0, 0, 1);
|
||||
bool passable = false;
|
||||
bool opaque = false;
|
||||
bool wall = true;
|
||||
std::string desc = "";
|
||||
std::vector<std::string> tags;
|
||||
|
||||
bool has_tag(std::string tag) const {
|
||||
for (std::string t : tags) {
|
||||
if (t == tag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class TileSet {
|
||||
std::map<std::string, Tile> tiles;
|
||||
public:
|
||||
TileSet();
|
||||
~TileSet();
|
||||
void load_from_table(kaguya::LuaStackRef table);
|
||||
void add_tile(std::string name, Tile tile);
|
||||
Tile const& get_tile(std::string name);
|
||||
std::vector<std::string> find_tiles(bool passable, bool opaque, bool wall, std::vector<std::string> include_tags = {}, std::vector<std::string> exclude_tags = {});
|
||||
};
|
||||
|
|
@ -10,10 +10,11 @@ int Tilemap::get_index(int x, int y) {
|
|||
return y * width + x;
|
||||
}
|
||||
|
||||
Tilemap::Tilemap(int width, int height) {
|
||||
Tilemap::Tilemap(TileSet tileset, int width, int height) {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
tilemap = std::vector<unsigned int>(width*height, 0);
|
||||
tilemap = std::vector<std::string>(width*height, "");
|
||||
tiles = tileset;
|
||||
}
|
||||
|
||||
Tilemap::~Tilemap() {}
|
||||
|
@ -46,22 +47,26 @@ std::vector<vec2i> Tilemap::get_neighbours(int x, int y, int up, int down, int l
|
|||
return neigh;
|
||||
}
|
||||
|
||||
void Tilemap::set_tile(int x, int y, unsigned int tile) {
|
||||
void Tilemap::set_tile(int x, int y, std::string tile) {
|
||||
if (is_inside_bounds(x, y)) {
|
||||
tilemap[get_index(x, y)] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
int Tilemap::get_tile(int x, int y) {
|
||||
std::string Tilemap::get_tile_id(int x, int y) {
|
||||
if (is_inside_bounds(x, y)) {
|
||||
return tilemap[get_index(x, y)];
|
||||
}
|
||||
return -1;
|
||||
return "nil";
|
||||
}
|
||||
|
||||
Tile const& Tilemap::get_tile(int x, int y) {
|
||||
return tiles.get_tile(get_tile_id(x,y));
|
||||
}
|
||||
|
||||
bool Tilemap::is_blocked(int x, int y) {
|
||||
if (is_inside_bounds(x, y)) {
|
||||
if (tilemap[get_index(x, y)] == '#') { // TODO: Replace hardcoded tiles
|
||||
if (!tiles.get_tile(tilemap[get_index(x, y)]).passable) {
|
||||
return true;
|
||||
}
|
||||
for (Entity* var : actors) {
|
||||
|
@ -92,14 +97,12 @@ void Tilemap::remove_actor(Actor * actor) {
|
|||
}
|
||||
}
|
||||
|
||||
Entity * Tilemap::get_entity(int x, int y, EntityTypes type) {
|
||||
Actor * Tilemap::get_actor(int x, int y) {
|
||||
vec2i pos = { x,y };
|
||||
for (Entity* ent : actors) {
|
||||
if (ent->entity_type() == type) {
|
||||
vec2i apos = ent->get_position();
|
||||
if (apos == pos) {
|
||||
return ent;
|
||||
}
|
||||
for (Actor* ent : actors) {
|
||||
vec2i apos = ent->get_position();
|
||||
if (apos == pos) {
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -130,21 +133,23 @@ void Tilemap::delete_actors() {
|
|||
}
|
||||
}
|
||||
|
||||
void Tilemap::draw(Renderer *renderer, SpriteAtlas* tileset, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view) {
|
||||
int w = tileset->get_tile_width();
|
||||
int h = tileset->get_tile_height();
|
||||
void Tilemap::draw(Renderer *renderer, SpriteAtlas* sprites, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view) {
|
||||
int w = sprites->get_tile_width();
|
||||
int h = sprites->get_tile_height();
|
||||
for (int ix = 0; ix < tw; ix++) {
|
||||
for (int iy = 0; iy < th; iy++) {
|
||||
int ax = tx + ix;
|
||||
int ay = ty + iy;
|
||||
if (is_inside_bounds(ax, ay)) {
|
||||
if (view == nullptr || view->has_seen({ ax, ay })) {
|
||||
Color fg = Color(1, 1, 1, 1);
|
||||
Color bg = Color(0, 0, 0, 1);
|
||||
Tile t = get_tile(ax, ay);
|
||||
Color fg = t.fg;
|
||||
Color bg = t.bg;
|
||||
if (view != nullptr && !view->can_see({ ax, ay })) {
|
||||
fg.a = 0.4f;
|
||||
bg.a = 0.4f;
|
||||
}
|
||||
renderer->draw_sprite(tileset->get_sprite(get_tile(ax, ay)), fg, bg, x + ix * w, y + iy * h);
|
||||
renderer->draw_sprite(sprites->get_sprite(t.glyph), fg, bg, x + ix * w, y + iy * h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,20 @@
|
|||
#include <vector>
|
||||
#include "SpriteAtlas.h"
|
||||
#include "Actor.h"
|
||||
#include "TileSet.h"
|
||||
|
||||
struct vec2i;
|
||||
class Renderer;
|
||||
class FieldOfView;
|
||||
|
||||
class Tilemap {
|
||||
std::vector<unsigned int> tilemap;
|
||||
std::vector<std::string> tilemap;
|
||||
std::vector<Actor*> actors;
|
||||
int width;
|
||||
int height;
|
||||
public:
|
||||
Tilemap(int width = 1, int height = 1);
|
||||
Tilemap(TileSet tileset = TileSet(), int width = 1, int height = 1);
|
||||
TileSet tiles;
|
||||
~Tilemap();
|
||||
int get_width();
|
||||
int get_height();
|
||||
|
@ -22,8 +24,9 @@ public:
|
|||
bool is_inside_bounds(int x, int y);
|
||||
std::vector<vec2i> get_neighbours(int x, int y, int range = 1);
|
||||
std::vector<vec2i> get_neighbours(int x, int y, int up, int down, int left, int right);
|
||||
void set_tile(int x, int y, unsigned int tile); // "Tile" is inteded for tile ids, but can be anything really.
|
||||
int get_tile(int x, int y);
|
||||
void set_tile(int x, int y, std::string tile); // "Tile" is inteded for tile ids, but can be anything really.
|
||||
std::string get_tile_id(int x, int y);
|
||||
Tile const& get_tile(int x, int y);
|
||||
bool is_blocked(int x, int y); // Checks if there is an actor blocking the tile.
|
||||
|
||||
void draw(Renderer *renderer, SpriteAtlas *tileset, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view);
|
||||
|
@ -33,7 +36,7 @@ public:
|
|||
|
||||
void debug_print();
|
||||
|
||||
Entity* get_entity(int x, int y, EntityTypes type);
|
||||
Actor* get_actor(int x, int y);
|
||||
std::vector<Actor*> get_actors(int x, int y, int range);
|
||||
std::vector<Actor*>* get_actor_list();
|
||||
void delete_actors();
|
||||
|
|
|
@ -11,7 +11,7 @@ WanderNode::~WanderNode() = default;
|
|||
|
||||
BehaviourTreeStatus WanderNode::tick(BTTick * tick) {
|
||||
vec2i pos = tick->target->get_position();
|
||||
std::vector<vec2i> neighbours = tick->target->get_map()->get_neighbours(pos.x, pos.y);
|
||||
std::vector<vec2i> neighbours = tick->map->get_neighbours(pos.x, pos.y);
|
||||
while (true) {
|
||||
if (neighbours.empty()) {
|
||||
previous.clear();
|
||||
|
@ -26,7 +26,7 @@ BehaviourTreeStatus WanderNode::tick(BTTick * tick) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (valid && tick->target->move(dp.x, dp.y)) {
|
||||
if (valid && tick->target->move(dp.x, dp.y, tick->map)) {
|
||||
previous.push_back(neighbours[i]);
|
||||
if (previous.size() > 5) {
|
||||
previous.erase(previous.begin());
|
||||
|
|
|
@ -28,7 +28,7 @@ Tilemap* World::GetMap(unsigned int level) {
|
|||
return maps[level];
|
||||
}
|
||||
else {
|
||||
Tilemap* map = new Tilemap(64, 64);
|
||||
Tilemap* map = new Tilemap(TileSet(),64, 64);
|
||||
// TODO: generate map
|
||||
maps[level] = map;
|
||||
return map;
|
||||
|
@ -45,4 +45,4 @@ World::~World() {
|
|||
}
|
||||
}
|
||||
maps.clear();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue