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;