Too much stuff once again
This commit is contained in:
parent
d5861948d2
commit
05e8f3d0a3
36 changed files with 558 additions and 231 deletions
data
dungeon.vcxprojdungeon.vcxproj.filterssrc
Actor.cppActor.hAttackEnemyNode.cppBehaviourTree.cppBehaviourTree.hEntity.cppEntity.hFieldOfView.cppFieldOfView.hFleeNode.cppGoblin.cppGoblin.hHealFriendNode.cppHero.cppHero.hIfNotSeeEnemyNode.cppIfSeeEnemyNode.cppIfSeeFriendNode.cppMapgen.cppMapgen.hPathfinder.cppPathfinder.hPlayState.cppRangedAttackNode.cppShaman.cppShaman.hTileSet.cppTileSet.hTilemap.cppTilemap.hWanderNode.cppWorld.cpp
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\SpriteAtlas.cpp" />
|
||||||
<ClCompile Include="src\WanderNode.cpp" />
|
<ClCompile Include="src\WanderNode.cpp" />
|
||||||
<ClCompile Include="src\World.cpp" />
|
<ClCompile Include="src\World.cpp" />
|
||||||
|
<ClCompile Include="src\TileSet.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\Shader.h" />
|
<ClInclude Include="src\Shader.h" />
|
||||||
|
@ -208,6 +209,7 @@
|
||||||
<ClInclude Include="src\WanderNode.h" />
|
<ClInclude Include="src\WanderNode.h" />
|
||||||
<ClInclude Include="src\wglew.h" />
|
<ClInclude Include="src\wglew.h" />
|
||||||
<ClInclude Include="src\World.h" />
|
<ClInclude Include="src\World.h" />
|
||||||
|
<ClInclude Include="src\TileSet.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|
|
@ -138,6 +138,9 @@
|
||||||
<ClCompile Include="src\SpriteAtlas.cpp">
|
<ClCompile Include="src\SpriteAtlas.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\TileSet.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\Actor.h">
|
<ClInclude Include="src\Actor.h">
|
||||||
|
@ -287,5 +290,8 @@
|
||||||
<ClInclude Include="src\SpriteAtlas.h">
|
<ClInclude Include="src\SpriteAtlas.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\TileSet.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -4,21 +4,22 @@
|
||||||
|
|
||||||
int id_counter = 0;
|
int id_counter = 0;
|
||||||
|
|
||||||
Actor::Actor(Tilemap *map, vec2i pos) : Entity(map, pos) {
|
Actor::Actor(vec2i pos) : Entity(pos) {
|
||||||
id = id_counter++;
|
id = id_counter++;
|
||||||
name = "Actor";
|
name = "Actor";
|
||||||
range = 1.5f;
|
range = 1.5f;
|
||||||
|
player_controlled = false;
|
||||||
collision = true;
|
collision = true;
|
||||||
bt = nullptr;
|
bt = nullptr;
|
||||||
faction = FACTION_NONE;
|
faction = FACTION_NONE;
|
||||||
sprite_id = 1;
|
sprite_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actor::update() {
|
void Actor::update(Tilemap* map) {
|
||||||
if (!alive) return;
|
if (!alive) return;
|
||||||
|
|
||||||
if (!player_controlled && bt != nullptr) {
|
if (!player_controlled && bt != nullptr) {
|
||||||
bt->tick(this);
|
bt->tick(this, map);
|
||||||
}
|
}
|
||||||
if (health < health_max) {
|
if (health < health_max) {
|
||||||
healcounter--;
|
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) {
|
if (dpos.dist() <= range) {
|
||||||
vec2i pos = get_position();
|
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) {
|
for (Entity* ent : acts) {
|
||||||
auto act = (Actor*)ent;
|
auto act = (Actor*)ent;
|
||||||
if (act->is_alive() && act->get_actor_faction() != faction) {
|
if (act->is_alive() && act->get_actor_faction() != faction) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ protected:
|
||||||
float range;
|
float range;
|
||||||
bool alive;
|
bool alive;
|
||||||
ActorFactions faction;
|
ActorFactions faction;
|
||||||
Actor(Tilemap *map, vec2i pos);
|
Actor(vec2i pos);
|
||||||
public:
|
public:
|
||||||
int id;
|
int id;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -39,7 +39,7 @@ public:
|
||||||
|
|
||||||
//Actor(Tilemap *map, vec2i pos, std::string datakey);
|
//Actor(Tilemap *map, vec2i pos, std::string datakey);
|
||||||
bool is_alive(){ return alive; };
|
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 attack(Actor* act);
|
||||||
void heal(int amount);
|
void heal(int amount);
|
||||||
void damage(int strength);
|
void damage(int strength);
|
||||||
|
@ -49,7 +49,7 @@ public:
|
||||||
ActorFactions get_actor_faction() { return faction; }
|
ActorFactions get_actor_faction() { return faction; }
|
||||||
float get_range() { return range; }
|
float get_range() { return range; }
|
||||||
void kill() { alive = false; health = 0; collision = false; };
|
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 bool is_type_of(Actors actor){ return actor == ACT_BASE; };
|
||||||
virtual Actors type() { return ACT_BASE; };
|
virtual Actors type() { return ACT_BASE; };
|
||||||
EntityTypes entity_type() override { return ENTITY_ACTOR; };
|
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);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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;
|
std::vector<Actor*> visibleEnemies;
|
||||||
|
|
||||||
for (Actor* actor : actors) {
|
for (Actor* actor : actors) {
|
||||||
|
@ -22,7 +22,7 @@ BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
|
||||||
|
|
||||||
if (actor->is_type_of(ACT_HERO) != ishero) {
|
if (actor->is_type_of(ACT_HERO) != ishero) {
|
||||||
vec2i pos = actor->get_position();
|
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);
|
visibleEnemies.push_back(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,11 +53,11 @@ BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
|
||||||
else {
|
else {
|
||||||
vec2i pos = tick->target->get_position();
|
vec2i pos = tick->target->get_position();
|
||||||
vec2i goal = closestActor->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()) {
|
if (!path.empty()) {
|
||||||
//path.pop_back();
|
//path.pop_back();
|
||||||
vec2i dpos = path.back() - pos;
|
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;
|
return BT_RUNNING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,11 @@ BehaviourTree::~BehaviourTree() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BehaviourTree::tick(Actor * target) {
|
void BehaviourTree::tick(Actor * target, Tilemap* map) {
|
||||||
BTTick tick;
|
BTTick tick;
|
||||||
tick.target = target;
|
tick.target = target;
|
||||||
tick.tree = this;
|
tick.tree = this;
|
||||||
|
tick.map = map;
|
||||||
|
|
||||||
root->execute(&tick);
|
root->execute(&tick);
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
class BehaviourTree;
|
class BehaviourTree;
|
||||||
class BehaviourTreeNode;
|
class BehaviourTreeNode;
|
||||||
class Actor;
|
class Actor;
|
||||||
|
class Tilemap;
|
||||||
|
|
||||||
struct BTTick {
|
struct BTTick {
|
||||||
Actor* target;
|
Actor* target;
|
||||||
|
Tilemap* map;
|
||||||
BehaviourTree* tree;
|
BehaviourTree* tree;
|
||||||
std::vector<BehaviourTreeNode*> openNodes;
|
std::vector<BehaviourTreeNode*> openNodes;
|
||||||
};
|
};
|
||||||
|
@ -17,6 +19,6 @@ class BehaviourTree {
|
||||||
public:
|
public:
|
||||||
BehaviourTree(BehaviourTreeNode* rootNode);
|
BehaviourTree(BehaviourTreeNode* rootNode);
|
||||||
~BehaviourTree();
|
~BehaviourTree();
|
||||||
void tick(Actor* target);
|
void tick(Actor* target, Tilemap* map);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
#include "Tilemap.h"
|
#include "Tilemap.h"
|
||||||
|
|
||||||
Entity::Entity(Tilemap *map, vec2i pos) {
|
Entity::Entity(vec2i pos) {
|
||||||
this->map = map;
|
|
||||||
position = pos;
|
position = pos;
|
||||||
collision = false;
|
collision = false;
|
||||||
sprite_id = '?';
|
sprite_id = '?';
|
||||||
|
@ -16,11 +15,11 @@ vec2i Entity::get_position() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Entity::move(vec2i dpos) {
|
bool Entity::move(vec2i dpos, Tilemap* map) {
|
||||||
return move(dpos.x, dpos.y);
|
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);
|
vec2i newpos = position + vec2i(dx, dy);
|
||||||
if (!collision || !map->is_blocked(newpos.x, newpos.y)) {
|
if (!collision || !map->is_blocked(newpos.x, newpos.y)) {
|
||||||
position = newpos;
|
position = newpos;
|
||||||
|
@ -33,10 +32,6 @@ void Entity::set_position(vec2i pos) {
|
||||||
position = pos;
|
position = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tilemap *Entity::get_map() {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entity::has_collision() {
|
bool Entity::has_collision() {
|
||||||
return collision;
|
return collision;
|
||||||
}
|
}
|
||||||
|
|
19
src/Entity.h
19
src/Entity.h
|
@ -1,10 +1,4 @@
|
||||||
//
|
#pragma once
|
||||||
// Created by Adrian on 2017-09-25.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef DUNGEON_ENTITY_H
|
|
||||||
#define DUNGEON_ENTITY_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "vec2i.h"
|
#include "vec2i.h"
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
|
@ -19,24 +13,19 @@ enum EntityTypes {
|
||||||
|
|
||||||
class Entity {
|
class Entity {
|
||||||
vec2i position;
|
vec2i position;
|
||||||
Tilemap* map;
|
|
||||||
protected:
|
protected:
|
||||||
unsigned int sprite_id;
|
unsigned int sprite_id;
|
||||||
Color sprite_color;
|
Color sprite_color;
|
||||||
bool collision;
|
bool collision;
|
||||||
public:
|
public:
|
||||||
Entity(Tilemap* map, vec2i pos);
|
Entity(vec2i pos);
|
||||||
|
|
||||||
Tilemap* get_map();
|
|
||||||
vec2i get_position();
|
vec2i get_position();
|
||||||
bool has_collision();
|
bool has_collision();
|
||||||
bool move(int dx, int dy); // returns false if movement failed
|
bool move(int dx, int dy, Tilemap* map); // returns false if movement failed
|
||||||
bool move(vec2i dpos);
|
bool move(vec2i dpos, Tilemap* map);
|
||||||
void set_position(vec2i pos);
|
void set_position(vec2i pos);
|
||||||
unsigned int get_sprite_id() { return sprite_id; };
|
unsigned int get_sprite_id() { return sprite_id; };
|
||||||
Color get_sprite_color() { return sprite_color; };
|
Color get_sprite_color() { return sprite_color; };
|
||||||
virtual EntityTypes entity_type() { return ENTITY_BASE; };
|
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 <cmath>
|
||||||
#include "FieldOfView.h"
|
#include "FieldOfView.h"
|
||||||
#include "Tilemap.h"
|
#include "Tilemap.h"
|
||||||
|
@ -12,13 +8,12 @@ FieldOfView::FieldOfView() {
|
||||||
|
|
||||||
FieldOfView::FieldOfView(Tilemap *map) {
|
FieldOfView::FieldOfView(Tilemap *map) {
|
||||||
this->map = map;
|
this->map = map;
|
||||||
seen = Tilemap(map->get_width(), map->get_height());
|
seen = std::vector<unsigned int>(map->get_width()*map->get_height(),0);
|
||||||
counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldOfView::calc(vec2i pos, float range) {
|
void FieldOfView::calc(vec2i pos, float range) {
|
||||||
counter++;
|
counter++;
|
||||||
seen.set_tile(pos.x, pos.y, counter);
|
seen[map->get_index(pos.x, pos.y)] = counter;
|
||||||
// Once for each octant
|
// Once for each octant
|
||||||
if (map != nullptr) {
|
if (map != nullptr) {
|
||||||
cast_light(1, 1.0f, 0.0f, 0, -1, -1, 0, pos.x, pos.y, range);
|
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) {
|
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) {
|
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,
|
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) {
|
if (sqrt(deltaX*deltaX + deltaY*deltaY) <= radius) {
|
||||||
seen.set_tile(currentX, currentY, counter);
|
seen[map->get_index(currentX, currentY)] = counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocked) {
|
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;
|
newStart = rightSlope;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +75,7 @@ void FieldOfView::cast_light(int row, float start, float end, int xx, int xy, in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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;
|
blocked = true;
|
||||||
cast_light(distance + 1, start, leftSlope, xx, xy, yx, yy, startX, startY, radius);
|
cast_light(distance + 1, start, leftSlope, xx, xy, yx, yy, startX, startY, radius);
|
||||||
newStart = rightSlope;
|
newStart = rightSlope;
|
||||||
|
@ -103,7 +98,7 @@ bool line_of_sight(Tilemap *map, vec2i start, vec2i end) {
|
||||||
// error may go below zero
|
// error may go below zero
|
||||||
int error(delta.y - (delta.x >> 1));
|
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
|
// reduce error, while taking into account the corner case of error == 0
|
||||||
if ((error > 0) || (!error && (ix > 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
|
// error may go below zero
|
||||||
int error(delta.x - (delta.y >> 1));
|
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
|
// reduce error, while taking into account the corner case of error == 0
|
||||||
if ((error > 0) || (!error && (iy > 0)))
|
if ((error > 0) || (!error && (iy > 0)))
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
//
|
#pragma once
|
||||||
// Created by Adrian on 2017-09-21.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef DUNGEON_FIELDOFVIEW_H
|
|
||||||
#define DUNGEON_FIELDOFVIEW_H
|
|
||||||
|
|
||||||
#include "vec2i.h"
|
#include "vec2i.h"
|
||||||
#include "Tilemap.h"
|
#include "Tilemap.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class FieldOfView {
|
class FieldOfView {
|
||||||
Tilemap* map;
|
Tilemap* map;
|
||||||
unsigned int counter;
|
unsigned int counter = 0;
|
||||||
Tilemap seen;
|
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);
|
void cast_light(int row, float start, float end, int xx, int xy, int yx, int yy, int startX, int startY, float radius);
|
||||||
public:
|
public:
|
||||||
|
unsigned int seen_cutoff = 0;
|
||||||
FieldOfView();
|
FieldOfView();
|
||||||
FieldOfView(Tilemap* map);
|
FieldOfView(Tilemap* map);
|
||||||
void calc(vec2i pos, float range);
|
void calc(vec2i pos, float range);
|
||||||
|
@ -22,5 +19,3 @@ public:
|
||||||
bool has_seen(vec2i pos);
|
bool has_seen(vec2i pos);
|
||||||
};
|
};
|
||||||
bool line_of_sight(Tilemap* map, vec2i start, vec2i end);
|
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) {
|
BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
|
||||||
Pathfinder::DijkstraMap dijkstra;
|
Pathfinder::DijkstraMap dijkstra;
|
||||||
Tilemap * map = tick->target->get_map();
|
Tilemap * map = tick->map;
|
||||||
std::vector<vec2i> enemyPos;
|
std::vector<vec2i> enemyPos;
|
||||||
bool ishero = tick->target->is_type_of(ACT_HERO);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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) {
|
for (Actor* actor : actors) {
|
||||||
if (actor->is_type_of(ACT_HERO) != ishero) {
|
if (actor->is_type_of(ACT_HERO) != ishero) {
|
||||||
vec2i pos = actor->get_position();
|
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);
|
enemyPos.push_back(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
|
||||||
if (enemyPos.empty()) {
|
if (enemyPos.empty()) {
|
||||||
return BT_FAILED;
|
return BT_FAILED;
|
||||||
}
|
}
|
||||||
Pathfinder::calcDijkstraMap(map, &enemyPos, &dijkstra, 16);
|
Pathfinder::calc_dijkstra_map(*map, enemyPos, dijkstra, 16);
|
||||||
|
|
||||||
dijkstra.add(-16);
|
dijkstra.add(-16);
|
||||||
dijkstra.mult(-1);
|
dijkstra.mult(-1);
|
||||||
|
@ -38,13 +38,13 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
|
||||||
std::vector<vec2i> safety;
|
std::vector<vec2i> safety;
|
||||||
for (int x = 0; x < dijkstra.width; x++) {
|
for (int x = 0; x < dijkstra.width; x++) {
|
||||||
for (int y = 0; y < dijkstra.height; y++) {
|
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);
|
safety.emplace_back(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pathfinder::calcDijkstraMap(map, &safety, &dijkstra);
|
Pathfinder::calc_dijkstra_map(*map, safety, dijkstra);
|
||||||
|
|
||||||
vec2i pos = tick->target->get_position();
|
vec2i pos = tick->target->get_position();
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
|
||||||
std::vector<vec2i> options;
|
std::vector<vec2i> options;
|
||||||
float lowestval = 999999;
|
float lowestval = 999999;
|
||||||
for (vec2i npos : neigh) {
|
for (vec2i npos : neigh) {
|
||||||
float val = dijkstra.getValue(npos.x, npos.y);
|
float val = dijkstra.get_value(npos.x, npos.y);
|
||||||
if (val < lowestval) {
|
if (val < lowestval) {
|
||||||
lowestval = val;
|
lowestval = val;
|
||||||
options.clear();
|
options.clear();
|
||||||
|
@ -70,7 +70,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
|
||||||
int i = rand() % options.size();
|
int i = rand() % options.size();
|
||||||
vec2i next = options[i];
|
vec2i next = options[i];
|
||||||
vec2i dp = next - pos;
|
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);
|
//printf("FLEEING val:%f\t(%i,%i)\n", lowestval, next.x, next.y);
|
||||||
return BT_RUNNING;
|
return BT_RUNNING;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
BehaviourTree* gobtree = nullptr;
|
BehaviourTree* gobtree = nullptr;
|
||||||
|
|
||||||
Goblin::Goblin(Tilemap* map, vec2i pos) : Actor(map, pos) {
|
Goblin::Goblin(vec2i pos) : Actor(pos) {
|
||||||
name = "Goblin";
|
name = "Goblin";
|
||||||
alive = true;
|
alive = true;
|
||||||
health = 4;
|
health = 4;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
class Goblin :
|
class Goblin :
|
||||||
public Actor {
|
public Actor {
|
||||||
public:
|
public:
|
||||||
Goblin(Tilemap* map, vec2i pos);
|
Goblin(vec2i pos);
|
||||||
~Goblin();
|
~Goblin();
|
||||||
bool is_type_of(Actors actor) { return actor == ACT_GOBLIN || Actor::is_type_of(actor); };
|
bool is_type_of(Actors actor) { return actor == ACT_GOBLIN || Actor::is_type_of(actor); };
|
||||||
Actors type() { return ACT_GOBLIN; }
|
Actors type() { return ACT_GOBLIN; }
|
||||||
|
|
|
@ -13,14 +13,14 @@ BehaviourTreeStatus HealFriendNode::tick(BTTick * tick) {
|
||||||
bool ishero = tick->target->is_type_of(ACT_HERO);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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;
|
std::vector<Actor*> friends;
|
||||||
for (Actor* actor : actors) {
|
for (Actor* actor : actors) {
|
||||||
if (actor == tick->target) continue;
|
if (actor == tick->target) continue;
|
||||||
|
|
||||||
if (actor->is_type_of(ACT_HERO) == ishero && actor->get_health() < actor->get_health_max()) {
|
if (actor->is_type_of(ACT_HERO) == ishero && actor->get_health() < actor->get_health_max()) {
|
||||||
vec2i pos = actor->get_position();
|
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);
|
friends.push_back(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "RestNode.h"
|
#include "RestNode.h"
|
||||||
#include "FleeNode.h"
|
#include "FleeNode.h"
|
||||||
|
|
||||||
Hero::Hero(Tilemap* map, vec2i pos) : Actor(map, pos) {
|
Hero::Hero(vec2i pos) : Actor(pos) {
|
||||||
name = "Hero";
|
name = "Hero";
|
||||||
alive = true;
|
alive = true;
|
||||||
player_controlled = true;
|
player_controlled = true;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
class Hero : public Actor {
|
class Hero : public Actor {
|
||||||
public:
|
public:
|
||||||
Hero(Tilemap* map, vec2i pos);
|
Hero(vec2i pos);
|
||||||
~Hero();
|
~Hero();
|
||||||
bool is_type_of(Actors actor) { return actor == ACT_HERO || Actor::is_type_of(actor); };
|
bool is_type_of(Actors actor) { return actor == ACT_HERO || Actor::is_type_of(actor); };
|
||||||
Actors type() { return ACT_HERO; }
|
Actors type() { return ACT_HERO; }
|
||||||
|
|
|
@ -16,13 +16,13 @@ BehaviourTreeStatus IfNotSeeEnemyNode::tick(BTTick * tick) {
|
||||||
bool ishero = tick->target->is_type_of(ACT_HERO);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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) {
|
for (Actor* actor : actors) {
|
||||||
if (actor == tick->target) continue;
|
if (actor == tick->target) continue;
|
||||||
|
|
||||||
if (actor->is_type_of(ACT_HERO) != ishero) {
|
if (actor->is_type_of(ACT_HERO) != ishero) {
|
||||||
vec2i pos = actor->get_position();
|
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;
|
return BT_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,13 @@ BehaviourTreeStatus IfSeeEnemyNode::tick(BTTick * tick) {
|
||||||
bool ishero = tick->target->is_type_of(ACT_HERO);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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) {
|
for (Actor* actor : actors) {
|
||||||
if (actor == tick->target) continue;
|
if (actor == tick->target) continue;
|
||||||
|
|
||||||
if (actor->is_type_of(ACT_HERO) != ishero) {
|
if (actor->is_type_of(ACT_HERO) != ishero) {
|
||||||
vec2i pos = actor->get_position();
|
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);
|
return children[0]->execute(tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ BehaviourTreeStatus IfSeeFriendNode::tick(BTTick * tick) {
|
||||||
bool ishero = tick->target->is_type_of(ACT_HERO);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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) {
|
for (Actor* actor : actors) {
|
||||||
if (actor == tick->target) continue;
|
if (actor == tick->target) continue;
|
||||||
|
|
||||||
if (actor->is_type_of(ACT_HERO) == ishero) {
|
if (actor->is_type_of(ACT_HERO) == ishero) {
|
||||||
vec2i pos = actor->get_position();
|
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);
|
return children[0]->execute(tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
167
src/Mapgen.cpp
167
src/Mapgen.cpp
|
@ -5,35 +5,35 @@
|
||||||
#include <random>
|
#include <random>
|
||||||
#include "Rng.h"
|
#include "Rng.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <stack>
|
||||||
|
#include "Goblin.h"
|
||||||
|
#include "Hero.h"
|
||||||
|
#include "Pathfinder.h"
|
||||||
|
|
||||||
struct Room {
|
struct Room {
|
||||||
vec2i pos;
|
vec2i pos;
|
||||||
vec2i size;
|
vec2i size;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char walltile = '#';
|
|
||||||
const char floortile = '.';
|
|
||||||
const char doortile = '+';
|
|
||||||
const char testtile = ' ';
|
|
||||||
|
|
||||||
bool aabb(Room &a, Room &b) {
|
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 &&
|
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;
|
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) {
|
void maze_fill(Tilemap& map, int x, int y, std::string wall, std::string floor, Rng &rng) {
|
||||||
if (map.get_tile(x, y) != walltile) return;
|
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) };
|
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()) {
|
while (!stack.empty()) {
|
||||||
vec2i pos = stack.back();
|
vec2i pos = stack.top();
|
||||||
map.set_tile(pos.x, pos.y, floortile);
|
map.set_tile(pos.x, pos.y, floor);
|
||||||
std::vector<vec2i> options;
|
std::vector<vec2i> options;
|
||||||
for (vec2i dir : dirs) {
|
for (vec2i dir : dirs) {
|
||||||
vec2i next = { pos.x + dir.x, pos.y + dir.y };
|
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;
|
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;
|
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);
|
std::vector<vec2i> neigh = map.get_neighbours(next.x, next.y, up, down, left, right);
|
||||||
bool enclosed = true;
|
bool enclosed = true;
|
||||||
for (vec2i n : neigh) {
|
for (vec2i n : neigh) {
|
||||||
if (map.get_tile(n.x, n.y) != walltile) {
|
if (!map.get_tile(n.x, n.y).wall) {
|
||||||
enclosed = false;
|
enclosed = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -55,31 +55,42 @@ void maze_fill(Tilemap& map, int x, int y, Rng &rng) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!options.empty()) {
|
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 {
|
else {
|
||||||
stack.pop_back();
|
stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tilemap generate_dungeon(int width, int height) {
|
Tilemap generate_dungeon(int width, int height, TileSet tileset) {
|
||||||
return generate_dungeon(Rng::get_random_seed(), width, height);
|
return generate_dungeon(Rng::get_random_seed(), width, height, tileset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tilemap generate_dungeon(unsigned int seed, int width, int height) {
|
Tilemap generate_dungeon(unsigned int seed, int width, int height, TileSet tileset) {
|
||||||
Tilemap map = Tilemap(width, height);
|
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
|
// Set the whole map to walls
|
||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
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
|
// Room placement
|
||||||
std::vector<Room> rooms;
|
std::vector<Room> rooms;
|
||||||
for (int i = 0; i < sqrt(width*height); i++) {
|
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 (Room r : rooms) {
|
||||||
for (int x = r.pos.x+1; x < r.pos.x + r.size.x-1; x++) {
|
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++) {
|
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);
|
std::vector<vec2i> neigh = map.get_neighbours(x, y, 1);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (vec2i n : neigh) {
|
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 this tile is a wall and is completely surrounded by other walls, start generating a maze here.
|
||||||
if (count >= 8) {
|
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));
|
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 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);
|
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;
|
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;
|
int doors_amount = potential_doors.size() < 3 ? potential_doors.size() : 4;
|
||||||
doors_amount = rng.get_int(2, doors_amount);
|
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);
|
int r = rng.get_int(potential_doors.size()-1);
|
||||||
vec2i pos = potential_doors.at(r);
|
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());
|
potential_doors.erase(r + potential_doors.begin());
|
||||||
for (int j = potential_doors.size() - 1; j >= 0; j--) {
|
for (int j = potential_doors.size() - 1; j >= 0; j--) {
|
||||||
if ((pos - potential_doors[j]).dist() <= 4) {
|
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) };
|
std::vector<vec2i> neigh{vec2i(x + 1, y), vec2i(x, y + 1), vec2i(x - 1, y), vec2i(x, y - 1) };
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (vec2i pos : neigh) {
|
for (vec2i pos : neigh) {
|
||||||
if (map.get_tile(pos.x, pos.y) == walltile) {
|
if (!map.get_tile(pos.x, pos.y).passable) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,7 +222,7 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
vec2i next;
|
vec2i next;
|
||||||
for (vec2i n : neigh) {
|
for (vec2i n : neigh) {
|
||||||
if (map.get_tile(n.x, n.y) == walltile) {
|
if (!map.get_tile(n.x, n.y).passable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -218,42 +231,90 @@ Tilemap generate_dungeon(unsigned int seed, int width, int height) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count == 1) {
|
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);
|
new_dead_ends.emplace_back(next);
|
||||||
}
|
}
|
||||||
else if (count == 0) {
|
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;
|
dead_ends = new_dead_ends;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* flood-fill the map to see that you can actually reach everywhere
|
// Place the entrance in a random room
|
||||||
bool started = false;
|
Room& startroom = rooms[rng.get_int(0, rooms.size() - 1)];
|
||||||
for (int x = 0; x < map.get_width(); x++) {
|
vec2i startpos = startroom.pos;
|
||||||
for (int y = 0; y < map.get_height(); y++) {
|
startpos.x += rng.get_int(1, startroom.size.x - 2);
|
||||||
if (map.get_tile(x,y) == floortile) {
|
startpos.y += rng.get_int(1, startroom.size.y - 2);
|
||||||
std::vector<vec2i> stack{vec2i(x,y)};
|
map.set_tile(startpos.x, startpos.y, entrance_tile);
|
||||||
map.set_tile(x, y, testtile);
|
|
||||||
while (!stack.empty()) {
|
|
||||||
vec2i pos = stack.back();
|
|
||||||
stack.pop_back();
|
|
||||||
|
|
||||||
auto neigh = map.get_neighbours(pos.x, pos.y);
|
// Find the room furthest away from the entrance and make it the exit
|
||||||
for (vec2i n : neigh) {
|
Pathfinder::DijkstraMap dijk;
|
||||||
char tile = map.get_tile(n.x, n.y);
|
const float maxv = width+height;
|
||||||
if (tile == floortile || tile == doortile) {
|
Pathfinder::calc_dijkstra_map(map, std::vector<vec2i>{ startpos }, dijk, maxv);
|
||||||
map.set_tile(pos.x, pos.y, testtile);
|
|
||||||
stack.emplace_back(n);
|
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;
|
return map;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Tilemap.h"
|
#include "Tilemap.h"
|
||||||
|
|
||||||
Tilemap generate_dungeon(int width, int height);
|
Tilemap generate_dungeon(int width, int height, TileSet tileset);
|
||||||
Tilemap generate_dungeon(unsigned int seed, int width, int height);
|
Tilemap generate_dungeon(unsigned int seed, int width, int height, TileSet tileset);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "Pathfinder.h"
|
#include "Pathfinder.h"
|
||||||
#include "Tilemap.h"
|
#include "Tilemap.h"
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
namespace Pathfinder
|
namespace Pathfinder
|
||||||
{
|
{
|
||||||
|
@ -11,7 +12,7 @@ namespace Pathfinder
|
||||||
return sqrtf(dx*dx + dy*dy);
|
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*> open;
|
||||||
std::vector<AStarNode*> closed;
|
std::vector<AStarNode*> closed;
|
||||||
|
@ -36,7 +37,7 @@ namespace Pathfinder
|
||||||
|
|
||||||
auto neighbours = map->get_neighbours(current->pos.x, current->pos.y);
|
auto neighbours = map->get_neighbours(current->pos.x, current->pos.y);
|
||||||
for (auto pos : neighbours) {
|
for (auto pos : neighbours) {
|
||||||
if (map->get_tile(pos.x, pos.y) == '#') {
|
if (!map->get_tile(pos.x, pos.y).passable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AStarNode* neighbour = new AStarNode();
|
AStarNode* neighbour = new AStarNode();
|
||||||
|
@ -140,53 +141,53 @@ namespace Pathfinder
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void calcDijkstraMap(Tilemap * map, std::vector<vec2i>* goals, DijkstraMap * out, float maxValue) {
|
void calc_dijkstra_map(Tilemap& map, std::vector<vec2i>& goals, DijkstraMap& out, float maxValue) {
|
||||||
if (out->tilemap != nullptr) {
|
if (out.tilemap != nullptr) {
|
||||||
delete out->tilemap;
|
delete out.tilemap;
|
||||||
}
|
}
|
||||||
out->tilemap = new float[map->get_width() * map->get_height()];
|
out.tilemap = new float[map.get_width() * map.get_height()];
|
||||||
for (int i = 0; i < map->get_width() * map->get_height(); i++) {
|
for (int i = 0; i < map.get_width() * map.get_height(); i++) {
|
||||||
out->tilemap[i] = maxValue;
|
out.tilemap[i] = maxValue;
|
||||||
}
|
}
|
||||||
out->height = map->get_height();
|
out.height = map.get_height();
|
||||||
out->width = map->get_width();
|
out.width = map.get_width();
|
||||||
for (vec2i pos : *goals) {
|
for (vec2i& pos : goals) {
|
||||||
out->setValue(pos.x, pos.y, 0);
|
out.setValue(pos.x, pos.y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<vec2i> queue;
|
std::queue<vec2i> queue;
|
||||||
|
|
||||||
for (vec2i pos : *goals) {
|
for (vec2i& pos : goals) {
|
||||||
auto neigh = map->get_neighbours(pos.x, pos.y);
|
auto neigh = map.get_neighbours(pos.x, pos.y);
|
||||||
for (vec2i npos : neigh) {
|
for (vec2i& npos : neigh) {
|
||||||
int val = out->getValue(npos.x, npos.y);
|
int val = out.get_value(npos.x, npos.y);
|
||||||
if (map->get_tile(npos.x, npos.y) != '#' && val > 1) {
|
if (map.get_tile(npos.x, npos.y).passable && val > 1.4f) {
|
||||||
if (npos.x != 0 && npos.y != 0) {
|
if (npos.x != 0 && npos.y != 0) {
|
||||||
out->setValue(npos.x, npos.y, 1.4f);
|
out.setValue(npos.x, npos.y, 1.4f);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
while (queue.size() > 0) {
|
||||||
vec2i current = queue.back();
|
vec2i current = queue.front();
|
||||||
queue.pop_back();
|
float val = out.get_value(current.x, current.y);
|
||||||
|
queue.pop();
|
||||||
|
|
||||||
std::vector<vec2i> neigh = map->get_neighbours(current.x, current.y);
|
std::vector<vec2i> neigh = map.get_neighbours(current.x, current.y);
|
||||||
for (int i = 0; i < neigh.size(); i++) {
|
for (vec2i& npos : neigh) {
|
||||||
float val = out->getValue(current.x, current.y) + 1;
|
|
||||||
vec2i npos = neigh[i];
|
|
||||||
vec2i dp = npos - current;
|
vec2i dp = npos - current;
|
||||||
|
float nval = val + 1;
|
||||||
if (dp.x != 0 && dp.y != 0) {
|
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
|
if (map.get_tile(npos.x, npos.y).passable && out.get_value(npos.x, npos.y) > nval) {
|
||||||
out->setValue(npos.x, npos.y, val);
|
out.setValue(npos.x, npos.y, nval);
|
||||||
queue.push_back(neigh[i]);
|
queue.push(npos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Pathfinder
|
||||||
float* tilemap = nullptr;
|
float* tilemap = nullptr;
|
||||||
int width, height;
|
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) {
|
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||||
return tilemap[y*width + x];
|
return tilemap[y*width + x];
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,6 @@ namespace Pathfinder
|
||||||
};
|
};
|
||||||
|
|
||||||
float distance(vec2i a, vec2i b);
|
float distance(vec2i a, vec2i b);
|
||||||
std::vector<vec2i> aStar(Tilemap* map, vec2i start, vec2i goal);
|
std::vector<vec2i> a_star(Tilemap* map, vec2i start, vec2i goal);
|
||||||
void calcDijkstraMap(Tilemap* map, std::vector<vec2i>* goals, DijkstraMap* out, float maxValue = 32);
|
void calc_dijkstra_map(Tilemap& map, std::vector<vec2i>& goals, DijkstraMap& out, float maxValue = 32);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
#include "Goblin.h"
|
#include "Goblin.h"
|
||||||
#include "Shaman.h"
|
#include "Shaman.h"
|
||||||
#include "Rng.h"
|
#include "Rng.h"
|
||||||
|
#include "TileSet.h"
|
||||||
const int mapwidth = 32;
|
#include <kaguya\kaguya.hpp>
|
||||||
|
|
||||||
InputAction player_action;
|
InputAction player_action;
|
||||||
|
TileSet tileset;
|
||||||
|
|
||||||
void PlayState::load() {
|
void PlayState::load() {
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating ascii tileset...\n");
|
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);
|
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
|
// Movement: keypad
|
||||||
app->input->bind_key(SDLK_KP_8, ACTION_MOVE_NORTH);
|
app->input->bind_key(SDLK_KP_8, ACTION_MOVE_NORTH);
|
||||||
app->input->bind_key(SDLK_KP_7, ACTION_MOVE_NORTHWEST);
|
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_n, ACTION_MOVE_SOUTHWEST);
|
||||||
app->input->bind_key(SDLK_m, ACTION_MOVE_SOUTHEAST);
|
app->input->bind_key(SDLK_m, ACTION_MOVE_SOUTHEAST);
|
||||||
|
|
||||||
|
// General
|
||||||
|
app->input->bind_key(SDLK_PERIOD, ACTION_WAIT);
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
app->input->bind_key(SDLK_F1, ACTION_TOGGLE_DEBUG);
|
app->input->bind_key(SDLK_F1, ACTION_TOGGLE_DEBUG);
|
||||||
app->input->bind_key(SDLK_r, ACTION_RESET);
|
app->input->bind_key(SDLK_r, ACTION_RESET);
|
||||||
|
@ -65,37 +77,36 @@ void PlayState::load() {
|
||||||
void PlayState::new_game() {
|
void PlayState::new_game() {
|
||||||
player_action = ACTION_NONE;
|
player_action = ACTION_NONE;
|
||||||
|
|
||||||
if (player_actor != nullptr) {
|
tilemap.delete_actors();
|
||||||
delete player_actor;
|
player_actor = nullptr;
|
||||||
player_actor = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n");
|
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n");
|
||||||
|
|
||||||
Rng rng;
|
Rng rng;
|
||||||
tilemap = generate_dungeon(128, 128);
|
tilemap = generate_dungeon(48, 48, tileset);
|
||||||
vec2i heropos;
|
vec2i heropos;
|
||||||
|
Tile t;
|
||||||
do {
|
do {
|
||||||
heropos.x = rng.get_int(1, tilemap.get_width() - 1);
|
heropos.x = rng.get_int(1, tilemap.get_width() - 1);
|
||||||
heropos.y = 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) == '#');
|
t = tilemap.get_tile(heropos.x, heropos.y);
|
||||||
player_actor = new Hero(&tilemap, vec2i(heropos.x,heropos.y));
|
} while (!t.passable);
|
||||||
tilemap.add_actor(player_actor);
|
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
|
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Calculating initial FOV...\n");
|
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Calculating initial FOV...\n");
|
||||||
fov = FieldOfView(&tilemap);
|
fov = FieldOfView(&tilemap);
|
||||||
fov.calc(player_actor->get_position(), 6);
|
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
|
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
|
||||||
|
|
||||||
current_entity_index = 0;
|
current_entity_index = 0;
|
||||||
Actor* actor = tilemap.get_actor_list()->at(current_entity_index);
|
Actor* actor = tilemap.get_actor_list()->at(current_entity_index);
|
||||||
is_player_turn = actor->player_controlled;
|
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) {
|
Gamestate *PlayState::update(double delta) {
|
||||||
if (!is_player_turn || player_action != ACTION_NONE) {
|
while (!is_player_turn || player_action != ACTION_NONE) {
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Starting turn...\n");
|
|
||||||
std::vector<Actor*>* actors = tilemap.get_actor_list();
|
std::vector<Actor*>* actors = tilemap.get_actor_list();
|
||||||
Actor* actor = actors->at(current_entity_index);
|
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
|
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 (dir != vec2i(0,0)) {
|
||||||
if (!actor->move(dir.x, dir.y)) {
|
if (!actor->move(dir.x, dir.y, &tilemap)) {
|
||||||
vec2i heropos = actor->get_position();
|
vec2i heropos = actor->get_position();
|
||||||
auto acts = tilemap.get_actors(heropos.x + dir.x, heropos.y + dir.y, 0);
|
Actor* act = tilemap.get_actor(heropos.x + dir.x, heropos.y + dir.y);
|
||||||
if(acts.empty()) {
|
if(act == nullptr) {
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: invalid player action.\n");
|
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: invalid player action.\n");
|
||||||
player_action = ACTION_NONE;
|
player_action = ACTION_NONE;
|
||||||
return nullptr; // unable to move and nothing to attack == abort turn
|
return nullptr; // unable to move and nothing to attack == abort turn
|
||||||
}
|
}
|
||||||
for (auto ent : acts) {
|
if (act->is_alive() && act->get_actor_faction() != actor->get_actor_faction()) {
|
||||||
auto act = (Actor*)ent;
|
actor->attack(act);
|
||||||
if (act->is_alive() && act->get_actor_faction() != actor->get_actor_faction()) {
|
|
||||||
actor->attack(act);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
player_action = ACTION_NONE;
|
||||||
|
|
||||||
|
@ -143,8 +152,8 @@ Gamestate *PlayState::update(double delta) {
|
||||||
if (is_player_turn) {
|
if (is_player_turn) {
|
||||||
camera_pos = next->get_position();
|
camera_pos = next->get_position();
|
||||||
player_actor = next;
|
player_actor = next;
|
||||||
|
fov.calc(player_actor->get_position(), 6);
|
||||||
}
|
}
|
||||||
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn finished.\n");
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -244,7 +253,8 @@ void PlayState::draw(double delta) {
|
||||||
|
|
||||||
int sprite = var->get_sprite_id();
|
int sprite = var->get_sprite_id();
|
||||||
Color fg = var->get_sprite_color();
|
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) {
|
if (player_actor != nullptr) {
|
||||||
|
|
|
@ -14,14 +14,14 @@ BehaviourTreeStatus RangedAttackNode::tick(BTTick * tick) {
|
||||||
bool ishero = tick->target->is_type_of(ACT_HERO);
|
bool ishero = tick->target->is_type_of(ACT_HERO);
|
||||||
vec2i targetpos = tick->target->get_position();
|
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;
|
std::vector<Actor*> enemies;
|
||||||
for (Actor* actor : actors) {
|
for (Actor* actor : actors) {
|
||||||
if (actor == tick->target) continue;
|
if (actor == tick->target) continue;
|
||||||
|
|
||||||
if (actor->is_type_of(ACT_HERO) != ishero) {
|
if (actor->is_type_of(ACT_HERO) != ishero) {
|
||||||
vec2i pos = actor->get_position();
|
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);
|
enemies.push_back(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
BehaviourTree* shamtree = nullptr;
|
BehaviourTree* shamtree = nullptr;
|
||||||
|
|
||||||
Shaman::Shaman(Tilemap* map, vec2i pos) : Actor(map, pos) {
|
Shaman::Shaman(vec2i pos) : Actor(pos) {
|
||||||
name = "Shaman";
|
name = "Shaman";
|
||||||
alive = true;
|
alive = true;
|
||||||
health = 2;
|
health = 2;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class Shaman :
|
class Shaman :
|
||||||
public Actor {
|
public Actor {
|
||||||
public:
|
public:
|
||||||
Shaman(Tilemap* map, vec2i pos);
|
Shaman(vec2i pos);
|
||||||
~Shaman();
|
~Shaman();
|
||||||
bool is_type_of(Actors actor) override { return actor == ACT_SHAMAN || Actor::is_type_of(actor); };
|
bool is_type_of(Actors actor) override { return actor == ACT_SHAMAN || Actor::is_type_of(actor); };
|
||||||
Actors type() override { return ACT_SHAMAN; }
|
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;
|
return y * width + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tilemap::Tilemap(int width, int height) {
|
Tilemap::Tilemap(TileSet tileset, int width, int height) {
|
||||||
this->width = width;
|
this->width = width;
|
||||||
this->height = height;
|
this->height = height;
|
||||||
tilemap = std::vector<unsigned int>(width*height, 0);
|
tilemap = std::vector<std::string>(width*height, "");
|
||||||
|
tiles = tileset;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tilemap::~Tilemap() {}
|
Tilemap::~Tilemap() {}
|
||||||
|
@ -46,22 +47,26 @@ std::vector<vec2i> Tilemap::get_neighbours(int x, int y, int up, int down, int l
|
||||||
return neigh;
|
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)) {
|
if (is_inside_bounds(x, y)) {
|
||||||
tilemap[get_index(x, y)] = tile;
|
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)) {
|
if (is_inside_bounds(x, y)) {
|
||||||
return tilemap[get_index(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) {
|
bool Tilemap::is_blocked(int x, int y) {
|
||||||
if (is_inside_bounds(x, 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;
|
return true;
|
||||||
}
|
}
|
||||||
for (Entity* var : actors) {
|
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 };
|
vec2i pos = { x,y };
|
||||||
for (Entity* ent : actors) {
|
for (Actor* ent : actors) {
|
||||||
if (ent->entity_type() == type) {
|
vec2i apos = ent->get_position();
|
||||||
vec2i apos = ent->get_position();
|
if (apos == pos) {
|
||||||
if (apos == pos) {
|
return ent;
|
||||||
return ent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
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) {
|
void Tilemap::draw(Renderer *renderer, SpriteAtlas* sprites, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view) {
|
||||||
int w = tileset->get_tile_width();
|
int w = sprites->get_tile_width();
|
||||||
int h = tileset->get_tile_height();
|
int h = sprites->get_tile_height();
|
||||||
for (int ix = 0; ix < tw; ix++) {
|
for (int ix = 0; ix < tw; ix++) {
|
||||||
for (int iy = 0; iy < th; iy++) {
|
for (int iy = 0; iy < th; iy++) {
|
||||||
int ax = tx + ix;
|
int ax = tx + ix;
|
||||||
int ay = ty + iy;
|
int ay = ty + iy;
|
||||||
if (is_inside_bounds(ax, ay)) {
|
if (is_inside_bounds(ax, ay)) {
|
||||||
if (view == nullptr || view->has_seen({ ax, ay })) {
|
if (view == nullptr || view->has_seen({ ax, ay })) {
|
||||||
Color fg = Color(1, 1, 1, 1);
|
Tile t = get_tile(ax, ay);
|
||||||
Color bg = Color(0, 0, 0, 1);
|
Color fg = t.fg;
|
||||||
|
Color bg = t.bg;
|
||||||
if (view != nullptr && !view->can_see({ ax, ay })) {
|
if (view != nullptr && !view->can_see({ ax, ay })) {
|
||||||
fg.a = 0.4f;
|
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 <vector>
|
||||||
#include "SpriteAtlas.h"
|
#include "SpriteAtlas.h"
|
||||||
#include "Actor.h"
|
#include "Actor.h"
|
||||||
|
#include "TileSet.h"
|
||||||
|
|
||||||
struct vec2i;
|
struct vec2i;
|
||||||
class Renderer;
|
class Renderer;
|
||||||
class FieldOfView;
|
class FieldOfView;
|
||||||
|
|
||||||
class Tilemap {
|
class Tilemap {
|
||||||
std::vector<unsigned int> tilemap;
|
std::vector<std::string> tilemap;
|
||||||
std::vector<Actor*> actors;
|
std::vector<Actor*> actors;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
public:
|
public:
|
||||||
Tilemap(int width = 1, int height = 1);
|
Tilemap(TileSet tileset = TileSet(), int width = 1, int height = 1);
|
||||||
|
TileSet tiles;
|
||||||
~Tilemap();
|
~Tilemap();
|
||||||
int get_width();
|
int get_width();
|
||||||
int get_height();
|
int get_height();
|
||||||
|
@ -22,8 +24,9 @@ public:
|
||||||
bool is_inside_bounds(int x, int y);
|
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 range = 1);
|
||||||
std::vector<vec2i> get_neighbours(int x, int y, int up, int down, int left, int right);
|
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.
|
void set_tile(int x, int y, std::string tile); // "Tile" is inteded for tile ids, but can be anything really.
|
||||||
int get_tile(int x, int y);
|
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.
|
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);
|
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();
|
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_actors(int x, int y, int range);
|
||||||
std::vector<Actor*>* get_actor_list();
|
std::vector<Actor*>* get_actor_list();
|
||||||
void delete_actors();
|
void delete_actors();
|
||||||
|
|
|
@ -11,7 +11,7 @@ WanderNode::~WanderNode() = default;
|
||||||
|
|
||||||
BehaviourTreeStatus WanderNode::tick(BTTick * tick) {
|
BehaviourTreeStatus WanderNode::tick(BTTick * tick) {
|
||||||
vec2i pos = tick->target->get_position();
|
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) {
|
while (true) {
|
||||||
if (neighbours.empty()) {
|
if (neighbours.empty()) {
|
||||||
previous.clear();
|
previous.clear();
|
||||||
|
@ -26,7 +26,7 @@ BehaviourTreeStatus WanderNode::tick(BTTick * tick) {
|
||||||
break;
|
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]);
|
previous.push_back(neighbours[i]);
|
||||||
if (previous.size() > 5) {
|
if (previous.size() > 5) {
|
||||||
previous.erase(previous.begin());
|
previous.erase(previous.begin());
|
||||||
|
|
|
@ -28,7 +28,7 @@ Tilemap* World::GetMap(unsigned int level) {
|
||||||
return maps[level];
|
return maps[level];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Tilemap* map = new Tilemap(64, 64);
|
Tilemap* map = new Tilemap(TileSet(),64, 64);
|
||||||
// TODO: generate map
|
// TODO: generate map
|
||||||
maps[level] = map;
|
maps[level] = map;
|
||||||
return map;
|
return map;
|
||||||
|
@ -45,4 +45,4 @@ World::~World() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maps.clear();
|
maps.clear();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue