278 lines
10 KiB
C++
278 lines
10 KiB
C++
/********************\
|
|
| Copyright 2024, |
|
|
| Ulysse Cura |
|
|
\********************/
|
|
|
|
////////////////////////////////////////////
|
|
// //
|
|
// Système pour le joueur, gère tout ce //
|
|
// qui est necessaire pour le joueur. //
|
|
// //
|
|
////////////////////////////////////////////
|
|
|
|
#ifndef PLAYER_HPP
|
|
#define PLAYER_HPP
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include "AnimationSystem.hpp"
|
|
#include "ECS.hpp"
|
|
#include "InteractableComponent.hpp"
|
|
#include "SpriteComponent.hpp"
|
|
#include "TransformComponent.hpp"
|
|
#include "../Camera.hpp"
|
|
#include "../Events.hpp"
|
|
#include "../Game.hpp"
|
|
#include "../TileMap.hpp"
|
|
#include "../Vector2D.hpp"
|
|
|
|
using std::lerp, std::abs, std::vector, std::sort;
|
|
|
|
constexpr const float EPSILON = 1e-6f; // Tolérance
|
|
|
|
class PlayerSystem : public Component {
|
|
public:
|
|
void init() override
|
|
{
|
|
m_transform = &entity->getComponent<TransformComponent>();
|
|
m_sprite = &entity->getComponent<SpriteComponent>();
|
|
m_hitbox = &entity->getComponent<HitboxComponent>();
|
|
m_animation = &entity->getComponent<AnimationSystem>();
|
|
}
|
|
|
|
void update() override
|
|
{
|
|
m_getInputs();
|
|
m_handleHitboxes();
|
|
m_adjustVelocity();
|
|
m_setStateAndAnimation();
|
|
m_setFlip();
|
|
m_setCamera();
|
|
m_checkInteractions();
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
void draw() override
|
|
{
|
|
SDL_SetRenderDrawColor(Game::renderer, 200, 20, 18, 255);
|
|
|
|
for(auto it {Game::entityManager.getEntities().begin() + 1}; it < Game::entityManager.getEntities().end(); it++)
|
|
{
|
|
if(it->get()->hasComponent<InteractableComponent>())
|
|
{
|
|
SDL_Rect interactableR;
|
|
|
|
if(it->get()->hasComponent<HitboxComponent>())
|
|
{
|
|
interactableR = it->get()->getComponent<HitboxComponent>().hitboxR;
|
|
}
|
|
else
|
|
{
|
|
interactableR.x = static_cast<int>(it->get()->getComponent<TransformComponent>().position.x);
|
|
interactableR.y = static_cast<int>(it->get()->getComponent<TransformComponent>().position.y);
|
|
interactableR.w = it->get()->getComponent<TransformComponent>().dimension.x * it->get()->getComponent<TransformComponent>().scale;
|
|
interactableR.h = it->get()->getComponent<TransformComponent>().dimension.y * it->get()->getComponent<TransformComponent>().scale;
|
|
}
|
|
|
|
interactableR.x -= m_interactionRange;
|
|
interactableR.y -= m_interactionRange;
|
|
interactableR.w += (m_interactionRange * 2);
|
|
interactableR.h += (m_interactionRange * 2);
|
|
|
|
SDL_RenderDrawRect(Game::renderer, &interactableR);
|
|
}
|
|
}
|
|
|
|
SDL_Rect futureHitboxR = m_hitbox->hitboxR;
|
|
futureHitboxR.x += static_cast<int>(m_transform->velocity.x * static_cast<float>(m_transform->speed));
|
|
futureHitboxR.y += static_cast<int>(m_transform->velocity.y * static_cast<float>(m_transform->speed));
|
|
|
|
SDL_RenderDrawRect(Game::renderer, &futureHitboxR);
|
|
|
|
SDL_SetRenderDrawColor(Game::renderer, 20, 20, 18, 255);
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
void m_getInputs()
|
|
{
|
|
m_transform->velocity.x = Game::events.keys[SDL_SCANCODE_A] * -1 + Game::events.keys[SDL_SCANCODE_D] * 1;
|
|
m_transform->velocity.y = Game::events.keys[SDL_SCANCODE_W] * -1 + Game::events.keys[SDL_SCANCODE_S] * 1;
|
|
}
|
|
|
|
void m_handleHitboxes()
|
|
{
|
|
for(int i {0}; i < 2; i++)
|
|
{
|
|
SDL_Rect futureHitboxR = m_hitbox->hitboxR;
|
|
if(i == 0) futureHitboxR.x += static_cast<int>(m_transform->velocity.x * static_cast<float>(m_transform->speed));
|
|
if(i == 1) futureHitboxR.y += static_cast<int>(m_transform->velocity.y * static_cast<float>(m_transform->speed));
|
|
|
|
SDL_Rect hitboxTilesR;
|
|
hitboxTilesR.x = futureHitboxR.x / (TILE_SIZE * TILEMAP_SCALE);
|
|
hitboxTilesR.y = futureHitboxR.y / (TILE_SIZE * TILEMAP_SCALE);
|
|
hitboxTilesR.w = (futureHitboxR.w + futureHitboxR.x) / (TILE_SIZE * TILEMAP_SCALE);
|
|
hitboxTilesR.h = (futureHitboxR.h + futureHitboxR.y) / (TILE_SIZE * TILEMAP_SCALE);
|
|
|
|
vector<int> touchedHitboxes {};
|
|
|
|
for(int tileY = hitboxTilesR.y; tileY <= hitboxTilesR.h; tileY++)
|
|
{
|
|
for(int tileX = hitboxTilesR.x; tileX <= hitboxTilesR.w; tileX++)
|
|
{
|
|
touchedHitboxes.emplace_back(Game::tileMap.hitboxes[tileX + tileY * Game::tileMap.tileMapWidth]);
|
|
}
|
|
}
|
|
|
|
sort(touchedHitboxes.begin(), touchedHitboxes.end());
|
|
|
|
for(const auto &touchedHitbox : touchedHitboxes)
|
|
{
|
|
if(touchedHitbox == 1)
|
|
{
|
|
if(i == 0) m_transform->velocity.x = 0;
|
|
if(i == 1) m_transform->velocity.y = 0;
|
|
break;
|
|
}
|
|
else if(touchedHitbox != 0)
|
|
{
|
|
Game::tileMap.LoadNextMap(touchedHitbox);
|
|
m_transform->position = Game::tileMap.getPlayerInitPos();
|
|
|
|
Game::camera.camR.x = static_cast<int>(m_transform->position.x - static_cast<float>(Game::camera.camR.w / 2 - m_transform->dimension.x * m_transform->scale / 2));
|
|
Game::camera.camR.y = static_cast<int>(m_transform->position.y - static_cast<float>(Game::camera.camR.h / 2 - m_transform->dimension.y * m_transform->scale / 2));
|
|
|
|
playerState = State::None;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(auto it {Game::entityManager.getEntities().begin() + 1}; it < Game::entityManager.getEntities().end(); it++)
|
|
{
|
|
if(it->get()->hasComponent<HitboxComponent>())
|
|
{
|
|
if(it->get()->getComponent<HitboxComponent>().hitboxActivated)
|
|
{
|
|
if(SDL_HasIntersection(&futureHitboxR, &it->get()->getComponent<HitboxComponent>().hitboxR))
|
|
{
|
|
if(i == 0) m_transform->velocity.x = 0;
|
|
if(i == 1) m_transform->velocity.y = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void m_adjustVelocity()
|
|
{
|
|
if(isNotZero(m_transform->velocity.x) && isNotZero(m_transform->velocity.y))
|
|
{
|
|
m_transform->velocity.x *= 0.707106781186548f;
|
|
m_transform->velocity.y *= 0.707106781186548f;
|
|
}
|
|
}
|
|
|
|
void m_setStateAndAnimation()
|
|
{
|
|
if((isNotZero(m_transform->velocity.x) || isNotZero(m_transform->velocity.y)) && playerState != State::Running)
|
|
{
|
|
playerState = State::Running;
|
|
m_animation->changeAnimation("ressources/heroes/knight/run-sheet.png", 6, 10);
|
|
}
|
|
else if(!(isNotZero(m_transform->velocity.x) || isNotZero(m_transform->velocity.y)) && playerState != State::Idle)
|
|
{
|
|
playerState = State::Idle;
|
|
m_animation->changeAnimation("ressources/heroes/knight/idle-sheet.png", 4, 15);
|
|
}
|
|
}
|
|
|
|
void m_setFlip()
|
|
{
|
|
if(m_transform->velocity.x < 0.0f)
|
|
m_sprite->flipSprite = true;
|
|
if(m_transform->velocity.x > 0.0f)
|
|
m_sprite->flipSprite = false;
|
|
}
|
|
|
|
void m_setCamera()
|
|
{
|
|
float targetX = m_transform->position.x - static_cast<float>(Game::camera.camR.w / 2 - m_transform->dimension.x * m_transform->scale / 2);
|
|
float targetY = m_transform->position.y - static_cast<float>(Game::camera.camR.h / 2 - m_transform->dimension.y * m_transform->scale / 2);
|
|
|
|
float smoothingFactor {0.08f};
|
|
|
|
if(playerState == State::Running) smoothingFactor = 0.12f;
|
|
|
|
Game::camera.camR.x = static_cast<int>(lerp(Game::camera.camR.x, targetX, smoothingFactor));
|
|
Game::camera.camR.y = static_cast<int>(lerp(Game::camera.camR.y, targetY, smoothingFactor));
|
|
}
|
|
|
|
void m_checkInteractions()
|
|
{
|
|
if(Game::events.keys[SDL_SCANCODE_E])
|
|
{
|
|
for(auto it {Game::entityManager.getEntities().begin() + 1}; it < Game::entityManager.getEntities().end(); it++)
|
|
{
|
|
if(it->get()->hasComponent<InteractableComponent>())
|
|
{
|
|
SDL_Rect interactableR;
|
|
|
|
if(it->get()->hasComponent<HitboxComponent>())
|
|
{
|
|
interactableR = it->get()->getComponent<HitboxComponent>().hitboxR;
|
|
}
|
|
else
|
|
{
|
|
interactableR.x = static_cast<int>(it->get()->getComponent<TransformComponent>().position.x);
|
|
interactableR.y = static_cast<int>(it->get()->getComponent<TransformComponent>().position.y);
|
|
interactableR.w = it->get()->getComponent<TransformComponent>().dimension.x * it->get()->getComponent<TransformComponent>().scale;
|
|
interactableR.h = it->get()->getComponent<TransformComponent>().dimension.y * it->get()->getComponent<TransformComponent>().scale;
|
|
}
|
|
|
|
if(m_isInRange(interactableR))
|
|
{
|
|
it->get()->getComponent<InteractableComponent>().interact();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool m_isInRange(const SDL_Rect &entityR)
|
|
{
|
|
SDL_Rect intersectR {entityR};
|
|
|
|
intersectR.x -= m_interactionRange;
|
|
intersectR.y -= m_interactionRange;
|
|
intersectR.w += (m_interactionRange * 2);
|
|
intersectR.h += (m_interactionRange * 2);
|
|
|
|
return SDL_HasIntersection(&m_hitbox->hitboxR, &intersectR);
|
|
}
|
|
|
|
inline bool isNotZero(float value)
|
|
{
|
|
return abs(value) > EPSILON;
|
|
}
|
|
|
|
enum class State {
|
|
None,
|
|
Idle,
|
|
Running
|
|
};
|
|
|
|
TransformComponent *m_transform;
|
|
SpriteComponent *m_sprite;
|
|
HitboxComponent *m_hitbox;
|
|
AnimationSystem *m_animation;
|
|
|
|
int m_interactionRange {10};
|
|
|
|
State playerState {State::None};
|
|
};
|
|
|
|
#endif
|