Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
40 kB
6
Indexable
// 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);
}