2D_Engine/ECS/PlayerSystem.hpp

340 lines
13 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 "HitboxComponent.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;
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_Rect futureHitboxR = m_hitbox->hitboxR;
futureHitboxR.x += static_cast<int>(m_transform->velocity.x * m_transform->speed);
futureHitboxR.y += static_cast<int>(m_transform->velocity.y * m_transform->speed);
SDL_SetRenderDrawColor(Game::renderer, 200, 20, 18, 255);
SDL_RenderDrawRect(Game::renderer, &futureHitboxR);
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_SetRenderDrawColor(Game::renderer, 200, 20, 18, 255);
SDL_RenderDrawRect(Game::renderer, &interactableR);
}
if(it->get()->hasComponent<HitboxComponent>())
{
SDL_Rect intersectR;
if(SDL_IntersectRect(&it->get()->getComponent<HitboxComponent>().hitboxR, &m_hitbox->hitboxR, &intersectR))
{
SDL_SetRenderDrawColor(Game::renderer, 20, 20, 200, 255);
SDL_RenderDrawRect(Game::renderer, &intersectR);
}
}
}
SDL_SetRenderDrawColor(Game::renderer, 20, 20, 18, 255);
}
#endif // DEBUG_MODE
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 * m_transform->speed);
if(i == 1) futureHitboxR.y += static_cast<int>(m_transform->velocity.y * 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 != 0)
{
if(touchedHitbox == 1)
{
if(i == 0) m_transform->velocity.x = 0;
if(i == 1) m_transform->velocity.y = 0;
break;
}
else
{
Game::tileMap.LoadNextMap(touchedHitbox);
m_transform->position = Game::tileMap.getPlayerInitPos();
Game::camera.centerPos.x = static_cast<int>(m_transform->position.x + static_cast<float>((m_transform->dimension.x * m_transform->scale) >> 1));
Game::camera.centerPos.y = static_cast<int>(m_transform->position.y + static_cast<float>((m_transform->dimension.y * m_transform->scale) >> 1));
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)
{
SDL_Rect &entityHitboxR {it->get()->getComponent<HitboxComponent>().hitboxR};
SDL_Rect intersectR;
if(SDL_IntersectRect(&m_hitbox->hitboxR, &entityHitboxR, &intersectR))
{
Vector2D<int> overlap;
overlap.x = intersectR.w;
overlap.y = intersectR.h;
if(overlap.x < overlap.y)
{
int leftDistance = abs(entityHitboxR.x + entityHitboxR.w / 2 - m_hitbox->hitboxR.x);
int rightDistance = abs(entityHitboxR.x + entityHitboxR.w / 2 - (m_hitbox->hitboxR.x + m_hitbox->hitboxR.w));
if(leftDistance > rightDistance)
{
m_transform->position.x -= m_transform->speed;
}
else
{
m_transform->position.x += m_transform->speed;
}
}
else
{
int upDistance = abs(entityHitboxR.y + entityHitboxR.h / 2 - m_hitbox->hitboxR.y);
int downDistance = abs(entityHitboxR.y + entityHitboxR.h / 2 - (m_hitbox->hitboxR.y + m_hitbox->hitboxR.h));
if(upDistance > downDistance)
{
m_transform->position.y -= m_transform->speed;
}
else
{
m_transform->position.y += m_transform->speed;
}
}
break;
}
if(SDL_HasIntersection(&futureHitboxR, &entityHitboxR))
{
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()
{
Vector2D<float> target;
target.x = m_transform->position.x + static_cast<float>((m_transform->dimension.x * m_transform->scale) >> 2);
target.y = m_transform->position.y + static_cast<float>((m_transform->dimension.y * m_transform->scale) >> 2);
float smoothingFactor {0.08f};
if(playerState == State::Running) smoothingFactor = 0.12f;
Game::camera.centerPos.x = static_cast<int>(lerp(Game::camera.centerPos.x, target.x, smoothingFactor));
Game::camera.centerPos.y = static_cast<int>(lerp(Game::camera.centerPos.y, target.y, 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 // PLAYER_HPP