#include "pch.h" #include "imgui.h" #include "Player.h" #include #include "colors.h" #include "GroundTileTypeManager.h" #include "utils.h" #include "Levels/World/WorldLevel.h" #include "Animations/Animation.h" #include "GridSystem/WorldTile.h" Player::Player(const Vector2f& Position, TextureManager* manager) : m_Position(Position), m_Size(Vector2f { 40, 40 }), m_Vel(Vector2f { 0, 0 }), m_Acc(Vector2f { 0, 0 }) { m_ContactMap[Collision::CollisionDirection::Top] = nullptr; m_ContactMap[Collision::CollisionDirection::Bottom] = nullptr; m_ContactMap[Collision::CollisionDirection::Left] = nullptr; m_ContactMap[Collision::CollisionDirection::Right] = nullptr; m_walkAnimation = new Animation( manager->GetTexture("animations/player/player_walk.png"), 8, 0.1f, Rectf { 0, 0, 70, 70 }, true); m_turnAnimation = new Animation( manager->GetTexture("animations/player/player_turn.png"), 5, 0.07f, Rectf { 0, 0, 70, 70 }, false); m_digStartAnimation = new Animation( manager->GetTexture("animations/player/player_dig_start.png"), 7, 0.07f, Rectf { 0, 0, 70, 70 }, false); m_digAnimation = new Animation( manager->GetTexture("animations/player/player_dig.png"), 7, 0.05f, Rectf { 0, 0, 70, 70 }, true); m_currentAnimation = m_walkAnimation; } Player::Player(Player&& other) { } Player::~Player() { delete m_walkAnimation; delete m_turnAnimation; delete m_digAnimation; delete m_digStartAnimation; } Collision::CollisionRect Player::GetCollisionRect() const { Collision::CollisionRect rect = { m_Position, m_Size, m_Vel }; return rect; } void Player::Draw() const { if (m_DrawCollisionRect) { utils::SetColor(Colors::PINK); utils::DrawRect(Rectf { m_Position.x, m_Position.y, m_Size.x, m_Size.y }); } Vector2f center = m_Position + m_Size / 2; const int frameWidth = 70; //TODO: fix this int halfFrameWidth = frameWidth / 2; int bobOffset = m_BobUp ? 1 : 0; float rotateOffset = std::abs(m_Vel.x) / 40; Vector2f drawPos = Vector2f { center.x, center.y + 9 + bobOffset }; glPushMatrix(); glTranslatef(drawPos.x - halfFrameWidth, drawPos.y - halfFrameWidth, 0); if (!m_Grounded && std::abs(m_Vel.y) > 0.1f) { glRotatef(m_Direction == PlayerDirection::Left ? rotateOffset : -rotateOffset, 0, 0, 1); } { m_currentAnimation->Draw(Vector2f { 0, 0 }, Rectf { 0, 0, frameWidth, frameWidth }); } glPopMatrix(); utils::DrawEllipse(m_DigDestination, 5, 5); utils::DrawEllipse(m_DigStart, 5, 5); } void Player::ProcessImGui() { ImGui::Begin("Collision Info", nullptr, ImGuiWindowFlags_AlwaysAutoResize); ImGui::Text("is Grounded: %s", m_Grounded ? "true" : "false"); ImGui::Checkbox("Draw Collision Rect", &m_DrawCollisionRect); std::string currentState { "No idea" }; switch (m_State) { case PlayerState::Idle: currentState = "Idle"; break; case PlayerState::Digging: currentState = "Digging"; break; case PlayerState::Walking: currentState = "Walking"; break; case PlayerState::Flying: currentState = "Flying"; break; } ImGui::Text("Player State %s", currentState.c_str()); ImGui::Text("Is digging Primed: %s", m_IsDiggingPrimed ? "true" : "false"); ImGui::Text("Bob counter: %f", m_BobTimer); ImGui::Text("Bob up: %s", m_BobUp ? "true" : "false"); ImGui::Text("Is Grounded: %s", m_Grounded ? "true" : "false"); std::string direction {}; switch (m_Direction) { case PlayerDirection::Down: direction = "Down"; break; case PlayerDirection::Left: direction = "Left"; break; case PlayerDirection::Right: direction = "Right"; break; case PlayerDirection::Up: direction = "Up"; break; } ImGui::Text("Direction: %s", direction.c_str()); //ContactMap ImGui::Text("ContactMap:"); ImGui::Text("Top: %s", m_ContactMap[Collision::CollisionDirection::Top] != nullptr ? "true" : "false"); ImGui::Text("Bottom: %s", m_ContactMap[Collision::CollisionDirection::Bottom] != nullptr ? "true" : "false"); ImGui::Text("Left: %s", m_ContactMap[Collision::CollisionDirection::Left] != nullptr ? "true" : "false"); ImGui::Text("Right: %s", m_ContactMap[Collision::CollisionDirection::Right] != nullptr ? "true" : "false"); ImGui::End(); } void Player::Dig(Collision::CollisionDirection dir, WorldLevel& level) { m_State = PlayerState::Digging; m_DigProgress = 0; m_DigTile = m_ContactMap[dir]; //Set the digging location in the center of the destination tile; const WorldTile* tile = m_ContactMap[dir]; //Add case for bottom because otherwise i clip through the floor m_DigDestination = tile->GetPosition(); if (dir == Collision::Bottom) { m_DigDestination += Vector2f { 0, 2 }; //Center m_DigDestination.x += tile->GetSize().x / 2 - m_Size.x / 2; } if (dir == Collision::Left) { m_DigDestination += Vector2f { 2, 0 }; } m_ContactMap[dir] = nullptr; } bool Player::CanDig(Collision::CollisionDirection dir, WorldLevel& level) { WorldTile* tile = m_ContactMap[dir]; if (tile == nullptr) { return false; } GroundTileType type = *tile->GetTileType(); //TODO: Add a list of non diggable tiles if (type == GroundTileTypeManager::GetInstance()->HARD_LEFT || type == GroundTileTypeManager::GetInstance()->HARD_MIDDLE || type == GroundTileTypeManager::GetInstance()-> HARD_RIGHT) { return false; } return true; } void Player::Update(float elapsedTime, WorldLevel& level) { m_BobTimer += elapsedTime; if (m_BobTimer >= m_BobTime) { m_BobUp = !m_BobUp; m_BobTimer = 0.0f; } //check for keys if (m_State != PlayerState::Digging) { if (utils::isKeyDown(SDL_SCANCODE_W)) { // if (m_Grounded) { m_State = PlayerState::Flying; m_Vel.y = m_Speed; m_Grounded = false; // } } if (utils::isKeyPressed(SDL_SCANCODE_S)) { if (m_Grounded) { if (this->CanDig(Collision::Bottom, level)) { m_DigDirection = DigDirection::Down; m_currentAnimation = m_digStartAnimation; m_currentAnimation->Reset(); m_IsDiggingPrimed = false; this->Dig(Collision::CollisionDirection::Bottom, level); } } } if (utils::isKeyDown(SDL_SCANCODE_A)) { if (!m_IsTurning) { m_walkAnimation->SetFlipped(false); } m_Acc.x = -m_Speed; if (m_Direction == PlayerDirection::Right) { m_IsTurning = true; m_currentAnimation = m_turnAnimation; m_currentAnimation->SetFlipped(true); m_currentAnimation->Reset(); } m_Direction = PlayerDirection::Left; if (m_Grounded && !m_DidJustDigLeft) { //Check if the player doesnt come from digging a tile if (this->CanDig(Collision::CollisionDirection::Left, level)) { m_DigDirection = DigDirection::Left; this->Dig(Collision::CollisionDirection::Left, level); m_DidJustDigLeft = true; } } } if (m_DidJustDigLeft) { if (!utils::isKeyDown(SDL_SCANCODE_A)) { m_DidJustDigLeft = false; } } if (utils::isKeyDown(SDL_SCANCODE_D)) { if (!m_IsTurning) { m_walkAnimation->SetFlipped(true); } m_Acc.x = m_Speed; if (m_Direction == PlayerDirection::Left) { m_IsTurning = true; m_currentAnimation = m_turnAnimation; m_currentAnimation->SetFlipped(false); m_currentAnimation->Reset(); } m_Direction = PlayerDirection::Right; if (m_Grounded && !m_DidJustDigRight) { //Check if the player doesnt come from digging a tile if (this->CanDig(Collision::CollisionDirection::Right, level)) { m_DigDirection = DigDirection::Right; this->Dig(Collision::CollisionDirection::Right, level); m_DidJustDigRight = true; } } } if (m_DidJustDigRight) { if (!utils::isKeyDown(SDL_SCANCODE_D)) { m_DidJustDigRight = false; } } m_Vel = m_Vel + m_Gravity * elapsedTime; m_Vel = m_Vel + m_Acc * elapsedTime; m_Vel = utils::clamp(m_Vel, Vector2f { -m_Speed, -m_Speed }, Vector2f { m_Speed, m_Speed }); //air resistance //only if not moving if (abs(m_Acc.x) < 0.1f) { m_Vel.x = m_Vel.x * 0.90f; } m_Acc = Vector2f { 0, 0 }; m_currentAnimation->Update(elapsedTime); if (m_currentAnimation->IsDone() && m_IsTurning) { m_currentAnimation = m_walkAnimation; m_IsTurning = false; } if(m_currentAnimation == m_digStartAnimation) { } #pragma region Collision m_ContactMap[Collision::CollisionDirection::Top] = nullptr; m_ContactMap[Collision::CollisionDirection::Bottom] = nullptr; m_ContactMap[Collision::CollisionDirection::Left] = nullptr; m_ContactMap[Collision::CollisionDirection::Right] = nullptr; m_Grounded = false; float t = 0, min_t = INFINITY; Vector2f intersectionPoint, normal; std::vector> contactTimes {}; const WorldGridManager& gridManager = level.GetGridManager(); for (int x { 0 }; x < WORLD_WIDTH; ++x) { for (int y { 0 }; y < WORLD_HEIGHT; ++y) { WorldTile* tile = gridManager.GetTileAtIndex(x, y); if (*tile->GetTileType() != GroundTileTypeManager::GetInstance()->AIR) { tile->m_Hightlight = false; if (Collision::DynamicRectVsRect(this->GetCollisionRect(), elapsedTime, tile->GetCollisionRect().getCollisionRect(), intersectionPoint, normal, t)) { contactTimes.emplace_back(std::pair { x + y * WORLD_WIDTH, t }); } } } } std::sort(contactTimes.begin(), contactTimes.end(), [](const std::pair& a, const std::pair& b) { return a.second < b.second; }); for (std::pair contact_time : contactTimes) { int x = contact_time.first % WORLD_WIDTH; int y = contact_time.first / WORLD_WIDTH; WorldTile* world_tile = gridManager.GetTileAtIndex(x, y); const Vector2f WorldTilePos = world_tile->GetCollisionRect().getCollisionRect().pos; const Vector2f WorldTileSize = world_tile->GetCollisionRect().getCollisionRect().size; if (WorldTilePos.y + WorldTileSize.y > m_Position.y) { if (WorldTilePos.x + WorldTileSize.x > m_Position.x) { if (WorldTilePos.y + WorldTileSize.y / 2 > m_Position.y && m_Position.y + m_Size.y / 2 > WorldTilePos.y) { //Right of player m_ContactMap[Collision::CollisionDirection::Right] = world_tile; } } } if (WorldTilePos.y + WorldTileSize.y > m_Position.y) { if (WorldTilePos.x < m_Position.x + m_Size.x) { if (WorldTilePos.y + WorldTileSize.y / 2 > m_Position.y && m_Position.y + m_Size.y / 2 > WorldTilePos.y) { //Left of player m_ContactMap[Collision::CollisionDirection::Left] = world_tile; } } } //Below the player if (WorldTilePos.y + WorldTileSize.y <= m_Position.y) { if (WorldTilePos.x + WorldTileSize.x / 2 > m_Position.x && m_Position.x + m_Size.x / 2 > WorldTilePos.x) { m_ContactMap[Collision::CollisionDirection::Bottom] = world_tile; m_Grounded = true; world_tile->m_Hightlight = true; } } Collision::CollisionRect rect = world_tile->GetCollisionRect().getCollisionRect(); //TODO: fix this mess Collision::ResolvePlayerVsRect(*this, elapsedTime, &rect); } #pragma endregion if (m_State == PlayerState::Walking || m_State == PlayerState::Idle) { //Fix for when the state is JUST set to digging if (std::abs(m_Vel.x) < 0.1f) { m_State = PlayerState::Idle; } else { m_State = PlayerState::Walking; } } } switch (m_State) { case PlayerState::Idle: m_walkAnimation->SetPlaying(false); break; case PlayerState::Walking: m_walkAnimation->SetPlaying(true); break; case PlayerState::Digging: { // m_walkAnimation->SetPlaying(false); //Diganimation m_currentAnimation->Update(elapsedTime); if(m_currentAnimation->IsDone() && m_State == PlayerState::Digging && !m_IsDiggingPrimed) { m_IsDiggingPrimed = true; m_currentAnimation = m_digAnimation; } if(m_IsDiggingPrimed) { if (!m_Digging) { //TODO: fix for setting the start position m_Digging = true; m_DigStart = m_Position; m_Vel = Vector2f { 0, 0 }; } m_DigProgress += elapsedTime; //lerp to the destination float progress = utils::map(m_DigProgress, 0.0f, m_DigTime, 0.0f, 1.0f); std::cout << progress << '\n'; m_Position = utils::lerp(m_DigStart, m_DigDestination, progress); if (progress >= 0.5f && !m_HasDeletedTile) { m_DigTile->SetTileType(GroundTileTypeManager::GetInstance()->AIR); m_DigTile = nullptr; m_HasDeletedTile = true; } if (progress >= 1.0f) { m_State = PlayerState::Idle; m_currentAnimation = m_walkAnimation; m_HasDeletedTile = false; m_Digging = false; } } break; } default: break; } if (m_State != PlayerState::Digging) { m_Position = m_Position + m_Vel * elapsedTime; } }