// 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);
}