#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 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() { std::cout << "World Created !\n"; 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())); std::cout << "Vertex Count : " << m_WorldVao->getVertexCount() << std::endl; } 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() { glm::vec2 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 = { (int)m_CursorPos.x, (int)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()) { glm::vec2 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 { glm::vec2 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 { glm::vec2 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(); } glm::vec2 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()); } glm::vec2 WorldRenderer::getClickWorldPos() const { return m_Renderer->getCursorWorldPos(m_LastClicked, Display::getAspectRatio(), m_Zoom, Display::getWindowWidth(), Display::getWindowHeight()); } } // namespace render } // namespace td