Untitled
unknown
plain_text
a year ago
40 kB
6
Indexable
Never
// Debug.h #pragma once #include "../Common.h" #include "../States/GameState/GameState.h" void DrawGrid(sf::RenderWindow& window, GameState* gameState); void DrawCube(sf::RenderWindow& window, sf::Vector2i pos, GameState* gameState); void DrawPathfinding(sf::RenderWindow& window, std::vector<sf::Vector2i>& path, sf::Vector2i pathStart, sf::Vector2i pathTarget, GameState* gameState); // Debug.cpp #include "Debug.h" void DrawGrid(sf::RenderWindow& window, GameState* gameState) { sf::Vector2f vec(0, 0); for (int y = 0; y < NumberOfTilesY; y++) { vec.x = 0; for (int x = 0; x < NumberOfTilesX; x++) { sf::VertexArray quad(sf::LinesStrip, 5); quad[0].color = sf::Color::White; quad[1].color = sf::Color::White; quad[2].color = sf::Color::White; quad[3].color = sf::Color::White; quad[4].color = sf::Color::White; //if (!tileArray[x][y].isEmpty) //{ // if (tileArray[x][y].tileType == sTile::Player) // { // quad[0].color = sf::Color::Green; // quad[1].color = sf::Color::Green; // quad[2].color = sf::Color::Green; // quad[3].color = sf::Color::Green; // quad[4].color = sf::Color::Green; // } // else // { // quad[0].color = sf::Color::Red; // quad[1].color = sf::Color::Red; // quad[2].color = sf::Color::Red; // quad[3].color = sf::Color::Red; // quad[4].color = sf::Color::Red; // } //} quad[0].position = vec; sf::Vector2f v = vec; v.x += gameState->tileWidth; quad[1].position = v; quad[2].position = sf::Vector2f(vec.x + gameState->tileWidth, vec.y + gameState->tileHeight); sf::Vector2f v2 = vec; v2.y += gameState->tileHeight; quad[3].position = v2; quad[4].position = vec; window.draw(quad); vec.x += gameState->tileWidth; } vec.y += gameState->tileHeight; } } void DrawCube(sf::RenderWindow& window, sf::Vector2i pos, GameState* gameState) { sf::Vector2f vec; vec.x = pos.x * gameState->tileWidth; vec.y = pos.y * gameState->tileHeight; sf::VertexArray quad(sf::LinesStrip, 5); quad[0].color = sf::Color::Red; quad[1].color = sf::Color::Red; quad[2].color = sf::Color::Red; quad[3].color = sf::Color::Red; quad[4].color = sf::Color::Red; quad[0].position = vec; sf::Vector2f v = vec; v.x += gameState->tileWidth; quad[1].position = v; quad[2].position = sf::Vector2f(vec.x + gameState->tileWidth, vec.y + gameState->tileHeight); sf::Vector2f v2 = vec; v2.y += gameState->tileHeight; quad[3].position = v2; quad[4].position = vec; window.draw(quad); } void DrawPathfinding(sf::RenderWindow& window, std::vector<sf::Vector2i>& path, sf::Vector2i pathStart, sf::Vector2i pathTarget, GameState* gameState) { for (sf::Vector2i pos : path) { sf::Vector2f vec; vec.x = pos.x * gameState->tileWidth; vec.y = pos.y * gameState->tileHeight; sf::VertexArray quad(sf::LinesStrip, 5); quad[0].color = sf::Color::Green; quad[1].color = sf::Color::Green; quad[2].color = sf::Color::Green; quad[3].color = sf::Color::Green; quad[4].color = sf::Color::Green; quad[0].position = vec; sf::Vector2f v = vec; v.x += gameState->tileWidth; quad[1].position = v; quad[2].position = sf::Vector2f(vec.x + gameState->tileWidth, vec.y + gameState->tileHeight); sf::Vector2f v2 = vec; v2.y += gameState->tileHeight; quad[3].position = v2; quad[4].position = vec; window.draw(quad); } sf::Vector2f vec; vec.x = pathStart.x * gameState->tileWidth; vec.y = pathStart.y * gameState->tileHeight; sf::VertexArray quad(sf::LinesStrip, 5); quad[0].color = sf::Color::Red; quad[1].color = sf::Color::Red; quad[2].color = sf::Color::Red; quad[3].color = sf::Color::Red; quad[4].color = sf::Color::Red; quad[0].position = vec; sf::Vector2f v = vec; v.x += gameState->tileWidth; quad[1].position = v; quad[2].position = sf::Vector2f(vec.x + gameState->tileWidth, vec.y + gameState->tileHeight); sf::Vector2f v2 = vec; v2.y += gameState->tileHeight; quad[3].position = v2; quad[4].position = vec; window.draw(quad); vec.x = pathTarget.x * gameState->tileWidth; vec.y = pathTarget.y * gameState->tileHeight; quad[0].position = vec; v = vec; v.x += gameState->tileWidth; quad[1].position = v; quad[2].position = sf::Vector2f(vec.x + gameState->tileWidth, vec.y + gameState->tileHeight); v2 = vec; v2.y += gameState->tileHeight; quad[3].position = v2; quad[4].position = vec; window.draw(quad); } // Entity.h #pragma once #include "../Common.h" class GameState; enum Directions { Up, Down, Left, Right, None }; enum class Entities { NotDefined, Pacman, Blinky, Clyde, Inky, Pinky, }; class Entity { public: Entity(GameState* gameState, Entities entityType); float speed = 140; sf::Vector2i gridPos; Directions currentDir = Directions::None; Entities entityType; sf::RectangleShape body; sf::Texture texture; virtual void Update(const float& deltaTime) = 0; virtual void Draw(sf::RenderWindow& rw) = 0; protected: GameState* gameState; sf::Vector2f GetFinalTilePosition(); bool IsNeighbourTileAvailable(Directions dir); bool IsTeleportTile(sf::Vector2i pos); void Teleport(Directions teleportTo); virtual void UpdateTileArray(sf::Vector2i newPos) = 0; virtual void Move(const float& deltaTime) = 0; }; Directions GetOppositeDirection(Directions dir); // entity.cpp #include "Entity.h" #include "../GameManager.h" #include "../States/GameState/GameState.h" Directions GetOppositeDirection(Directions dir) { switch (dir) { case Up: return Down; break; case Down: return Up; break; case Left: return Right; break; case Right: return Left; break; } return None; } Entity::Entity(GameState* gameState, Entities entityType) { this->gameState = gameState; this->entityType = entityType; } sf::Vector2f Entity::GetFinalTilePosition() { return sf::Vector2f((gridPos.x * gameState->tileWidth) - 5, (gridPos.y * gameState->tileHeight) - 5); } bool Entity::IsNeighbourTileAvailable(Directions dir) { switch (dir) { case Up: if (!gameState->tileArray[gridPos.x][gridPos.y - 1].DoesTileHaveType(sTile::Wall)) return true; break; case Down: if (!gameState->tileArray[gridPos.x][gridPos.y + 1].DoesTileHaveType(sTile::Wall)) return true; break; case Left: if (IsTeleportTile(sf::Vector2i(gridPos.x - 1, gridPos.y))) return false; if (!gameState->tileArray[gridPos.x - 1][gridPos.y].DoesTileHaveType(sTile::Wall)) return true; break; case Right: if (IsTeleportTile(sf::Vector2i(gridPos.x + 1, gridPos.y))) return false; if (!gameState->tileArray[gridPos.x + 1][gridPos.y].DoesTileHaveType(sTile::Wall)) return true; break; } return false; } bool Entity::IsTeleportTile(sf::Vector2i pos) { if (pos.x < 0 || pos.x > 27) return true; return false; } void Entity::Teleport(Directions teleportTo) { switch (teleportTo) { case Left: body.setPosition(sf::Vector2f((-1 * gameState->tileWidth) - 5, (gridPos.y * gameState->tileHeight) - 5)); break; case Right: body.setPosition(sf::Vector2f((28 * gameState->tileWidth) - 5, (gridPos.y * gameState->tileHeight) - 5)); break; } } // snack.h #pragma once #include "../../Common.h" #include "../Entity.h" #include "../../Animation/Animator.h" #include "../../Animation/Animation.h" class Snack : public Entity { public: enum SnackType { SmallSnack, BigSnack }; SnackType snackType; Snack(SnackType type, sf::Vector2i gridPos, GameState* gameState); void UpdateTileArray(sf::Vector2i newPos) {}; void Move(const float& deltaTime) override {}; void Update(const float& deltaTime) override; void Draw(sf::RenderWindow& rw) override; private: //todo delete animator and animation pointers on destructor Animator* animator; Animation* bigSnackFlickerAnimation; void SetupAnimation(); }; // snack.cpp #include "Snack.h" #include "../../States/GameState/GameState.h" Snack::Snack(SnackType type, sf::Vector2i gridPos, GameState* gameState) : Entity(gameState, Entities::NotDefined) { animator = new Animator(&body); snackType = type; if (type == SmallSnack) { body = sf::RectangleShape(sf::Vector2f(gameState->tileWidth, gameState->tileHeight)); texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(226, 240, 7, 7)); } else { body = sf::RectangleShape(sf::Vector2f(gameState->tileWidth, gameState->tileHeight)); texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(233, 240, 8, 8)); SetupAnimation(); animator->SetAnimationClip(bigSnackFlickerAnimation); } this->gridPos = gridPos; body.setTexture(&texture); body.move(sf::Vector2f(gameState->tileWidth * gridPos.x, gameState->tileHeight * gridPos.y)); } void Snack::Update(const float& deltaTime) { if(snackType == BigSnack) animator->Update(deltaTime); } void Snack::Draw(sf::RenderWindow& rw) { rw.draw(body); } void Snack::SetupAnimation() { sf::Texture spriteOn, spriteOff; spriteOn.loadFromFile("Resources/PacManSprites.png", sf::IntRect(233, 240, 8, 8)); spriteOff.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 160, 8, 8)); std::vector<sf::Texture> leftAnimTextures{ spriteOff, spriteOn }; bigSnackFlickerAnimation = new Animation(leftAnimTextures, true, 0.2f); } // pacman.h #pragma once #include "../../Common.h" #include "../../GameManager.h" #include "../Entity.h" #include "../../Animation/Animation.h" #include "../../Animation/Animator.h" #include "../../Audio/AudioManager.h" class Pacman : public Entity { public: Pacman(int x, int y, GameState* gameState); ~Pacman(); void Update(const float& deltaTime) override; void Draw(sf::RenderWindow& rw) override; void Die(); private: Animator* animator; AudioManager audio; Animation* animations[5];//left, right, up, down, death Directions nextDir = None; bool hasCompletedMovement = false; bool isEatingSnacks = false; void Move(const float& deltaTime) override; void EatSnack(sf::Vector2i snackGridPosition); void UpdatePlayerTilePosition(); void UpdateTileArray(sf::Vector2i newPos); void SetupAnimations(); void ChangeAnimation(Directions dir); }; // pacman.cpp #include "Pacman.h" #include "../../Tile.h" #include "../Enemy/Enemy.h" #include "../../Debugger/Debug.h" #include "../../States/GameState/GameState.h" #include "../../Audio/AudioAssets.h" Pacman::Pacman(int tileX, int tileY, GameState* gameState) : Entity(gameState, Entities::Pacman) { body.setSize(sf::Vector2f(40, 40)); gridPos = sf::Vector2i(tileX, tileY); gameState->tileArray[tileX][tileY].isEmpty = false; gameState->tileArray[tileX][tileY].tileTypes.push_back(sTile::Player); SetupAnimations(); animator = new Animator(&body); if (texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 1, 13, 13))) body.setTexture(&texture); else std::cout << "texture not loaded correctly" << std::endl; body.move(sf::Vector2f(30 * tileX, 25.5f * tileY)); } Pacman::~Pacman() { delete animator; for (auto const& x : animations) delete x; } void Pacman::Draw(sf::RenderWindow& rw) { rw.draw(body); //DrawCube(rw, gridPos, gameState); } void Pacman::Update(const float& deltaTime) { if (!gameState->isPacmanDead) { if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) nextDir = Up; else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) nextDir = Down; else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) nextDir = Left; else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) nextDir = Right; //handle eating snack sound effect if (isEatingSnacks && !audio.IsPlayingAudio(Sounds::Munch)) audio.PlaySound(Sounds::Munch, true, VOLUME_MUNCH); else if (!isEatingSnacks && audio.IsPlayingAudio(Sounds::Munch)) audio.StopSound(Sounds::Munch); Move(deltaTime); } animator->Update(deltaTime); } void Pacman::Die() { animator->SetAnimationClip(animations[4]); audio.StopSound(); gameState->OnPacmanDeath(); } void Pacman::Move(const float& deltaTime) { float dt = speed * deltaTime; switch (currentDir) { case None: UpdatePlayerTilePosition(); return; case Up: if (GetFinalTilePosition().y <= body.getPosition().y) body.setPosition(body.getPosition().x, body.getPosition().y - dt); else UpdatePlayerTilePosition(); break; case Down: if (GetFinalTilePosition().y >= body.getPosition().y) body.setPosition(body.getPosition().x, body.getPosition().y + dt); else UpdatePlayerTilePosition(); break; case Left: if (GetFinalTilePosition().x <= body.getPosition().x) body.setPosition(body.getPosition().x - dt, body.getPosition().y); else UpdatePlayerTilePosition(); break; case Right: if (GetFinalTilePosition().x >= body.getPosition().x) body.setPosition(body.getPosition().x + dt, body.getPosition().y); else UpdatePlayerTilePosition(); break; } } void Pacman::EatSnack(sf::Vector2i snackGridPosition) { if (gameState->SnackList[gameState->FindSnackID(snackGridPosition)]->snackType == Snack::BigSnack) { gameState->ScareEnemys(); } isEatingSnacks = true; gameState->score += 10; gameState->DeleteSnack(snackGridPosition); } void Pacman::UpdatePlayerTilePosition() { if (nextDir != None) { if (IsNeighbourTileAvailable(nextDir)) { currentDir = nextDir; ChangeAnimation(currentDir); } //nextDir = None; } if (IsNeighbourTileAvailable(currentDir)) { switch (currentDir) { case Up: UpdateTileArray(sf::Vector2i(gridPos.x, gridPos.y - 1)); break; case Down: UpdateTileArray(sf::Vector2i(gridPos.x, gridPos.y + 1)); break; case Left: UpdateTileArray(sf::Vector2i(gridPos.x - 1, gridPos.y)); break; case Right: UpdateTileArray(sf::Vector2i(gridPos.x + 1, gridPos.y)); break; } } else { switch (currentDir) { case Left: if (IsTeleportTile(sf::Vector2i(gridPos.x - 1, gridPos.y))) { Teleport(Right); UpdateTileArray(sf::Vector2i(27, gridPos.y)); break; } case Right: if (IsTeleportTile(sf::Vector2i(gridPos.x + 1, gridPos.y))) { Teleport(Left); UpdateTileArray(sf::Vector2i(0, gridPos.y)); break; } } //if the player is stuck on a corner, and its not a teleport tile, munching sound should stop playing if (isEatingSnacks) isEatingSnacks = false; } } void Pacman::UpdateTileArray(sf::Vector2i newPos) { //emptying current tile gameState->tileArray[gridPos.x][gridPos.y].isEmpty = true; gameState->tileArray[gridPos.x][gridPos.y].EraseTileType(sTile::Player); gridPos = newPos; if (gameState->tileArray[gridPos.x][gridPos.y].DoesTileHaveType(sTile::Snack)) { gameState->tileArray[gridPos.x][gridPos.y].EraseTileType(sTile::Snack); EatSnack(newPos); } else isEatingSnacks = false; Enemy* e = gameState->FindEnemyByPosition(gridPos); if (e != NULL) { if (e->state == EnemyState::Frightened) { e->Eaten(); gameState->score += 100; } else if (e->state != EnemyState::Eaten_Retreating) Die(); } //transfering player to next tile gameState->tileArray[gridPos.x][gridPos.y].isEmpty = false; gameState->tileArray[gridPos.x][gridPos.y].tileTypes.push_back(sTile::Player); } void Pacman::SetupAnimations() { //right animation sf::Texture r1, r2, r3; r1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 1, 13, 13)); r2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 1, 13, 13)); r3.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 1, 13, 13)); std::vector<sf::Texture> rightAnimTextures{ r1,r2,r3 }; //left animation sf::Texture l1, l2, l3; l1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 17, 13, 13)); l2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 17, 13, 13)); l3.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 17, 13, 13)); std::vector<sf::Texture> leftAnimTextures{ l1,l2,l3 }; //up animation sf::Texture u1, u2, u3; u1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 33, 13, 13)); u2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 33, 13, 13)); u3.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 33, 13, 13)); std::vector<sf::Texture> upAnimTextures{ u1,u2,u3 }; //down animation sf::Texture d1, d2, d3; d1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 49, 13, 13)); d2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 49, 13, 13)); d3.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 49, 13, 13)); std::vector<sf::Texture> downAnimTextures{ d1, d2, d3 }; //death animation sf::Texture de1, de2, de3, de4, de5, de6, de7, de8, de9, de10, de11, de12; de1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(278, 1, 13, 13)); de2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(294, 1, 13, 13)); de3.loadFromFile("Resources/PacManSprites.png", sf::IntRect(310, 1, 13, 13)); de4.loadFromFile("Resources/PacManSprites.png", sf::IntRect(326, 1, 13, 13)); de5.loadFromFile("Resources/PacManSprites.png", sf::IntRect(342, 1, 13, 13)); de6.loadFromFile("Resources/PacManSprites.png", sf::IntRect(358, 1, 13, 13)); de7.loadFromFile("Resources/PacManSprites.png", sf::IntRect(374, 1, 13, 13)); de8.loadFromFile("Resources/PacManSprites.png", sf::IntRect(390, 1, 13, 13)); de9.loadFromFile("Resources/PacManSprites.png", sf::IntRect(406, 1, 13, 13)); de10.loadFromFile("Resources/PacManSprites.png", sf::IntRect(422, 1, 13, 13)); de11.loadFromFile("Resources/PacManSprites.png", sf::IntRect(438, 1, 13, 13)); de12.loadFromFile("Resources/PacManSprites.png", sf::IntRect(438, 17, 13, 13)); std::vector<sf::Texture> deathAnimTextures{ de1, de2, de3, de4, de5, de6, de7, de8, de9, de10, de11, de12 }; animations[0] = new Animation(leftAnimTextures); animations[1] = new Animation(rightAnimTextures); animations[2] = new Animation(upAnimTextures); animations[3] = new Animation(downAnimTextures); animations[4] = new Animation(deathAnimTextures, false, 0.20f); } void Pacman::ChangeAnimation(Directions dir) { switch (dir) { case Up: animator->SetAnimationClip(animations[2]); break; case Down: animator->SetAnimationClip(animations[3]); break; case Left: animator->SetAnimationClip(animations[0]); break; case Right: animator->SetAnimationClip(animations[1]); break; } } // Blinky.h #pragma once #include "Enemy.h" class Blinky : public Enemy { public: Blinky(sf::Vector2i gridPos, GameState* gameState); ~Blinky(); private: sf::Vector2i GetScatterTargetPosition() override; void SetupAnimations() override; }; #include "Blinky.h" #include "../../States/GameState/GameState.h" Blinky::Blinky(sf::Vector2i gridPos, GameState* gameState) : Enemy(gridPos, sf::Vector2i(230, 65), gameState, Entities::Blinky) { SetupAnimations(); } Blinky::~Blinky() { } sf::Vector2i Blinky::GetScatterTargetPosition() { return sf::Vector2i(26,1); } void Blinky::SetupAnimations() { sf::Texture r1, r2, l1, l2; r1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 65, 14, 14)); r2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 65, 14, 14)); l1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 65, 14, 14)); l2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(278, 65, 14, 14)); std::vector<sf::Texture> leftAnimTextures{ l1,l2 }; std::vector<sf::Texture> rightAnimTextures{ r1,r2 }; sf::Texture u1, u2, d1, d2; u1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(294, 65, 14, 14)); u2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(310, 65, 14, 14)); d1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(326, 65, 14, 14)); d2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(342, 65, 14, 14)); std::vector<sf::Texture> upAnimTextures{ u1,u2 }; std::vector<sf::Texture> downAnimTextures{ d1,d2 }; animations[0] = new Animation(leftAnimTextures, true, 0.1f); animations[1] = new Animation(rightAnimTextures, true, 0.1f); animations[2] = new Animation(upAnimTextures, true, 0.1f); animations[3] = new Animation(downAnimTextures, true, 0.1f); } // Clyde.h #pragma once #include "Enemy.h" class Clyde : public Enemy { public: Clyde(sf::Vector2i gridPos, GameState* gameState); ~Clyde(); private: sf::Vector2i GetScatterTargetPosition() override; sf::Vector2i GetChaseTargetPosition() override; void SetupAnimations() override; }; #include "Clyde.h" #include "../../Tile.h" #include "../Pacman/Pacman.h" #include "../../States/GameState/GameState.h" Clyde::Clyde(sf::Vector2i gridPos, GameState* gameState) : Enemy(gridPos, sf::Vector2i(230, 113), gameState, Entities::Clyde) { SetupAnimations(); } Clyde::~Clyde() { } sf::Vector2i Clyde::GetScatterTargetPosition() { return sf::Vector2i(1, 29); } sf::Vector2i Clyde::GetChaseTargetPosition() { if (sTile::GetDistanceBetweenTiles(gridPos, gameState->pacman->gridPos) > 8) return gameState->pacman->gridPos; return sf::Vector2i(1, 29); } void Clyde::SetupAnimations() { sf::Texture r1, r2, l1, l2; r1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 113, 14, 14)); r2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 113, 14, 14)); l1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 113, 14, 14)); l2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(278, 113, 14, 14)); std::vector<sf::Texture> leftAnimTextures{ l1,l2 }; std::vector<sf::Texture> rightAnimTextures{ r1,r2 }; sf::Texture u1, u2, d1, d2; u1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(294, 113, 14, 14)); u2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(310, 113, 14, 14)); d1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(326, 113, 14, 14)); d2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(342, 113, 14, 14)); std::vector<sf::Texture> upAnimTextures{ u1,u2 }; std::vector<sf::Texture> downAnimTextures{ d1,d2 }; animations[0] = new Animation(leftAnimTextures, true, 0.1f); animations[1] = new Animation(rightAnimTextures, true, 0.1f); animations[2] = new Animation(upAnimTextures, true, 0.1f); animations[3] = new Animation(downAnimTextures, true, 0.1f); } // Enemy.h #pragma once #include "../Entity.h" #include "../../Animation/Animator.h" #include "../../Animation/Animation.h" #include "../../Audio/AudioManager.h" enum class EnemyState { Scatter, Chase, Frightened, Eaten_FreezedGame, Eaten_Retreating }; class Enemy : public Entity { public: EnemyState state = EnemyState::Scatter; Enemy(sf::Vector2i gridPos, sf::Vector2i texturePos, GameState* gameState, Entities entityType); ~Enemy(); void Scare(); void UpdateTileArray(sf::Vector2i newPos) override; void Update(const float& deltaTime) override; void Draw(sf::RenderWindow& rw) override; void Eaten(); private: int currentWave = 0; float totalWaveTime = 0; float scaredTimer = 0; bool hasStartedflickeringAnim = false; AudioManager audio; std::vector<sf::Vector2i> currentPath; struct Wave { EnemyState waveState; float duration; Wave(EnemyState s, float d) { waveState = s; duration = d; } }; Wave waves[8] = {Wave(EnemyState::Scatter, 7), Wave(EnemyState::Chase, 20) , Wave(EnemyState::Scatter, 7), Wave(EnemyState::Chase, 20) , Wave(EnemyState::Scatter, 5), Wave(EnemyState::Chase, 20) , Wave(EnemyState::Scatter, 5), Wave(EnemyState::Chase, -1) }; void UpdateEnemyTilePosition(); void ChangeAnimation(); sf::Vector2i GetOppositeDirectionNeighbour(); void Move(const float& deltaTime) override; protected: Animator* animator; Animation* animations[6]; //left, right, up, down, frightened, flickeringFrightened virtual void SetupAnimations(); virtual sf::Vector2i GetScatterTargetPosition(); virtual sf::Vector2i GetChaseTargetPosition(); virtual sf::Vector2i GetFrightenedTargetPosition(); }; #include "Enemy.h" #include <cstdlib> #include "../../Debugger/Debug.h" #include "../../Pathfinding/Pathfinding.h" #include "../Pacman/Pacman.h" #include "../../Audio/AudioAssets.h" #include "../../States/GameState/GameState.h" Enemy::Enemy(sf::Vector2i gridPos, sf::Vector2i texturePos, GameState* gameState, Entities entityType) : Entity(gameState, entityType) { body.setSize(sf::Vector2f(40, 40)); this->gridPos = gridPos; gameState->tileArray[gridPos.x][gridPos.y].isEmpty = false; gameState->tileArray[gridPos.x][gridPos.y].tileTypes.push_back(sTile::Ghost); SetupAnimations(); animator = new Animator(&body); if (texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(texturePos.x, texturePos.y, 14, 14))) body.setTexture(&texture); else std::cout << "texture not loaded correctly" << std::endl; body.move(sf::Vector2f(30 * gridPos.x, 25.5f * gridPos.y)); } Enemy::~Enemy() { delete animator; for (auto const& x : animations) delete x; } void Enemy::Scare() { state = EnemyState::Frightened; animator->SetAnimationClip(animations[4]); scaredTimer = 0; hasStartedflickeringAnim = false; } void Enemy::Eaten() { state = EnemyState::Eaten_FreezedGame; scaredTimer = 0; audio.PlaySound(Sounds::EatGhost, false, VOLUME); ChangeAnimation(); gameState->FreezeGame(entityType); } void Enemy::Update(const float& deltaTime) { switch (state) { //updating frightened timer case EnemyState::Frightened: scaredTimer += deltaTime; if (scaredTimer >= 6 && !hasStartedflickeringAnim){ animator->SetAnimationClip(animations[5]); scaredTimer = 0; hasStartedflickeringAnim = true; } if (scaredTimer >= 6 && hasStartedflickeringAnim) { state = waves[currentWave].waveState; gameState->StopPowerSnackSound(); hasStartedflickeringAnim = false; ChangeAnimation(); scaredTimer = 0; } break; case EnemyState::Eaten_FreezedGame: //start retreating mode if (!audio.IsPlayingAudio(Sounds::EatGhost)) { scaredTimer = 0; hasStartedflickeringAnim = false; state = EnemyState::Eaten_Retreating; ChangeAnimation(); gameState->UnfreezeGame(); gameState->audioManager.StopSound(Sounds::PowerSnack); audio.PlaySound(Sounds::Retreating, true, VOLUME); } break; case EnemyState::Eaten_Retreating: //when ghost gets to ghost house, switch back to normal behaviour if (gameState->tileArray[gridPos.x][gridPos.y].DoesTileHaveType(sTile::GhostHouse)) { state = waves[currentWave].waveState; audio.StopSound(Sounds::Retreating); //only play sound if power snack effect is still active if(gameState->powerSnackActive) gameState->audioManager.PlaySound(Sounds::PowerSnack, true, VOLUME); } break; //updating wave system default: totalWaveTime += deltaTime; if (totalWaveTime >= waves[currentWave].duration) { totalWaveTime -= waves[currentWave].duration; if(currentWave < (sizeof(waves) / sizeof(waves[0])) - 1) currentWave++; state = waves[currentWave].waveState; } break; } if(state != EnemyState::Eaten_Retreating && state != EnemyState::Eaten_FreezedGame) animator->Update(deltaTime); if(state != EnemyState::Eaten_FreezedGame) Move(deltaTime); } void Enemy::Move(const float& deltaTime) { float dt = speed * deltaTime; if (state == EnemyState::Frightened) dt /= 2; else if (state == EnemyState::Eaten_Retreating) dt *= 2; switch (currentDir) { case None: UpdateEnemyTilePosition(); return; case Up: if (GetFinalTilePosition().y <= body.getPosition().y) body.setPosition(body.getPosition().x, body.getPosition().y - dt); else UpdateEnemyTilePosition(); break; case Down: if (GetFinalTilePosition().y >= body.getPosition().y) body.setPosition(body.getPosition().x, body.getPosition().y + dt); else UpdateEnemyTilePosition(); break; case Left: if (GetFinalTilePosition().x <= body.getPosition().x) body.setPosition(body.getPosition().x - dt, body.getPosition().y); else UpdateEnemyTilePosition(); break; case Right: if (GetFinalTilePosition().x >= body.getPosition().x) body.setPosition(body.getPosition().x + dt, body.getPosition().y); else UpdateEnemyTilePosition(); break; } } void Enemy::Draw(sf::RenderWindow& rw) { rw.draw(body); //DrawCube(rw, gridPos, gameState); //if (currentPath.size() > 0) { // switch (state) // { // case EnemyState::Scatter: // DrawPathfinding(rw, currentPath, gridPos, GetScatterTargetPosition()); // break; // case EnemyState::Chase: // DrawPathfinding(rw, currentPath, gridPos, GetChaseTargetPosition()); // break; // } //} } void Enemy::UpdateEnemyTilePosition() { std::vector<sf::Vector2i> pos; switch (state) { case EnemyState::Scatter: pos = FindPath(gridPos, GetScatterTargetPosition(), currentDir, gameState); break; case EnemyState::Chase: pos = FindPath(gridPos, GetChaseTargetPosition(), currentDir, gameState); break; case EnemyState::Eaten_Retreating: pos = FindPath(gridPos, sf::Vector2i(13, 14), currentDir, gameState); break; case EnemyState::Frightened: std::vector<sf::Vector2i> path{ GetFrightenedTargetPosition() }; pos = path; break; } //in the case that no path is found, the enemy will set a neighbour tile as his path if (pos.size() == 0) { pos = FindPath(gridPos, GetOppositeDirectionNeighbour(), currentDir, gameState); //in the case that no path is found, its because the enemy is about to be teleported to the other side of the screen if (pos.size() == 0) { sf::Vector2i p = gridPos; switch (currentDir) { case Left: p.x--; if (p.x < 0) { Teleport(Right); gridPos.x = 27; return; } else pos.push_back(p); break; case Right: p.x++; if (p.x > 27) { Teleport(Left); gridPos.x = 0; return; } else pos.push_back(p); break; } } } currentPath = pos; if (pos[0].x < gridPos.x) { if (currentDir != Left) { currentDir = Left; ChangeAnimation(); } } else if (pos[0].x > gridPos.x) { if (currentDir != Right) { currentDir = Right; ChangeAnimation(); } } else if (pos[0].y < gridPos.y) { if (currentDir != Up) { currentDir = Up; ChangeAnimation(); } } else if (pos[0].y > gridPos.y) { if (currentDir != Down) { currentDir = Down; ChangeAnimation(); } } UpdateTileArray(pos[0]); } void Enemy::ChangeAnimation() { if (state != EnemyState::Frightened) { if (state == EnemyState::Eaten_Retreating) { switch (currentDir) { case Left: if (texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(374, 81, 14, 14))) body.setTexture(&texture); break; case Right: if (texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(358, 81, 14, 14))) body.setTexture(&texture); break; case Up: if (texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(390, 81, 14, 14))) body.setTexture(&texture); break; case Down: if (texture.loadFromFile("Resources/PacManSprites.png", sf::IntRect(406, 81, 14, 14))) body.setTexture(&texture); break; case None: break; } } else { switch (currentDir) { case Left: animator->SetAnimationClip(animations[0]); break; case Right: animator->SetAnimationClip(animations[1]); break; case Up: animator->SetAnimationClip(animations[2]); break; case Down: animator->SetAnimationClip(animations[3]); break; } } } } sf::Vector2i Enemy::GetOppositeDirectionNeighbour() { Directions dir = GetOppositeDirection(currentDir); sf::Vector2i tile = gridPos; switch (dir) { case Up: tile.y--; break; case Down: tile.y++; break; case Left: tile.x--; break; case Right: tile.x++; break; } return tile; } void Enemy::UpdateTileArray(sf::Vector2i newPos) { //emptying current tile bool hasSnack = gameState->FindSnackID(gridPos) == -1; gameState->tileArray[gridPos.x][gridPos.y].isEmpty = hasSnack? true : false; gameState->tileArray[gridPos.x][gridPos.y].EraseTileType(sTile::Ghost); gridPos = newPos; //transfering enemy to next tile gameState->tileArray[gridPos.x][gridPos.y].isEmpty = false; gameState->tileArray[gridPos.x][gridPos.y].tileTypes.push_back(sTile::Ghost); if (newPos == gameState->pacman->gridPos) { if (state == EnemyState::Frightened) Eaten(); else if (state != EnemyState::Eaten_Retreating) gameState->pacman->Die(); } } sf::Vector2i Enemy::GetScatterTargetPosition() { return sf::Vector2i(1, 1); } sf::Vector2i Enemy::GetChaseTargetPosition() { return gameState->pacman->gridPos; } sf::Vector2i Enemy::GetFrightenedTargetPosition() { std::vector<Directions> possibleDirections; if (GetOppositeDirection(currentDir) != Left && IsNeighbourTileAvailable(Left)) possibleDirections.push_back(Left); if (GetOppositeDirection(currentDir) != Right && IsNeighbourTileAvailable(Right)) possibleDirections.push_back(Right); if (GetOppositeDirection(currentDir) != Up && IsNeighbourTileAvailable(Up)) possibleDirections.push_back(Up); if (GetOppositeDirection(currentDir) != Down && IsNeighbourTileAvailable(Down)) possibleDirections.push_back(Down); if (possibleDirections.size() != 0) { int randomDir = std::rand() % possibleDirections.size(); sf::Vector2i pos = gridPos; switch (possibleDirections[randomDir]) { case Up: pos.y--; break; case Down: pos.y++; break; case Left: pos.x--; break; case Right: pos.x++; break; } return pos; } return gridPos; } void Enemy::SetupAnimations() { sf::Texture f1, f2, ff1, ff2; f1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(358, 65, 14, 14)); f2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(374, 65, 14, 14)); ff1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(390, 65, 14, 14)); ff2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(406, 65, 14, 14)); std::vector<sf::Texture> frightenedAnimTextures{ f1, f2 }; std::vector<sf::Texture> flickeringFrightenedAnimTextures{ f1, f2, ff1, ff2 }; animations[4] = new Animation(frightenedAnimTextures); animations[5] = new Animation(flickeringFrightenedAnimTextures); } // inky.h #pragma once #include "Enemy.h" class Inky : public Enemy { public: Inky(sf::Vector2i gridPos, GameState* gameState); ~Inky(); private: sf::Vector2i GetScatterTargetPosition() override; sf::Vector2i GetChaseTargetPosition() override; void SetupAnimations() override; }; #include "Inky.h" #include "../Pacman/Pacman.h" #include "../../States/GameState/GameState.h" Inky::Inky(sf::Vector2i gridPos, GameState* gameState) : Enemy(gridPos, sf::Vector2i(230, 97), gameState, Entities::Inky) { SetupAnimations(); } Inky::~Inky() { } sf::Vector2i Inky::GetScatterTargetPosition() { return sf::Vector2i(26, 29); } sf::Vector2i Inky::GetChaseTargetPosition() { sf::Vector2i dist; switch (gameState->pacman->currentDir) { Up: dist.y -= 2; break; Down: dist.y += 2; break; Left: dist.x -= 2; break; Right: dist.x += 2; break; } sf::Vector2i pacmanPos = gameState->pacman->gridPos; sf::Vector2i blinkyPos = gameState->enemys[0]->gridPos; if (pacmanPos.x < blinkyPos.x) dist.x = blinkyPos.x - pacmanPos.x; else dist.x = pacmanPos.x - blinkyPos.x; if (pacmanPos.y < blinkyPos.y) dist.y = blinkyPos.y - pacmanPos.y; else dist.y = pacmanPos.y - blinkyPos.y; dist *= 2; sf::Vector2i finalPos; if (gridPos.x + dist.x > NumberOfTilesX - 2) finalPos.x = NumberOfTilesX - 2; else if (gridPos.x + dist.x < 1) finalPos.x = 1; else finalPos.x = gridPos.x + dist.x; if (gridPos.y + dist.y > NumberOfTilesY - 2) finalPos.y = NumberOfTilesY - 2; else if (gridPos.y + dist.y < 1) finalPos.y = 1; else finalPos.y = gridPos.y + dist.y; return finalPos; } void Inky::SetupAnimations() { sf::Texture r1, r2, l1, l2; r1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 97, 14, 14)); r2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 97, 14, 14)); l1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 97, 14, 14)); l2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(278, 97, 14, 14)); std::vector<sf::Texture> leftAnimTextures{ l1,l2 }; std::vector<sf::Texture> rightAnimTextures{ r1,r2 }; sf::Texture u1, u2, d1, d2; u1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(294, 97, 14, 14)); u2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(310, 97, 14, 14)); d1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(326, 97, 14, 14)); d2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(342, 97, 14, 14)); std::vector<sf::Texture> upAnimTextures{ u1,u2 }; std::vector<sf::Texture> downAnimTextures{ d1,d2 }; animations[0] = new Animation(leftAnimTextures, true, 0.1f); animations[1] = new Animation(rightAnimTextures, true, 0.1f); animations[2] = new Animation(upAnimTextures, true, 0.1f); animations[3] = new Animation(downAnimTextures, true, 0.1f); } // pinky.h #pragma once #include "Enemy.h" class Pinky : public Enemy { public: Pinky(sf::Vector2i gridPos, GameState* gameState); ~Pinky(); private: sf::Vector2i GetChaseTargetPosition() override; void SetupAnimations() override; }; #include "Pinky.h" #include "../Pacman/Pacman.h" #include "../../States/GameState/GameState.h" Pinky::Pinky(sf::Vector2i gridPos, GameState* gameState) : Enemy(gridPos, sf::Vector2i(230, 81), gameState, Entities::Pinky) { SetupAnimations(); } Pinky::~Pinky() { } sf::Vector2i Pinky::GetChaseTargetPosition() { sf::Vector2i pos = gameState->pacman->gridPos; switch (gameState->pacman->currentDir) { case Up: pos.y -= 4; break; case Down: pos.y += 4; break; case Left: pos.x -= 4; break; case Right: pos.x += 4; break; } return pos; } void Pinky::SetupAnimations() { sf::Texture r1, r2, l1, l2; r1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(230, 81, 14, 14)); r2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(246, 81, 14, 14)); l1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(262, 81, 14, 14)); l2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(278, 81, 14, 14)); std::vector<sf::Texture> leftAnimTextures{ l1,l2 }; std::vector<sf::Texture> rightAnimTextures{ r1,r2 }; sf::Texture u1, u2, d1, d2; u1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(294, 81, 14, 14)); u2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(310, 81, 14, 14)); d1.loadFromFile("Resources/PacManSprites.png", sf::IntRect(326, 81, 14, 14)); d2.loadFromFile("Resources/PacManSprites.png", sf::IntRect(342, 81, 14, 14)); std::vector<sf::Texture> upAnimTextures{ u1,u2 }; std::vector<sf::Texture> downAnimTextures{ d1,d2 }; animations[0] = new Animation(leftAnimTextures, true, 0.1f); animations[1] = new Animation(rightAnimTextures, true, 0.1f); animations[2] = new Animation(upAnimTextures, true, 0.1f); animations[3] = new Animation(downAnimTextures, true, 0.1f); }