diff --git a/Unuk-QT/Unuk-QT.pro b/Unuk-QT/Unuk-QT.pro
index d3f8211..cb08ce9 100644
--- a/Unuk-QT/Unuk-QT.pro
+++ b/Unuk-QT/Unuk-QT.pro
@@ -77,6 +77,7 @@ SOURCES += ../src/libUnuk/Engine/WorldManager.cpp \
../src/libUnuk/LevelGen/LevelGen.cpp \
../src/libUnuk/LevelGen/MapEntities.cpp \
../src/libUnuk/LevelGen/MapElement.cpp \
+ ../src/libUnuk/LevelGen/MapTile.cpp \
../src/libUnuk/UI/EventHistory.cpp \
../src/libUnuk/UI/Bar.cpp \
../src/libUnuk/System/Vec2.cpp \
diff --git a/Win32/Unuk/LibUnuk/LibUnuk.vcproj b/Win32/Unuk/LibUnuk/LibUnuk.vcproj
index f4944ab..175ce34 100644
--- a/Win32/Unuk/LibUnuk/LibUnuk.vcproj
+++ b/Win32/Unuk/LibUnuk/LibUnuk.vcproj
@@ -232,6 +232,10 @@
RelativePath="..\..\..\src\libUnuk\LevelGen\MapEntities.h"
>
+
+
@@ -316,6 +320,14 @@
RelativePath="..\..\..\src\libUnuk\System\Timer.h"
>
+
+
+
+
GetWorld().OnPlayerMove(this);
+ }
+
_healthBar.SetProgress((float)GetHealth() / 100.0f);
}
@@ -123,7 +134,7 @@ void Player::SetCamera(void) {
void Player::Move() {
map->MoveIfPossible(this, xVel, yVel, true);
- Character::HealthBarScroll();
+ Character::HealthBarScroll();
}
void Player::SetLevel(int level) {
diff --git a/src/Unuk/Player.h b/src/Unuk/Player.h
index d2d9972..a5bf7f4 100644
--- a/src/Unuk/Player.h
+++ b/src/Unuk/Player.h
@@ -26,6 +26,8 @@ public:
void SetLevelLiteral(int level) { _level = level; }
void SetExpLiteral(int exp) { _exp = exp; }
void SetHealthLiteral(int health) { _health = health; }
+
+ void SetXY(float xArg, float yArg) { x = xArg, y = yArg; _lastTileX = xArg / TILE_WIDTH; _lastTileY = yArg / TILE_HEIGHT; }
static const int MAX_LEVEL = 20;
static const int EXP_TABLE[MAX_LEVEL];
@@ -41,4 +43,7 @@ private:
string _name;
int _level;
int _exp;
+
+ int _lastTileX;
+ int _lastTileY;
};
diff --git a/src/libUnuk/Engine/NPC.cpp b/src/libUnuk/Engine/NPC.cpp
index cab1e5d..4f59627 100644
--- a/src/libUnuk/Engine/NPC.cpp
+++ b/src/libUnuk/Engine/NPC.cpp
@@ -1,11 +1,12 @@
#include "NPC.h"
+#include "../Unuk/Player.h"
NPC::NPC(LevelGen* mapArg) : Character(mapArg) {
- _moveTimer.Start();
-
- _moveChangeFrequency = 14000;
- _moveDurationMax = 3000;
- _moveDurationMin = 1000;
+ _moveTimer.Start();
+
+ _moveChangeFrequency = 14000;
+ _moveDurationMax = 3000;
+ _moveDurationMin = 1000;
}
NPC::~NPC(void) {
@@ -13,51 +14,80 @@ NPC::~NPC(void) {
}
void NPC::Update(void) {
- // Store the NPC's health.
- // int health = GetHealth(); // not referenced
+ // Store the NPC's health.
+ // int health = GetHealth(); // not referenced
- Move();
+ Move();
- if(xVel > 0) directionFacing = FACING_RIGHT;
- else if(xVel < 0) directionFacing = FACING_LEFT;
- else if(yVel > 0) directionFacing = FACING_DOWN;
- else if(yVel < 0) directionFacing = FACING_UP;
+ if(xVel > 0) directionFacing = FACING_RIGHT;
+ else if(xVel < 0) directionFacing = FACING_LEFT;
+ else if(yVel > 0) directionFacing = FACING_DOWN;
+ else if(yVel < 0) directionFacing = FACING_UP;
- _healthBar.SetProgress((float)GetHealth() / 100.0f);
+ _healthBar.SetProgress((float)GetHealth() / 100.0f);
}
void NPC::Move(void) {
- if(_moving && _moveTimer.GetTicks() > _moveDurationMax) {
- xVel = 0.0f;
- yVel = 0.0f;
- _moving = false;
- }
-
- if(_moving && _moveTimer.GetTicks() >= _moveDurationCurrent) {
- xVel = 0.0f;
- yVel = 0.0f;
- _moving = false;
- }
-
- if(_moveTimer.GetTicks() > _moveChangeFrequency) {
- _moveTimer.Start();
- _moveDurationCurrent = _moveDurationMin + (rand() % (_moveDurationMax - _moveDurationMin));
- if(rand() % 2) {
- yVel = 0.0f;
- if(rand() % 2)
- xVel = CHARACTER_SPEED;
- else
- xVel = -CHARACTER_SPEED;
- } else {
- xVel = 0.0f;
- if(rand() % 2)
- yVel = CHARACTER_SPEED;
- else
- yVel = -CHARACTER_SPEED;
- }
- _moving = true;
- }
-
- map->MoveIfPossible(this, xVel, yVel, false);
- Character::HealthBarScroll();
+ if(_moving && _moveTimer.GetTicks() > _moveDurationMax) {
+ xVel = 0.0f;
+ yVel = 0.0f;
+ _moving = false;
+ }
+
+ if(_moving && _moveTimer.GetTicks() >= _moveDurationCurrent) {
+ xVel = 0.0f;
+ yVel = 0.0f;
+ _moving = false;
+ }
+
+ if(_moveTimer.GetTicks() > _moveChangeFrequency) {
+ _moveTimer.Start();
+ _moveDurationCurrent = _moveDurationMin + (rand() % (_moveDurationMax - _moveDurationMin));
+ if(rand() % 2) {
+ yVel = 0.0f;
+ if(rand() % 2)
+ xVel = CHARACTER_SPEED;
+ else
+ xVel = -CHARACTER_SPEED;
+ } else {
+ xVel = 0.0f;
+ if(rand() % 2)
+ yVel = CHARACTER_SPEED;
+ else
+ yVel = -CHARACTER_SPEED;
+ }
+ _moving = true;
+ }
+
+ map->MoveIfPossible(this, xVel, yVel, false);
+ Character::HealthBarScroll();
+}
+
+void NPC::OnPlayerMove(Player* player) {
+ MapTile& start = map->GetTile(x / TILE_WIDTH, y / TILE_HEIGHT);
+ MapTile& goal = map->GetTile(player->GetX() / TILE_WIDTH, player->GetY() / TILE_HEIGHT);
+
+ _astar.SetStartAndGoalStates(start, goal);
+
+ bool solutionFound = false;
+
+ // Dirty loop to calculate the path
+ while(true) {
+ _astar.SearchStep();
+
+ int state = _astar.GetState();
+ if(state == AStarSearch::SEARCH_STATE_SUCCEEDED) {
+ solutionFound = true;
+ break;
+ } else if(state == AStarSearch::SEARCH_STATE_SEARCHING) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if(solutionFound) {
+ x = _astar.GetSolutionEnd()->GetTileX();
+ y = _astar.GetSolutionEnd()->GetTileY();
+ }
}
diff --git a/src/libUnuk/Engine/NPC.h b/src/libUnuk/Engine/NPC.h
index d381533..55010d6 100644
--- a/src/libUnuk/Engine/NPC.h
+++ b/src/libUnuk/Engine/NPC.h
@@ -1,6 +1,10 @@
#pragma once
#include "Character.h"
+#include "Pathfinding.h"
+#include "../LevelGen/MapTile.h"
+
+class Player;
class NPC : public Character {
public:
@@ -9,6 +13,8 @@ public:
void Update(void);
+ void OnPlayerMove(Player* player);
+
protected:
void Move(void);
@@ -22,4 +28,6 @@ private:
bool _moving;
Timer _moveTimer;
+
+ AStarSearch _astar;
};
diff --git a/src/libUnuk/Engine/Pathfinding.h b/src/libUnuk/Engine/Pathfinding.h
index b3f96ce..d30f3ab 100644
--- a/src/libUnuk/Engine/Pathfinding.h
+++ b/src/libUnuk/Engine/Pathfinding.h
@@ -37,7 +37,7 @@ public:
float h; // Heuristic estimate of the distance of the goal.
float f; // Sum of cost and heuristic.
- Node(void) : parent(0), child(0), g(0.0f), h(0.0f), f(0.0) {}
+ Node(UserState userState) : parent(0), child(0), g(0.0f), h(0.0f), f(0.0), _userState(userState) {}
UserState _userState;
};
@@ -68,14 +68,11 @@ public:
void SetStartAndGoalStates(UserState& start, UserState& goal) {
_cancelRequest= false;
- _start = AllocateNode();
- _goal = AllocateNode();
+ _start = AllocateNode(start);
+ _goal = AllocateNode(goal);
assert((_start != NULL && _goal != NULL));
- _start->_userState = _start;
- _goal->_userState = _goal;
-
_state = SEARCH_STATE_SEARCHING;
// Initialize the AStar specific parts of the start node.
@@ -245,7 +242,7 @@ public:
// Call this to add a successor to a list of
// successors when expanding the seach frontier.
bool AddSuccessor(UserState& state) {
- Node* node = Allocate();
+ Node* node = AllocateNode(state);
if(node) {
node->_userState = state;
@@ -307,8 +304,8 @@ public:
// Get the end node.
UserState* GetSolutionEnd(void) {
_currentSolutionNode = _goal;
- if(goal) {
- return &goal->_userState;
+ if(_goal) {
+ return &_goal->_userState;
} else {
return NULL;
}
@@ -470,8 +467,8 @@ private:
}
- Node* AllocateNode(void) {
- Node *p = new Node;
+ Node* AllocateNode(UserState& userState) {
+ Node *p = new Node(userState);
return p;
}
diff --git a/src/libUnuk/Engine/WorldManager.cpp b/src/libUnuk/Engine/WorldManager.cpp
index b4f7265..b9f164b 100644
--- a/src/libUnuk/Engine/WorldManager.cpp
+++ b/src/libUnuk/Engine/WorldManager.cpp
@@ -185,3 +185,9 @@ void WorldManager::OnPlayerAttack(Player* player) {
}
}
}
+
+void WorldManager::OnPlayerMove(Player* player) {
+ for(std::list::iterator i = _npcs.begin(); i != _npcs.end(); ++i) {
+ (*i)->OnPlayerMove(player);
+ }
+}
diff --git a/src/libUnuk/Engine/WorldManager.h b/src/libUnuk/Engine/WorldManager.h
index fe1cfb1..5833abf 100644
--- a/src/libUnuk/Engine/WorldManager.h
+++ b/src/libUnuk/Engine/WorldManager.h
@@ -27,6 +27,7 @@ public:
int GetNPCCount() { return _npcs.size(); }
void OnPlayerAttack(Player* player);
+ void OnPlayerMove(Player* player);
private:
LevelGen* _level;
diff --git a/src/libUnuk/LevelGen/LevelGen.cpp b/src/libUnuk/LevelGen/LevelGen.cpp
index 8e5afa2..6b7ca76 100644
--- a/src/libUnuk/LevelGen/LevelGen.cpp
+++ b/src/libUnuk/LevelGen/LevelGen.cpp
@@ -255,7 +255,7 @@ void LevelGen::Unload(void) {
_entityTextures.Unload();
for(int x = 0; x < TILE_ARRAY_SIZE; x++) {
for(int y = 0; y < TILE_ARRAY_SIZE; y++) {
- _tile[x][y] = MapTile();
+ _tile[x][y] = MapTile(this);
}
}
}
@@ -411,17 +411,17 @@ void LevelGen::MoveIfPossible(Character* character, float xVel, float yVel, bool
charRect.w = character->GetWidth();
charRect.h = character->GetHeight() / 4;
- for(int x = 0; x < BOUNDARIES_X; x++) {
- for(int y = 0; y < BOUNDARIES_Y; y++) {
- if(!_tile[x][y].GetEntitySolitity()) {
+ for(int i = 0; i < BOUNDARIES_X; i++) {
+ for(int j = 0; j < BOUNDARIES_Y; j++) {
+ if(!_tile[i][j].GetEntitySolitity()) {
continue;
}
SDL_Rect entityRect;
- entityRect.x = _tile[x][y].GetEntityX();
- entityRect.y = _tile[x][y].GetEntityY();
- entityRect.w = _tile[x][y].GetEntityWidth();
- entityRect.h = _tile[x][y].GetEntityHeight();
+ entityRect.x = _tile[i][j].GetEntityX();
+ entityRect.y = _tile[i][j].GetEntityY();
+ entityRect.w = _tile[i][j].GetEntityWidth();
+ entityRect.h = _tile[i][j].GetEntityHeight();
if(CheckCollisionRect(entityRect, charRect)) {
return;
@@ -448,6 +448,41 @@ void LevelGen::MoveIfPossible(Character* character, float xVel, float yVel, bool
character->SetXY(targetX, targetY);
}
+bool LevelGen::CanMoveToPoint(int xArg, int yArg) {
+ if(xArg < 0 || xArg > SCREEN_WIDTH ||
+ yArg < 0 || yArg > SCREEN_HEIGHT) {
+ return false;
+ }
+
+ int tileX = xArg / TILE_WIDTH;
+ int tileY = yArg / TILE_HEIGHT;
+
+ if(_tile[tileX][tileY].GetTileSolidity()) {
+ return false;
+ }
+
+ for(int i = 0; i < BOUNDARIES_X; i++) {
+ for(int j = 0; j < BOUNDARIES_Y; j++) {
+ if(!_tile[i][j].GetEntitySolitity()) {
+ continue;
+ }
+
+ SDL_Rect entityRect;
+ entityRect.x = _tile[i][j].GetEntityX();
+ entityRect.y = _tile[i][j].GetEntityY();
+ entityRect.w = _tile[i][j].GetEntityWidth();
+ entityRect.h = _tile[i][j].GetEntityHeight();
+
+ if(xArg >= entityRect.x && xArg <= (entityRect.x + entityRect.w) &&
+ yArg >= entityRect.y && yArg <= (entityRect.y + entityRect.h)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
string LevelGen::GetCurrentMap(void) {
return _currentMap;
}
@@ -491,3 +526,7 @@ int LevelGen::GetEntityHeight(int xArg, int yArg) {
int LevelGen::GetTileZLevel(int xArg, int yArg) {
return _tile[xArg + 1][yArg + 1].GetZLevel();
}
+
+MapTile& LevelGen::GetTile(int xArg, int yArg) {
+ return _tile[xArg][yArg];
+}
diff --git a/src/libUnuk/LevelGen/LevelGen.h b/src/libUnuk/LevelGen/LevelGen.h
index f083a4e..a3b1bb0 100644
--- a/src/libUnuk/LevelGen/LevelGen.h
+++ b/src/libUnuk/LevelGen/LevelGen.h
@@ -32,6 +32,7 @@ public:
void FindSpawnPoint(int& xArg, int& yArg, int objWidth, int objHeight);
void MoveIfPossible(Character* character, float xVel, float yVel, bool isPlayer = false);
+ bool CanMoveToPoint(int xArg, int yArg);
bool GetTileSolidity(int xArg, int yArg);
int GetTileX(int xArg, int yArg);
@@ -45,6 +46,8 @@ public:
int GetTileZLevel(int xArg, int yArg);
+ MapTile& GetTile(int xArg, int yArg);
+
string GetCurrentMap(void);
WorldManager& GetWorld(void) { return _world; }
diff --git a/src/libUnuk/LevelGen/MapTile.cpp b/src/libUnuk/LevelGen/MapTile.cpp
new file mode 100644
index 0000000..e861204
--- /dev/null
+++ b/src/libUnuk/LevelGen/MapTile.cpp
@@ -0,0 +1,68 @@
+#include "MapTile.h"
+#include "LevelGen.h"
+
+MapTile::MapTile(const MapTile& source) {
+ _level = source._level;
+ _tile = source._tile;
+ _entity = source._entity;
+ _zLevel = source._zLevel;
+}
+
+bool MapTile::IsSameState(MapTile& tile) {
+ return (tile.GetTileX() == _tile.GetX()) && (tile.GetTileY() == _tile.GetY());
+}
+
+bool MapTile::IsGoal(MapTile& tile) {
+ return IsSameState(tile);
+}
+
+float MapTile::GoalDistanceEstimate(MapTile& goal) {
+ Vec2 thisPos(_tile.GetX(), _tile.GetY());
+ Vec2 goalPos(goal.GetTileX(), goal.GetTileY());
+ return abs(Vec2::DistanceSquared(thisPos, goalPos));
+}
+
+float MapTile::GetCost(MapTile& goal) {
+ return 64.0f*64.0f;
+}
+
+bool MapTile::GetSuccessors(AStarSearch* search, MapTile* parent) {
+ int tileX = _tile.GetX() / TILE_WIDTH;
+ int tileY = _tile.GetY() / TILE_HEIGHT;
+
+ // Add tile to the left if possible.
+ if(tileX > 0) {
+ MapTile& successor = _level->GetTile(tileX - 1, tileY);
+ if(successor.GetTileSolidity() || successor.GetEntitySolitity()) {
+ search->AddSuccessor(successor);
+ }
+ }
+
+ // Add tile to the right if possible
+ // TODO: replace 64 with map width
+ if(tileX < 64) {
+ MapTile& successor = _level->GetTile(tileX + 1, tileY);
+ if(successor.GetTileSolidity() || successor.GetEntitySolitity()) {
+ search->AddSuccessor(successor);
+ }
+ }
+
+ // Add tile to the bottom if possible
+ if(tileY > 0) {
+ MapTile& successor = _level->GetTile(tileX, tileY - 1);
+ if(successor.GetTileSolidity() || successor.GetEntitySolitity()) {
+ search->AddSuccessor(successor);
+ }
+ }
+
+ // Add tile to the top if possible
+ // TODO: replace 64 with map height
+ if(tileY < 64) {
+ MapTile& successor = _level->GetTile(tileX, tileY + 1);
+ if(successor.GetTileSolidity() || successor.GetEntitySolitity()) {
+ search->AddSuccessor(successor);
+ }
+ }
+
+ return true;
+}
diff --git a/src/libUnuk/LevelGen/MapTile.h b/src/libUnuk/LevelGen/MapTile.h
index f7e2e55..01f506c 100644
--- a/src/libUnuk/LevelGen/MapTile.h
+++ b/src/libUnuk/LevelGen/MapTile.h
@@ -7,11 +7,15 @@
#include "../Sprite/ApplySurface.h"
#include "../LevelGen/MapElement.h"
#include "../LevelGen/MapEntities.h"
+#include "../Engine/Pathfinding.h"
+#include "../System/Vec2.h"
using namespace std;
+class LevelGen;
+
class MapTile {
public:
- MapTile(void) { }
+ MapTile(LevelGen* level = NULL) : _level(level) { }
~MapTile(void) { }
void Render(void) { _tile.Render(), _entity.Render(); }
@@ -48,7 +52,16 @@ public:
void SetZLevel(int arg) { _zLevel = arg; }
int GetZLevel(void) { return _zLevel; }
+ // Pathfinding stuff.
+ MapTile(const MapTile& source);
+ bool IsSameState(MapTile& tile);
+ bool IsGoal(MapTile& tile);
+ float GoalDistanceEstimate(MapTile& goal);
+ float GetCost(MapTile& goal);
+ bool GetSuccessors(AStarSearch* search, MapTile* parent);
+
private:
+ LevelGen* _level;
MapElement _tile;
MapEntityGeneric _entity;