Bunch of stuff, preparing for items

This commit is contained in:
Adrian Hedqvist 2017-09-26 15:49:11 +02:00
parent 97189fc442
commit d32d448f31
26 changed files with 425 additions and 241 deletions

View file

@ -2,28 +2,16 @@
#include "Tilemap.h" #include "Tilemap.h"
#include "BehaviourTree.h" #include "BehaviourTree.h"
int idcounter = 0; int id_counter = 0;
Actor::Actor(Tilemap * map, vec2i pos) { Actor::Actor(Tilemap *map, vec2i pos) : Entity(map, pos) {
id = idcounter++; id = id_counter++;
name = "Actor"; name = "Actor";
this->map = map; range = 1.5f;
position = pos; collision = true;
bt = nullptr; bt = nullptr;
} team = TEAM_NONE;
sprite_id = 1;
const vec2i Actor::get_position() {
return position;
}
bool Actor::Move(int dx, int dy) {
vec2i newpos = position + vec2i(dx, dy); //GoTo({0,0}, {dx,dy});
//dir = ParseDir(dx, dy);
if (!map->IsBlocked(newpos.x, newpos.y)) {
position = newpos;
return true;
}
return false;
} }
void Actor::update() { void Actor::update() {
@ -32,16 +20,52 @@ void Actor::update() {
if (bt != nullptr) { if (bt != nullptr) {
bt->tick(this); bt->tick(this);
} }
if (health < maxhealth) { if (health < health_max) {
if (healcounter <= 0) { if (healcounter <= 0) {
health++; health++;
healcounter = 4; healcounter = 4;
} }
healcounter--; healcounter--;
} }
if (health <= 0) { if (health <= 0) {
Kill(); kill();
}
}
void Actor::damage(int strength) {
health -= strength;
if (health <= 0) {
kill();
}
}
void Actor::attack(Actor *act) {
if (act) {
vec2i dpos = get_position() - act->get_position();
if (dpos.dist() <= range) {
act->damage(strength);
}
}
}
void Actor::attack(vec2i dpos) {
if (dpos.dist() <= range) {
vec2i pos = get_position();
auto acts = get_map()->get_entities(pos.x + dpos.x, pos.y + dpos.y, 0, ENTITY_ACTOR);
for (Entity* ent : acts) {
auto act = (Actor*)ent;
if (act->is_alive() && act->get_actor_team() != team) {
act->damage(strength);
break;
}
}
}
}
void Actor::heal(int amount) {
health += amount;
if (health > health_max) {
health = health_max;
} }
} }

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "vec2i.h" #include "vec2i.h"
#include "Entity.h"
#include <string> #include <string>
class BehaviourTree; class BehaviourTree;
@ -11,32 +12,44 @@ enum Actors {
ACT_SHAMAN ACT_SHAMAN
}; };
enum ActorTeams {
TEAM_NONE,
TEAM_PLAYER,
TEAM_GOBS
};
class Tilemap; class Tilemap;
class Actor { class Actor : public Entity {
vec2i position;
int healcounter; int healcounter;
protected: protected:
BehaviourTree* bt; BehaviourTree* bt;
int health;
int health_max;
int strength;
float range;
bool alive;
ActorTeams team;
public: public:
int id; int id;
std::string name; std::string name;
Tilemap* map;
bool alive;
int health;
int maxhealth;
int strength;
Actor(Tilemap *map, vec2i pos);
Actor(Tilemap* map, vec2i pos); bool is_alive(){ return alive; };
const vec2i get_position(); void attack(vec2i dpos); // basic melee attack
bool IsAlive(){ return alive; }; void attack(Actor* act);
bool Move(int dx, int dy); void heal(int amount);
int GetHealth() { return health; } void damage(int strength);
void Kill() { alive = false; health = 0; }; int get_strength() { return strength; }
int get_health() { return health; }
int get_health_max() { return health_max; }
ActorTeams get_actor_team() { return team; }
float get_range() { return range; }
void kill() { alive = false; health = 0; collision = false; };
void update(); void update();
virtual bool isTypeOf(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; };
~Actor(); ~Actor();
}; };

View file

@ -7,31 +7,34 @@
AttackEnemyNode::AttackEnemyNode(BehaviourTreeNode * parent) : BehaviourTreeNode(parent){} AttackEnemyNode::AttackEnemyNode(BehaviourTreeNode * parent) : BehaviourTreeNode(parent){}
AttackEnemyNode::~AttackEnemyNode() {} AttackEnemyNode::~AttackEnemyNode() = default;
BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) { BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
bool ishero = tick->target->isTypeOf(ACT_HERO); bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
auto actors = tick->target->map->get_actor_list(); auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
std::vector<Actor*> visibleEnemies; std::vector<Actor*> visibleEnemies;
for (auto actor : *actors) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor == tick->target) continue; if (actor == tick->target) continue;
if (actor->isTypeOf(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->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
visibleEnemies.push_back(actor); visibleEnemies.push_back(actor);
} }
} }
} }
if (visibleEnemies.size() == 0) { if (visibleEnemies.empty()) {
return BT_FAILED; return BT_FAILED;
} }
Actor* closestActor = nullptr; Actor* closestActor = nullptr;
float closestDist; float closestDist = tick->target->get_range();
for (Actor* actor : visibleEnemies) { for (Actor* actor : visibleEnemies) {
float dist = Pathfinder::distance(tick->target->get_position(), actor->get_position()); float dist = Pathfinder::distance(tick->target->get_position(), actor->get_position());
if (closestActor == nullptr || if (closestActor == nullptr ||
@ -41,10 +44,9 @@ BehaviourTreeStatus AttackEnemyNode::tick(BTTick * tick) {
} }
} }
if (closestDist < 1.5f) { if (closestDist < tick->target->get_range()) {
closestActor->health -= tick->target->strength; tick->target->attack(closestActor);
if (closestActor->health <= 0) { if (!closestActor->is_alive()) {
closestActor->Kill();
return BT_SUCCEEDED; return BT_SUCCEEDED;
} }
return BT_RUNNING; return BT_RUNNING;
@ -52,11 +54,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->map, pos, goal); auto path = Pathfinder::aStar(tick->target->get_map(), pos, goal);
if (path.size() > 0) { 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)) {
return BT_RUNNING; return BT_RUNNING;
} }
} }

43
src/Entity.cpp Normal file
View file

@ -0,0 +1,43 @@
//
// Created by Adrian on 2017-09-25.
//
#include "Entity.h"
#include "Tilemap.h"
Entity::Entity(Tilemap *map, vec2i pos) {
this->map = map;
position = pos;
collision = false;
sprite_id = '?';
}
vec2i Entity::get_position() {
return position;
}
bool Entity::move(vec2i dpos) {
return move(dpos.x, dpos.y);
}
bool Entity::move(int dx, int dy) {
vec2i newpos = position + vec2i(dx, dy); //GoTo({0,0}, {dx,dy});
//dir = ParseDir(dx, dy);
if (!collision || !map->IsBlocked(newpos.x, newpos.y)) {
position = newpos;
return true;
}
return false;
}
void Entity::set_position(vec2i pos) {
position = pos;
}
Tilemap *Entity::get_map() {
return map;
}
bool Entity::has_collision() {
return collision;
}

39
src/Entity.h Normal file
View file

@ -0,0 +1,39 @@
//
// Created by Adrian on 2017-09-25.
//
#ifndef DUNGEON_ENTITY_H
#define DUNGEON_ENTITY_H
#include "vec2i.h"
class Tilemap;
enum EntityTypes {
ENTITY_BASE, // All entities are objects that can be placed on the map and can be interacted with
ENTITY_ACTOR, // Actors are characters with AI or controlled by the player
ENTITY_ITEM, // Items can be picked up
};
class Entity {
vec2i position;
Tilemap* map;
protected:
unsigned int sprite_id;
bool collision;
public:
Entity(Tilemap* map, 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);
void set_position(vec2i pos);
unsigned int get_sprite_id() { return sprite_id; };
virtual EntityTypes entity_type() { return ENTITY_BASE; };
};
#endif //DUNGEON_ENTITY_H

View file

@ -49,7 +49,7 @@ BehaviourTreeStatus ExploreNode::tick(BTTick * tick) {
int i = std::rand() % options.size(); int i = std::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)) {
//printf("EXPLORE %f\n", lowestval); //printf("EXPLORE %f\n", lowestval);
return BT_RUNNING; return BT_RUNNING;
} }

View file

@ -10,23 +10,25 @@
FleeNode::FleeNode(BehaviourTreeNode* parent) : BehaviourTreeNode(parent) {} FleeNode::FleeNode(BehaviourTreeNode* parent) : BehaviourTreeNode(parent) {}
FleeNode::~FleeNode() {} FleeNode::~FleeNode() = default;
BehaviourTreeStatus FleeNode::tick(BTTick * tick) { BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
Pathfinder::DijkstraMap dijkstra; Pathfinder::DijkstraMap dijkstra;
Tilemap * map = tick->target->map; Tilemap * map = tick->target->get_map();
std::vector<vec2i> enemyPos; std::vector<vec2i> enemyPos;
bool ishero = tick->target->isTypeOf(ACT_HERO); bool ishero = tick->target->is_type_of(ACT_HERO);
auto actors = tick->target->map->get_actor_list(); vec2i targetpos = tick->target->get_position();
for (Actor* actor : *actors) { auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
if (actor->isTypeOf(ACT_HERO) != ishero) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor->is_type_of(ACT_HERO) != ishero) {
vec2i pos = actor->get_position(); vec2i pos = actor->get_position();
if (line_of_sight(tick->target->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
enemyPos.push_back(pos); enemyPos.push_back(pos);
} }
} }
} }
if (enemyPos.size() <= 0) { if (enemyPos.empty()) {
return BT_FAILED; return BT_FAILED;
} }
Pathfinder::calcDijkstraMap(map, &enemyPos, &dijkstra, 16); Pathfinder::calcDijkstraMap(map, &enemyPos, &dijkstra, 16);
@ -38,7 +40,7 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
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.getValue(x,y) <= 0 && dijkstra.getValue(x, y) >= -10) {
safety.push_back(vec2i(x, y)); safety.emplace_back(x, y);
} }
} }
} }
@ -65,16 +67,16 @@ BehaviourTreeStatus FleeNode::tick(BTTick * tick) {
//printf("FLEEING SUCCESS\n"); //printf("FLEEING SUCCESS\n");
return BT_FAILED; return BT_FAILED;
} }
while (options.size() > 0) { while (!options.empty()) {
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)) {
//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;
} }
options.erase(options.begin() + i); options.erase(options.begin() + i);
if (options.size() == 0) { if (options.empty()) {
return BT_FAILED; return BT_FAILED;
} }
} }

View file

@ -11,8 +11,11 @@ Goblin::Goblin(Tilemap* map, vec2i pos) : Actor(map, pos) {
name = "Goblin"; name = "Goblin";
alive = true; alive = true;
health = 4; health = 4;
maxhealth = 4; health_max = 4;
strength = 1; strength = 1;
sprite_id = 'g';
team = TEAM_GOBS;
if (gobtree == nullptr) { if (gobtree == nullptr) {
auto * root = new BehaviourTreeSelector(nullptr); auto * root = new BehaviourTreeSelector(nullptr);
gobtree = new BehaviourTree(root); gobtree = new BehaviourTree(root);

View file

@ -6,7 +6,7 @@ class Goblin :
public: public:
Goblin(Tilemap* map, vec2i pos); Goblin(Tilemap* map, vec2i pos);
~Goblin(); ~Goblin();
bool isTypeOf(Actors actor) { return actor == ACT_GOBLIN || Actor::isTypeOf(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; }
}; };

View file

@ -7,37 +7,39 @@
HealFriendNode::HealFriendNode(BehaviourTreeNode * parent) : BehaviourTreeNode(parent){} HealFriendNode::HealFriendNode(BehaviourTreeNode * parent) : BehaviourTreeNode(parent){}
HealFriendNode::~HealFriendNode() {} HealFriendNode::~HealFriendNode() = default;
BehaviourTreeStatus HealFriendNode::tick(BTTick * tick) { BehaviourTreeStatus HealFriendNode::tick(BTTick * tick) {
bool ishero = tick->target->isTypeOf(ACT_HERO); bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
auto actors = tick->target->map->get_actor_list(); auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
std::vector<Actor*> friends; std::vector<Actor*> friends;
for (auto actor : *actors) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor == tick->target) continue; if (actor == tick->target) continue;
if (actor->isTypeOf(ACT_HERO) == ishero && actor->health < actor->maxhealth-1) { 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->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
friends.push_back(actor); friends.push_back(actor);
} }
} }
} }
if (friends.size() == 0) { if (friends.empty()) {
return BT_FAILED; return BT_FAILED;
} }
Actor* lowestHpActor = nullptr; Actor* lowestHpActor = nullptr;
int lowestHp; int lowestHp;
for (Actor* actor : friends) { for (Actor* actor : friends) {
if (lowestHpActor == nullptr || actor->health < lowestHp) { if (lowestHpActor == nullptr || actor->get_health() < lowestHp) {
lowestHpActor = actor; lowestHpActor = actor;
lowestHp = actor->health; lowestHp = actor->get_health();
} }
} }
lowestHpActor->health += 1; lowestHpActor->heal(1);
return BT_SUCCEEDED; return BT_SUCCEEDED;
} }

View file

@ -12,8 +12,10 @@ Hero::Hero(Tilemap* map, vec2i pos) : Actor(map, pos) {
name = "Hero"; name = "Hero";
alive = true; alive = true;
health = 6; health = 6;
maxhealth = 6; health_max = 6;
strength = 2; strength = 2;
sprite_id = '@';
team = TEAM_PLAYER;
/* /*
BehaviourTreeSelector* root = new BehaviourTreeSelector(nullptr); BehaviourTreeSelector* root = new BehaviourTreeSelector(nullptr);
bt = new BehaviourTree(root); bt = new BehaviourTree(root);

View file

@ -5,7 +5,7 @@ class Hero : public Actor {
public: public:
Hero(Tilemap* map, vec2i pos); Hero(Tilemap* map, vec2i pos);
~Hero(); ~Hero();
bool isTypeOf(Actors actor) { return actor == ACT_HERO || Actor::isTypeOf(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; }
}; };

View file

@ -6,11 +6,11 @@ IfHealthBelow::IfHealthBelow(BehaviourTreeNode * root, int healthBelow) : Behavi
this->healthBelow = healthBelow; this->healthBelow = healthBelow;
} }
IfHealthBelow::~IfHealthBelow() {} IfHealthBelow::~IfHealthBelow() = default;
BehaviourTreeStatus IfHealthBelow::tick(BTTick * tick) { BehaviourTreeStatus IfHealthBelow::tick(BTTick * tick) {
if (children.size() == 0) return BT_ERROR; if (children.empty()) return BT_ERROR;
if (tick->target->health < healthBelow) { if (tick->target->get_health() < healthBelow) {
return children[0]->execute(tick); return children[0]->execute(tick);
} }
return BT_FAILED; return BT_FAILED;

View file

@ -13,15 +13,17 @@ BehaviourTreeStatus IfNotSeeEnemyNode::tick(BTTick * tick) {
if (children.size() <= 0) { if (children.size() <= 0) {
return BT_ERROR; return BT_ERROR;
} }
bool ishero = tick->target->isTypeOf(ACT_HERO); bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
auto actors = tick->target->map->get_actor_list(); auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
for (auto actor : *actors) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor == tick->target) continue; if (actor == tick->target) continue;
if (actor->isTypeOf(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->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
return BT_FAILED; return BT_FAILED;
} }
} }

View file

@ -13,15 +13,17 @@ BehaviourTreeStatus IfSeeEnemyNode::tick(BTTick * tick) {
if (children.size() <= 0) { if (children.size() <= 0) {
return BT_ERROR; return BT_ERROR;
} }
bool ishero = tick->target->isTypeOf(ACT_HERO); bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
auto actors = tick->target->map->get_actor_list(); auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
for (auto actor : *actors) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor == tick->target) continue; if (actor == tick->target) continue;
if (actor->isTypeOf(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->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
return children[0]->execute(tick); return children[0]->execute(tick);
} }
} }

View file

@ -14,15 +14,17 @@ BehaviourTreeStatus IfSeeFriendNode::tick(BTTick * tick) {
if (children.size() <= 0) { if (children.size() <= 0) {
return BT_ERROR; return BT_ERROR;
} }
bool ishero = tick->target->isTypeOf(ACT_HERO); bool ishero = tick->target->is_type_of(ACT_HERO);
vec2i targetpos = tick->target->get_position();
auto actors = tick->target->map->get_actor_list(); auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
for (auto actor : *actors) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor == tick->target) continue; if (actor == tick->target) continue;
if (actor->isTypeOf(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->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
return children[0]->execute(tick); return children[0]->execute(tick);
} }
} }

View file

@ -7,8 +7,8 @@
#include "Renderer.h" #include "Renderer.h"
#include "Actor.h" #include "Actor.h"
#include "App.h" #include "App.h"
#include "Tilemap.h"
#include "Tileset.h" #include "Tileset.h"
#include "Tilemap.h"
#include "FieldOfView.h" #include "FieldOfView.h"
#include "imgui.h" #include "imgui.h"
#include "Hero.h" #include "Hero.h"
@ -16,40 +16,6 @@
#include "Shaman.h" #include "Shaman.h"
const int mapwidth = 32; const int mapwidth = 32;
const std::string map =
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# @ . . . # # # # # # # # . . . . . . . . . . . . . . . . . # #"
"# . . . . . . . . # # # # . # # . # # # # # # . # # # # # . # #"
"# . . . . # # # . . . . . . # . g . # # # # # . # # # . . g . #"
"# . . . . # # # # # # # # . # . . . # # . . . . . . . . . . . #"
"# # # . # # # # # # # # # . . . . g # # . # # # . # # . . g . #"
"# . . . . . . . . . . . . . # # # # # . . . # # . # # # # # # #"
"# . # # # # # # # # . # # . # # # # # . g . # # . # . . g . . #"
"# . . . . g # # . . . # . . . # # # # . . . # # . # . . . . . #"
"# . . g . . # # . # # # . s . . . # # # # # # # . . . . s . . #"
"# . . . . . # # . . . # . . . # . . . . . . . . . # . g . . . #"
"# # . # # # # # . # . # # # # # # # # # . # # # # # # # # . # #"
"# . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . #"
"# . # # # # # # # # # # . # . # # # # # # # # # # . # # # # . #"
"# . . . . . . . . . . . . # . # . . . . # . . . . . # # # . . #"
"# # # # # # . # # # . # # # . # . . . . # . . . # . # # # . # #"
"# . . . . # . # . . . . . # . . . . . . . . . . # . # # . . . #"
"# . . . . # . # . . . . . # . # . . . . # # # # # . . . . . . #"
"# . . . . . . # . . . . . # . # # # # # # . . . . . # # . . . #"
"# . . . . # . # # # # # # # . . . . . . . . # # # # # # # # # #"
"# . . . . # . . . . . . . . . # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #";
InputAction action; InputAction action;
@ -68,6 +34,10 @@ void PlayState::load() {
app->input->bind_key(SDLK_KP_1, ACTION_MOVE_SOUTHWEST); app->input->bind_key(SDLK_KP_1, ACTION_MOVE_SOUTHWEST);
app->input->bind_key(SDLK_KP_3, ACTION_MOVE_SOUTHEAST); app->input->bind_key(SDLK_KP_3, ACTION_MOVE_SOUTHEAST);
app->input->bind_key(SDLK_KP_5, ACTION_WAIT); app->input->bind_key(SDLK_KP_5, ACTION_WAIT);
app->input->bind_key(SDLK_UP, ACTION_MOVE_NORTH);
app->input->bind_key(SDLK_DOWN, ACTION_MOVE_SOUTH);
app->input->bind_key(SDLK_LEFT, ACTION_MOVE_WEST);
app->input->bind_key(SDLK_RIGHT, ACTION_MOVE_EAST);
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);
@ -95,6 +65,41 @@ void PlayState::new_game() {
fov = nullptr; fov = nullptr;
} }
std::string map =
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# @ . . . # # # # # # # # . . . . . . . . . . . . . . . . . # #"
"# . . . . . . . . # # # # . # # . # # # # # # . # # # # # . # #"
"# . . . . # # # . . . . . . # . g . # # # # # . # # # . . g . #"
"# . . . . # # # # # # # # . # . . . # # . . . . . . . . . . . #"
"# # # . # # # # # # # # # . . . . g # # . # # # . # # . . g . #"
"# . . . . . . . . . . . . . # # # # # . . . # # . # # # # # # #"
"# . # # # # # # # # . # # . # # # # # . g . # # . # . . g . . #"
"# . . . . g # # . . . # . . . # # # # . . . # # . # . . . . . #"
"# . . g . . # # . # # # . s . . . # # # # # # # . . . . s . . #"
"# . . . . . # # . . . # . . . # . . . . . . . . . # . g . . . #"
"# # . # # # # # . # . # # # # # # # # # . # # # # # # # # . # #"
"# . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . #"
"# . # # # # # # # # # # . # . # # # # # # # # # # . # # # # . #"
"# . . . . . . . . . . . . # . # . . . . # . . . . . # # # . . #"
"# # # # # # . # # # . # # # . # . . . . # . . . # . # # # . # #"
"# . . . . # . # . . . . . # . . . . . . . . . . # . # # . . . #"
"# . . . . # . # . . . . . # . # . . . . # # # # # . . . . . . #"
"# . . . . . . # . . . . . # . # # # # # # . . . . . # # . . . #"
"# . . . . # . # # # # # # # . . . . . . . . # # # # # # # # # #"
"# . . . . # . . . . . . . . . # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #";
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n"); SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n");
tilemap = new Tilemap(32, 32); tilemap = new Tilemap(32, 32);
int y = 0; int y = 0;
@ -114,15 +119,15 @@ void PlayState::new_game() {
if (i == '@') { if (i == '@') {
hero = new Hero(tilemap, vec2i(x, y)); hero = new Hero(tilemap, vec2i(x, y));
tilemap->add_actor(hero); tilemap->add_entity(hero);
tilemap->set_tile(x, y, '.'); tilemap->set_tile(x, y, '.');
} }
else if (i == 'g') { else if (i == 'g') {
tilemap->add_actor(new Goblin(tilemap, vec2i(x, y))); tilemap->add_entity(new Goblin(tilemap, vec2i(x, y)));
tilemap->set_tile(x, y, '.'); tilemap->set_tile(x, y, '.');
} }
else if (i == 's') { else if (i == 's') {
tilemap->add_actor(new Shaman(tilemap, vec2i(x, y))); tilemap->add_entity(new Shaman(tilemap, vec2i(x, y)));
tilemap->set_tile(x, y, '.'); tilemap->set_tile(x, y, '.');
} }
else { else {
@ -139,7 +144,7 @@ void PlayState::new_game() {
Gamestate *PlayState::update(double delta) { Gamestate *PlayState::update(double delta) {
if (action != ACTION_NONE) { if (action != ACTION_NONE) {
if (hero) { if (hero && hero->is_alive()) {
vec2i dir; vec2i dir;
switch (action) { switch (action) {
case ACTION_MOVE_NORTH: dir = {0, -1}; break; case ACTION_MOVE_NORTH: dir = {0, -1}; break;
@ -154,17 +159,18 @@ Gamestate *PlayState::update(double delta) {
default: action = ACTION_NONE; return nullptr; // abort turn default: action = ACTION_NONE; return nullptr; // abort turn
} }
if (dir != vec2i(0,0)) { if (dir != vec2i(0,0)) {
if (!hero->Move(dir.x, dir.y)) { if (!hero->move(dir.x, dir.y)) {
vec2i heropos = hero->get_position(); vec2i heropos = hero->get_position();
Actor* act = tilemap->GetActor(heropos.x + dir.x, heropos.y + dir.y, ACT_BASE); auto acts = tilemap->get_entities(heropos.x + dir.x, heropos.y + dir.y, 0, ENTITY_ACTOR);
if (act) { if(acts.empty()) {
act->health -= hero->strength; return nullptr; // unable to move and nothing to attack == abort turn
if (act->health <= 0) {
act->Kill();
}
} }
else { for (auto ent : acts) {
return nullptr; // abort turn auto act = (Actor*)ent;
if (act->is_alive() && act->get_actor_team() != hero->get_actor_team()) {
hero->attack(act);
break;
}
} }
} }
} }
@ -172,14 +178,17 @@ Gamestate *PlayState::update(double delta) {
fov->calc(hero->get_position(), 6); fov->calc(hero->get_position(), 6);
} }
auto actors = tilemap->get_actor_list(); auto actors = tilemap->get_entity_list();
for (Actor* var : *actors) { for (Entity* var : *actors) {
if (var == hero) continue; if (var == hero) continue;
var->update(); if (var->entity_type() == ENTITY_ACTOR) {
((Actor*)var)->update();
}
} }
/* // We got enough memory, we can leave the corpses on the field.
unsigned int actor_size = actors->size(); unsigned int actor_size = actors->size();
for (unsigned int i = actor_size - 1; i <= actor_size; i--) { // Woo unsigned int underflow abuse! for (unsigned int i = actor_size - 1; i <= actor_size; i--) { // Woo unsigned int underflow abuse!
if (!actors->at(i)->alive) { if (!actors->at(i)->is_alive()) {
if (actors->at(i) == hero) { if (actors->at(i) == hero) {
hero = nullptr; hero = nullptr;
} }
@ -187,6 +196,7 @@ Gamestate *PlayState::update(double delta) {
actors->erase(actors->begin() + i); actors->erase(actors->begin() + i);
} }
} }
*/
action = ACTION_NONE; action = ACTION_NONE;
} }
return nullptr; return nullptr;
@ -223,17 +233,20 @@ void PlayState::draw(double delta) {
if (debug_actors) { if (debug_actors) {
ImGui::Begin("Actors", &debug_actors); ImGui::Begin("Actors", &debug_actors);
auto actors = tilemap->get_actor_list(); auto actors = tilemap->get_entity_list();
const char* headers[] { const char* headers[] {
"id", "name", "health", "strength" "id", "name", "health", "strength"
}; };
static float widths[4]{}; static float widths[4]{};
ImGui::BeginTable("ActorColumns", headers, widths, 4); ImGui::BeginTable("ActorColumns", headers, widths, 4);
for (Actor* act : *actors) { for (Entity* ent : *actors) {
ImGui::Text("%d", act->id); ImGui::NextColumn(); if (ent->entity_type() == ENTITY_ACTOR) {
ImGui::Text(act->name.c_str()); ImGui::NextColumn(); auto act = (Actor*) ent;
ImGui::Text("%d/%d", act->health, act->maxhealth); ImGui::NextColumn(); ImGui::Text("%d", act->id); ImGui::NextColumn();
ImGui::Text("%d", act->strength); ImGui::NextColumn(); ImGui::Text(act->name.c_str()); ImGui::NextColumn();
ImGui::Text("%d/%d", act->get_health(), act->get_health_max()); ImGui::NextColumn();
ImGui::Text("%d", act->get_strength()); ImGui::NextColumn();
}
} }
ImGui::EndTable(); ImGui::EndTable();
@ -264,38 +277,22 @@ void PlayState::draw(double delta) {
tilemap->draw(app->renderer, ascii, margin.x, margin.y, -offset.x, -offset.y, tilesize.x, tilesize.y, fov); tilemap->draw(app->renderer, ascii, margin.x, margin.y, -offset.x, -offset.y, tilesize.x, tilesize.y, fov);
auto actors = tilemap->get_actor_list(); auto entities = tilemap->get_entity_list();
for (Actor* var : *actors) { for (Entity* var : *entities) {
vec2i pos = var->get_position(); vec2i pos = var->get_position();
if (fov == nullptr || fov->can_see(pos)) { if (fov == nullptr || fov->can_see(pos)) {
app->renderer->set_color(0, 0, 0, 255); app->renderer->set_color(0, 0, 0, 255);
app->renderer->draw_sprite(ascii->get_sprite(219), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y); app->renderer->draw_sprite(ascii->get_sprite(219), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
int sprite; int sprite = var->get_sprite_id();
switch (var->Type()) { app->renderer->set_color(1, 1, 1, 1);
case ACT_HERO:
app->renderer->set_color(.2f, .6f, 1, 1);
sprite = '@';
break;
case ACT_GOBLIN:
app->renderer->set_color(.6f, 1, .2f, 1);
sprite = 'g';
break;
case ACT_SHAMAN:
app->renderer->set_color(.2f, 1, .6f, 1);
sprite = 's';
break;
default:
app->renderer->set_color(1, 1, 1, 1);
sprite = 2;
break;
}
app->renderer->draw_sprite(ascii->get_sprite(sprite), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y); app->renderer->draw_sprite(ascii->get_sprite(sprite), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
} }
} }
if (hero != nullptr) { if (hero != nullptr) {
app->renderer->set_color(155, 5, 5, 255); app->renderer->set_color(155, 5, 5, 255);
for (int i = 0; i < hero->health; i++) { for (int i = 0; i < hero->get_health(); i++) {
app->renderer->set_color(0, 0, 0, 255); app->renderer->set_color(0, 0, 0, 255);
app->renderer->draw_sprite(ascii->get_sprite(219), (i+1) * asciisize.x, asciisize.y); app->renderer->draw_sprite(ascii->get_sprite(219), (i+1) * asciisize.x, asciisize.y);
app->renderer->set_color(255, 0, 0, 255); app->renderer->set_color(255, 0, 0, 255);
@ -305,7 +302,21 @@ void PlayState::draw(double delta) {
} }
void PlayState::quit() { void PlayState::quit() {
if (tilemap != nullptr) {
delete tilemap;
tilemap = nullptr;
hero = nullptr;
}
if (hero != nullptr) {
delete hero;
hero = nullptr;
}
if (fov != nullptr) {
delete fov;
fov = nullptr;
}
} }
void PlayState::inputevent(InputEvent *event) { void PlayState::inputevent(InputEvent *event) {

View file

@ -8,43 +8,39 @@
RangedAttackNode::RangedAttackNode(BehaviourTreeNode* parent) : BehaviourTreeNode(parent) {} RangedAttackNode::RangedAttackNode(BehaviourTreeNode* parent) : BehaviourTreeNode(parent) {}
RangedAttackNode::~RangedAttackNode() {} RangedAttackNode::~RangedAttackNode() = default;
BehaviourTreeStatus RangedAttackNode::tick(BTTick * tick) { BehaviourTreeStatus RangedAttackNode::tick(BTTick * tick) {
if (children.size() <= 0) { bool ishero = tick->target->is_type_of(ACT_HERO);
return BT_ERROR; vec2i targetpos = tick->target->get_position();
}
bool ishero = tick->target->isTypeOf(ACT_HERO);
auto actors = tick->target->map->get_actor_list(); auto actors = tick->target->get_map()->get_entities(targetpos.x, targetpos.y, 6, ENTITY_ACTOR);
std::vector<Actor*> enemies; std::vector<Actor*> enemies;
for (auto actor : *actors) { for (auto ent : actors) {
auto actor = (Actor*)ent;
if (actor == tick->target) continue; if (actor == tick->target) continue;
if (actor->isTypeOf(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->map, tick->target->get_position(), pos)) { if (line_of_sight(tick->target->get_map(), tick->target->get_position(), pos)) {
enemies.push_back(actor); enemies.push_back(actor);
} }
} }
} }
if (enemies.size() == 0) { if (enemies.empty()) {
return BT_FAILED; return BT_FAILED;
} }
Actor* lowestHpActor = nullptr; Actor* lowestHpActor = nullptr;
int lowestHp; int lowestHp;
for (Actor* actor : enemies) { for (Actor* actor : enemies) {
if (lowestHpActor == nullptr || actor->health < lowestHp) { if (lowestHpActor == nullptr || actor->get_health() < lowestHp) {
lowestHpActor = actor; lowestHpActor = actor;
lowestHp = actor->health; lowestHp = actor->get_health();
} }
} }
lowestHpActor->health -= tick->target->strength; tick->target->attack(lowestHpActor);
if (lowestHpActor->health <= 0) {
lowestHpActor->Kill();
}
return BT_SUCCEEDED; return BT_SUCCEEDED;
} }

View file

@ -5,10 +5,10 @@
RestNode::RestNode(BehaviourTreeNode * parent) : BehaviourTreeNode(parent) {} RestNode::RestNode(BehaviourTreeNode * parent) : BehaviourTreeNode(parent) {}
RestNode::~RestNode() {} RestNode::~RestNode() = default;
BehaviourTreeStatus RestNode::tick(BTTick * tick) { BehaviourTreeStatus RestNode::tick(BTTick * tick) {
if (tick->target->health < tick->target->maxhealth) { if (tick->target->get_health() < tick->target->get_health_max()) {
return BT_SUCCEEDED; return BT_SUCCEEDED;
} }
return BT_FAILED; return BT_FAILED;

View file

@ -5,7 +5,7 @@
#include "RestNode.h" #include "RestNode.h"
#include "IfSeeFriendNode.h" #include "IfSeeFriendNode.h"
#include "HealFriendNode.h" #include "HealFriendNode.h"
#include "RangedAttackNode.h" #include "AttackEnemyNode.h"
#include "FleeNode.h" #include "FleeNode.h"
@ -15,8 +15,11 @@ Shaman::Shaman(Tilemap* map, vec2i pos) : Actor(map, pos) {
name = "Shaman"; name = "Shaman";
alive = true; alive = true;
health = 2; health = 2;
maxhealth = 2; health_max = 2;
strength = 1; strength = 1;
range = 6;
sprite_id = 's';
team = TEAM_GOBS;
if (shamtree == nullptr) { if (shamtree == nullptr) {
auto * root = new BehaviourTreeSelector(nullptr); auto * root = new BehaviourTreeSelector(nullptr);
@ -26,7 +29,7 @@ Shaman::Shaman(Tilemap* map, vec2i pos) : Actor(map, pos) {
auto * fsel = new BehaviourTreeSelector(seefriend); auto * fsel = new BehaviourTreeSelector(seefriend);
{ {
new HealFriendNode(fsel); new HealFriendNode(fsel);
//new RangedAttackNode(fsel); new AttackEnemyNode(fsel);
} }
new FleeNode(root); new FleeNode(root);

View file

@ -5,7 +5,7 @@ class Shaman :
public: public:
Shaman(Tilemap* map, vec2i pos); Shaman(Tilemap* map, vec2i pos);
~Shaman(); ~Shaman();
bool isTypeOf(Actors actor) { return actor == ACT_SHAMAN || Actor::isTypeOf(actor); }; bool is_type_of(Actors actor) override { return actor == ACT_SHAMAN || Actor::is_type_of(actor); };
Actors Type() { return ACT_SHAMAN; } Actors type() override { return ACT_SHAMAN; }
}; };

29
src/Stats.h Normal file
View file

@ -0,0 +1,29 @@
//
// Created by Adrian on 2017-09-25.
//
#ifndef DUNGEON_STATS_H
#define DUNGEON_STATS_H
struct Stats {
int vitality;
int endurance;
int intelligence;
int strength;
int dexterity;
int perception;
};
struct SecondaryStats { // derived from the primary stats
int health;
int health_max;
int mana;
int mana_max;
int damage_melee;
int damage_magic;
int damage_ranged;
int defence;
int resistance;
};
#endif //DUNGEON_STATS_H

View file

@ -21,7 +21,7 @@ Tilemap::Tilemap(int width, int height)
Tilemap::~Tilemap() Tilemap::~Tilemap()
{ {
delete tilemap; delete tilemap;
for (auto var : actors) { for (auto var : entities) {
delete var; delete var;
} }
} }
@ -46,7 +46,7 @@ std::vector<vec2i> Tilemap::getNeighbours(int x, int y, int range)
std::vector<vec2i> neigh; std::vector<vec2i> neigh;
if (range == 0) if (range == 0)
{ {
neigh.push_back({x,y}); neigh.emplace_back(x,y);
return neigh; return neigh;
} }
for (int dx = -range; dx <= range; dx++) for (int dx = -range; dx <= range; dx++)
@ -55,14 +55,14 @@ std::vector<vec2i> Tilemap::getNeighbours(int x, int y, int range)
{ {
if ((dx != 0 || dy != 0) && IsInsideBounds(x + dx, y + dy)) if ((dx != 0 || dy != 0) && IsInsideBounds(x + dx, y + dy))
{ {
neigh.push_back({x+dx,y+dy}); neigh.emplace_back(x+dx,y+dy);
} }
} }
} }
return neigh; return neigh;
} }
void Tilemap::set_tile(int x, int y, int tile) void Tilemap::set_tile(int x, int y, unsigned int tile)
{ {
if (IsInsideBounds(x, y)) if (IsInsideBounds(x, y))
{ {
@ -86,57 +86,56 @@ bool Tilemap::IsBlocked(int x, int y)
if (tilemap[GetIndex(x,y)] == '#') { // TODO: Replace hardcoded tiles if (tilemap[GetIndex(x,y)] == '#') { // TODO: Replace hardcoded tiles
return true; return true;
} }
for (Actor* var : actors) { for (Entity* var : entities) {
vec2i pos = var->get_position(); vec2i pos = var->get_position();
if (var->IsAlive() && pos.x == x && pos.y == y) { if (var->has_collision() && pos == vec2i(x, y))
return true; return true;
}
} }
return false; return false;
} }
return true; return true;
} }
void Tilemap::add_actor(Actor *actor) { void Tilemap::add_entity(Entity *actor) {
for (Actor* var : actors) { for (Entity* var : entities) {
if (var == actor) { if (var == actor) {
return; return;
} }
} }
actors.push_back(actor); entities.push_back(actor);
} }
void Tilemap::RemoveActor(Actor * actor) { void Tilemap::remove_entity(Entity * actor) {
for (auto it = actors.begin(); it != actors.end(); it++) { for (auto it = entities.begin(); it != entities.end(); it++) {
if ((*it) == actor) { if ((*it) == actor) {
actors.erase(it); entities.erase(it);
return; return;
} }
} }
} }
Actor * Tilemap::GetActor(int x, int y, Actors type) { Entity * Tilemap::get_entity(int x, int y, EntityTypes type) {
vec2i pos = { x,y }; vec2i pos = { x,y };
for (Actor* act : actors) { for (Entity* ent : entities) {
if (act->isTypeOf(type)) { if (ent->entity_type() == type) {
vec2i apos = act->get_position(); vec2i apos = ent->get_position();
if (apos == pos) { if (apos == pos) {
return act; return ent;
} }
} }
} }
return nullptr; return nullptr;
} }
std::vector<Actor*> Tilemap::GetActors(int x, int y, int range, Actors type) { std::vector<Entity*> Tilemap::get_entities(int x, int y, int range, EntityTypes type) {
std::vector<Actor*> found; std::vector<Entity*> found;
std::vector<vec2i> neigh = getNeighbours(x, y, range); std::vector<vec2i> neigh = getNeighbours(x, y, range);
for (Actor* act : actors) { for (Entity* ent : entities) {
for (vec2i pos : neigh) { for (vec2i pos : neigh) {
if (act->isTypeOf(type)) { if (ent->entity_type() == type) {
vec2i apos = act->get_position(); vec2i apos = ent->get_position();
if (apos == pos) { if (apos == pos) {
found.push_back(act); found.emplace_back(ent);
break; break;
} }
} }
@ -145,8 +144,8 @@ std::vector<Actor*> Tilemap::GetActors(int x, int y, int range, Actors type) {
return found; return found;
} }
std::vector<Actor*>* Tilemap::get_actor_list() { std::vector<Entity*>* Tilemap::get_entity_list() {
return &actors; return &entities;
} }
void Tilemap::draw(Renderer *renderer, Tileset* tileset, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view) { void Tilemap::draw(Renderer *renderer, Tileset* tileset, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view) {

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include "Actor.h"
#include "Tileset.h" #include "Tileset.h"
#include "Entity.h"
struct vec2i; struct vec2i;
class Renderer; class Renderer;
@ -9,7 +9,7 @@ class FieldOfView;
class Tilemap { class Tilemap {
unsigned int* tilemap; unsigned int* tilemap;
std::vector<Actor*> actors; std::vector<Entity*> entities;
int width; int width;
int height; int height;
public: public:
@ -20,19 +20,19 @@ public:
int GetIndex(int x, int y); // Converts [x,y] to a 1D index. int GetIndex(int x, int y); // Converts [x,y] to a 1D index.
bool IsInsideBounds(int x, int y); bool IsInsideBounds(int x, int y);
std::vector<vec2i> getNeighbours(int x, int y, int range = 1); std::vector<vec2i> getNeighbours(int x, int y, int range = 1);
void set_tile(int x, int y, int tile); // "Tile" is inteded for tile ids, but can be anything really. void set_tile(int x, int y, unsigned int tile); // "Tile" is inteded for tile ids, but can be anything really.
int GetTile(int x, int y); int GetTile(int x, int y);
bool IsBlocked(int x, int y); // Checks if there is an actor blocking the tile. bool IsBlocked(int x, int y); // Checks if there is an actor blocking the tile.
void draw(Renderer *renderer, Tileset *tileset, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view); void draw(Renderer *renderer, Tileset *tileset, int x, int y, int tx, int ty, int tw, int th, FieldOfView* view);
void add_actor(Actor *actor); void add_entity(Entity *actor);
void RemoveActor(Actor* actor); void remove_entity(Entity *actor);
void debug_print(); void debug_print();
Actor* GetActor(int x, int y, Actors type); Entity* get_entity(int x, int y, EntityTypes type);
std::vector<Actor*> GetActors(int x, int y, int range, Actors type); std::vector<Entity*> get_entities(int x, int y, int range, EntityTypes type);
std::vector<Actor*>* get_actor_list(); std::vector<Entity*>* get_entity_list();
}; };

View file

@ -7,13 +7,13 @@
WanderNode::WanderNode(BehaviourTreeNode* parent) : BehaviourTreeNode(parent){} WanderNode::WanderNode(BehaviourTreeNode* parent) : BehaviourTreeNode(parent){}
WanderNode::~WanderNode() {} 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->map->getNeighbours(pos.x, pos.y); std::vector<vec2i> neighbours = tick->target->get_map()->getNeighbours(pos.x, pos.y);
while (true) { while (true) {
if (neighbours.size() <= 0) { if (neighbours.empty()) {
previous.clear(); previous.clear();
return BT_FAILED; return BT_FAILED;
} }
@ -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)) {
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());

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <cmath>
struct vec2i { struct vec2i {
int x, y; int x, y;
@ -13,6 +15,14 @@ struct vec2i {
this->y = y; this->y = y;
} }
double dist() {
return sqrt(dist_squared());
}
int dist_squared() {
return x*x + y*y;
}
bool operator==(vec2i a) { bool operator==(vec2i a) {
return a.x == x && a.y == y; return a.x == x && a.y == y;
} }