SpelunkyRemake / Snake.cpp
Snake.cpp
Raw
#include "pch.h"
#include "Snake.h"

Texture* Snake::m_pTextures{ nullptr };
int Snake::m_Count{ 0 };

Snake::Snake(int x, int y)
	: Snake{ Point2f{ x * Tile::m_Size, y * Tile::m_Size } }
{

}

Snake::Snake(const Point2f& pos)
	: Enemy{ pos }
	, m_ActionState{ ActionState::IDLE }
	, m_AttackTimer{ }
	, m_AttackDuration{ 0.5f }
	, m_MoveSpeed{ 100.0f }
	, m_AttackSFX{ "Resources/Sounds/snakebite.wav" }
{
	m_Hitbox.width = 51;
	m_Hitbox.height = 47;
	m_HitboxMargin.x = 14;
	m_HitboxMargin.y = 14;

	m_Lives = 1;

	if (m_Count == 0)
	{
		m_pTextures = new Texture{ "Resources/Sprites/enemy_snake.png" };
	}
	m_pTexture = m_pTextures;

	SetAction(ActionState::IDLE);

	m_Count++;
}

Snake::~Snake()
{
	m_Count--;

	if (m_Count == 0)
	{
		delete m_pTextures;
		m_pTextures = nullptr;
	}
}

void Snake::Draw() const
{
	Enemy::Draw();
}

void Snake::Update(float elapsedSec, const Level& level, Player* pPlayer, ParticleManager* pParticleManager)
{
	if (!level.IsOnGround(m_Hitbox)) m_Velocity += elapsedSec * m_GravityAcceleration;
	m_Hitbox.bottom += m_Velocity.y * elapsedSec;
	m_Hitbox.left += m_Velocity.x * elapsedSec;

	level.HandleCollision(m_Hitbox, m_Velocity);

	if ((m_ActionState != ActionState::ATTACKING) && (pPlayer->GetState() != Player::ActionState::HURT))
	{
		if (utils::IsOverlapping(m_Hitbox, pPlayer->GetHitbox()))
		{
			if (pPlayer->DidJumpOnEnemy(GetTopVertices()))
			{
				pParticleManager->CreateBlood(Point2f{ GetHitbox().left, GetHitbox().bottom });
				m_Lives--;
			}
			else
			{
				(pPlayer->GetHitbox().left < m_Hitbox.left) ? m_Direction = Direction::LEFT : m_Direction = Direction::RIGHT;

				m_AttackSFX.Play(0);
				pParticleManager->CreateBlood(Point2f{ pPlayer->GetHitbox().left, pPlayer->GetHitbox().bottom });

				SetAction(ActionState::ATTACKING);
				pPlayer->Hit(1, int(m_Direction));
			}
		}
	}

	switch (m_ActionState)
	{
	case ActionState::IDLE:
		if (level.IsOnGround(m_Hitbox) && !level.IsOnSingleTile(m_Hitbox))
		{
			SetAction(ActionState::MOVING);
			break;
		}
		break;
	case ActionState::MOVING:
		
		if (!level.IsOnGround(m_Hitbox) || level.IsOnSingleTile(m_Hitbox))
		{
			SetAction(ActionState::IDLE);
			break;
		}

		if (m_Velocity.x == 0) // hit wall - switch direction
		{
			(m_Direction == Direction::RIGHT) ? m_Direction = Direction::LEFT : m_Direction = Direction::RIGHT;
			(m_Direction == Direction::RIGHT) ? m_Velocity.x = m_MoveSpeed : m_Velocity.x = -m_MoveSpeed;
			break;
		}

		if (level.IsNearlyFalling(m_Hitbox, int(m_Direction))) // almost falling off tile - switch direction
		{
			(m_Direction == Direction::RIGHT) ? m_Direction = Direction::LEFT : m_Direction = Direction::RIGHT;
			(m_Direction == Direction::RIGHT) ? m_Velocity.x = m_MoveSpeed : m_Velocity.x = -m_MoveSpeed;
		}

		break;
	case ActionState::ATTACKING:
		m_AttackTimer += elapsedSec;
		if (m_AttackTimer >= m_AttackDuration)
		{
			SetAction(ActionState::IDLE);
			break;
		}
		break;
	default:
		break;
	}

	m_AnimTime += elapsedSec;
	if (m_AnimTime >= 1.f / m_NrFramesPerSec)
	{
		m_AnimFrame++;
		if (m_AnimFrame >= m_AnimLength)
		{
			if (m_AnimLoop)
			{
				m_AnimFrame = 0;
			}
			else
			{
				m_AnimFrame = m_AnimLength;
			}
		}

		m_AnimTime = 0;
	}

	m_SpriteRect.left = m_Hitbox.left - m_HitboxMargin.x;
	m_SpriteRect.bottom = m_Hitbox.bottom - m_HitboxMargin.y;
}

void Snake::SetAction(ActionState action)
{
	m_ActionState = action;

	m_AnimFrame = 0;
	m_AnimLoop = true;

	switch (action)
	{
	case ActionState::IDLE:
		m_Velocity.x = 0;

		m_AnimFirstFrameId = 0;
		m_AnimLength = 4;
		m_NrFramesPerSec = 5;
		break;

	case ActionState::ATTACKING:
		m_AttackTimer = 0;
		m_Velocity.x = 0;

		m_AnimFirstFrameId = 12;
		m_AnimLength = 6;
		m_NrFramesPerSec = 10;
		break;

	case ActionState::MOVING:
		(m_Direction == Direction::RIGHT) ? m_Velocity.x = m_MoveSpeed : m_Velocity.x = -m_MoveSpeed;

		m_AnimFirstFrameId = 5;
		m_AnimLength = 6;
		m_NrFramesPerSec = 8;
		break;

	default:
		break;
	}
}