#include "render/WorldRenderer.h" #include "render/loader/WorldLoader.h" #include "render/Renderer.h" #include "render/gui/imgui/imgui.h" #include "gui/imgui/imgui_internal.h" #include "window/Display.h" #include "game/client/ClientGame.h" #include "game/client/Client.h" #include "misc/Format.h" namespace td { namespace render { ImVec4 WorldRenderer::GetImGuiTeamColor(game::TeamColor color) { switch (color) { case td::game::TeamColor::None: break; case td::game::TeamColor::Red: return ImVec4(1, 0, 0, 1); case td::game::TeamColor::Blue: return ImVec4(0, 0, 1, 1); } return ImVec4(1, 1, 1, 1); } void WorldRenderer::LoadModels() { utils::LOGD("World Created !"); m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(m_World))); m_MobVao = std::make_unique(std::move(WorldLoader::LoadMobModel())); m_SelectTileVao = std::make_unique(std::move(WorldLoader::LoadTileSelectModel())); utils::LOGD(utils::format("Vertex Count : %u", m_WorldVao->GetVertexCount())); } WorldRenderer::WorldRenderer(game::World* world, client::ClientGame* client) : m_Client(client), m_Renderer(m_Client->GetRenderer()), m_World(world), m_Zoom(0.1) { m_Renderer->SetZoom(m_Zoom); m_Renderer->SetCamMovement({}); m_TowerPlacePopup = std::make_unique(m_Client->GetClient()); m_MobTooltip = std::make_unique(m_Client->GetClient()); m_CastleTooltip = std::make_unique(m_Client->GetClient()); m_Client->GetWorldClient().GetWorldNotifier().BindListener(this); } void WorldRenderer::UpdateCursorPos() { m_CursorPos = GetCursorWorldPos(); } void WorldRenderer::Update() { if (m_WorldVao == nullptr) return; ImGuiIO& io = ImGui::GetIO(); if (io.MouseDown[0] && !ImGui::IsAnyItemActive()) { ImVec2 mouseDelta = ImGui::GetIO().MouseDelta; const float relativeX = mouseDelta.x / (float)Display::GetWindowWidth() * 2; const float relativeY = mouseDelta.y / (float)Display::GetWindowHeight() * 2; MoveCam(relativeX, relativeY, Display::GetAspectRatio()); } if (io.MouseWheel != 0) { ChangeZoom(io.MouseWheel); } UpdateCursorPos(); if (ImGui::IsMouseClicked(0)) { if (!m_PopupOpened) { m_HoldCursorPos = { io.MousePos.x, io.MousePos.y }; } else { m_PopupOpened = false; } } if (ImGui::IsMouseDoubleClicked(1)) RemoveTower(); } void WorldRenderer::RemoveTower() { Vec2f cursorPos = GetCursorWorldPos(); game::TowerPtr clickedTower = m_World->GetTower(cursorPos); if (clickedTower != nullptr) { m_Client->GetClient()->RemoveTower(clickedTower->GetID()); } } void WorldRenderer::RenderWorld() const { m_Renderer->RenderVAO(*m_WorldVao); } void WorldRenderer::RenderMobs() const { for (game::MobPtr mob : m_World->GetMobList()) { Renderer::Model model; model.vao = m_MobVao.get(); model.positon = { mob->GetCenterX(), mob->GetCenterY() }; m_Renderer->RenderModel(model); } } void WorldRenderer::RenderTowers() const { if (!m_TowersCache.IsEmpty()) m_Renderer->RenderVAO(m_TowersCache.GetVertexArray()); } void WorldRenderer::RenderTileSelect() const { if (ImGui::IsAnyItemHovered()) return; if (m_MobTooltip->IsShown() || m_CastleTooltip->IsShown()) return; Renderer::Model tileSelectModel; tileSelectModel.vao = m_SelectTileVao.get(); tileSelectModel.positon = { std::floor(m_CursorPos.x), std::floor(m_CursorPos.y) }; m_Renderer->RenderModel(tileSelectModel); } void WorldRenderer::RenderPopups() { m_TowerPlacePopup->Render(); RenderTowerUpgradePopup(); } void WorldRenderer::Render() { if (m_WorldVao == nullptr) return; RenderWorld(); RenderMobs(); RenderTowers(); RenderTileSelect(); RenderTooltips(); RenderPopups(); DetectClick(); } WorldRenderer::~WorldRenderer() { } void WorldRenderer::RenderTooltips() const { RenderMobTooltip(); RenderCastleTooltip(); } void WorldRenderer::MoveCam(float relativeX, float relativeY, float aspectRatio) { if (m_WorldVao == nullptr) return; float movementX = -relativeX / m_Zoom * aspectRatio; float movementY = relativeY / m_Zoom; m_Renderer->SetCamMovement({ movementX, movementY }); } void WorldRenderer::ChangeZoom(float zoomStep) { if (m_WorldVao == nullptr) return; static float sensibility = 1.5f; if (zoomStep < 0) { m_Zoom /= -zoomStep * sensibility; } else { m_Zoom *= zoomStep * sensibility; } m_Renderer->SetZoom(m_Zoom); m_Renderer->SetCamMovement({}); } void WorldRenderer::Click() { const game::TowerPtr tower = m_Client->GetWorld().GetTower(GetClickWorldPos()); m_TowerPlacePopup->SetClickPos(GetClickWorldPos()); if (tower != nullptr) { // there is a tower here ImGui::OpenPopup("TowerUpgrade"); } else if (m_Client->GetWorld().CanPlaceLittleTower(GetClickWorldPos(), m_Client->GetPlayer()->GetID())) { ImGui::OpenPopup("TowerPlace"); } } void WorldRenderer::SetCamPos(float camX, float camY) { m_CamPos = { camX, camY }; m_Renderer->SetCamPos(m_CamPos); } void WorldRenderer::RenderTowerUpgradePopup() { if (ImGui::BeginPopup("TowerUpgrade")) { m_PopupOpened = true; game::TowerPtr tower = m_Client->GetWorld().GetTower(GetClickWorldPos()); if (tower == nullptr) { ImGui::EndPopup(); return; } ImGui::Text("Tower : %s", game::TowerFactory::GetTowerName(tower->GetType()).c_str()); for (int y = 0; y < 3; y++) { // path: 0 -> top 1 -> middle 2 -> bottom for (int x = 0; x < 4; x++) { // level: 1, 2, 3, 4 if (x > 0) ImGui::SameLine(); std::uint8_t currentLevel = x + 1; game::TowerPath currentPath = game::TowerPath(y); const game::TowerStats* towerStats = game::GetTowerStats(tower->GetType(), { currentLevel, currentPath }); game::TowerPath towerPath = tower->GetLevel().GetPath(); bool disabled = towerStats == nullptr; int towerLevel = tower->GetLevel().GetLevel(); bool alreadyUpgraded = currentLevel <= towerLevel; bool canUpgrade = (towerLevel + 1) == currentLevel; if (canUpgrade && towerPath != game::TowerPath::Base) { if (currentPath != towerPath) { canUpgrade = false; } } ImGui::PushID(x * 4 + y); if (disabled) { ImGui::BeginDisabled(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); ImGui::Button("", ImVec2(100, 100)); ImGui::PopStyleColor(); ImGui::EndDisabled(); } else if (alreadyUpgraded) { ImGui::BeginDisabled(); ImGui::Button("Already", ImVec2(100, 100)); ImGui::EndDisabled(); } else if (canUpgrade) { if (ImGui::Button("Upgrade", ImVec2(100, 100))) { m_Client->GetClient()->UpgradeTower(tower->GetID(), { currentLevel, currentPath }); } } else { ImGui::BeginDisabled(); ImGui::Button("Locked", ImVec2(100, 100)); ImGui::EndDisabled(); } ImGui::PopID(); } } ImGui::EndPopup(); } } void WorldRenderer::DetectClick() { ImGuiIO& io = ImGui::GetIO(); if (ImGui::IsMouseReleased(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemFocused()) { Vec2f cursorPos = { io.MousePos.x, io.MousePos.y }; if (cursorPos == m_HoldCursorPos) { m_LastClicked = m_HoldCursorPos; Click(); } } } void WorldRenderer::RenderMobTooltip() const { if (ImGui::IsAnyItemHovered()) return; DetectMobHovering(); m_MobTooltip->Render(); } void WorldRenderer::RenderCastleTooltip() const { if (ImGui::IsAnyItemHovered()) return; DetectCastleHovering(); m_CastleTooltip->Render(); } void WorldRenderer::DetectMobHovering() const { Vec2f cursorWorldPos = GetCursorWorldPos(); for (game::MobPtr mob : m_World->GetMobList()) { if (mob->CollidesWith({ cursorWorldPos.x, cursorWorldPos.y })) { m_MobTooltip->SetMob(mob.get()); return; } } m_MobTooltip->SetMob(nullptr); } void WorldRenderer::DetectCastleHovering() const { Vec2f cursorWorldPos = GetCursorWorldPos(); for (const game::Team& team : m_World->GetTeams()) { if (team.GetCastle().CollidesWith({ cursorWorldPos.x, cursorWorldPos.y })) { m_CastleTooltip->SetCastle(&team.GetCastle()); return; } } m_CastleTooltip->SetCastle(nullptr); } void WorldRenderer::OnTowerAdd(game::TowerPtr tower) { WorldLoader::RenderData RenderData = WorldLoader::LoadTowerModel(tower); m_TowersCache.AddData(tower->GetID(), RenderData.positions, RenderData.colors); m_TowersCache.UpdateVertexArray(); } void WorldRenderer::OnTowerRemove(game::TowerPtr tower) { m_TowersCache.RemoveData(tower->GetID()); m_TowersCache.UpdateVertexArray(); } Vec2f WorldRenderer::GetCursorWorldPos() const { ImGuiIO& io = ImGui::GetIO(); return m_Renderer->GetCursorWorldPos({ io.MousePos.x, io.MousePos.y }, Display::GetAspectRatio(), m_Zoom, Display::GetWindowWidth(), Display::GetWindowHeight()); } Vec2f WorldRenderer::GetClickWorldPos() const { return m_Renderer->GetCursorWorldPos(m_LastClicked, Display::GetAspectRatio(), m_Zoom, Display::GetWindowWidth(), Display::GetWindowHeight()); } } // namespace render } // namespace td