colobot/colobot-base/ui/debug_menu.cpp

491 lines
14 KiB
C++

/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "ui/debug_menu.h"
#include "app/app.h"
#include "common/event.h"
#include "common/stringutils.h"
#include "graphics/engine/engine.h"
#include "graphics/engine/lightning.h"
#include "graphics/engine/terrain.h"
#include "level/robotmain.h"
#include "object/object.h"
#include "object/object_manager.h"
#include "sound/sound.h"
#include "ui/controls/button.h"
#include "ui/controls/check.h"
#include "ui/controls/interface.h"
#include "ui/controls/label.h"
#include "ui/controls/window.h"
#include <SDL_clipboard.h>
namespace Ui
{
CDebugMenu::CDebugMenu(CRobotMain* main, Gfx::CEngine* engine, CObjectManager* objMan, CSoundInterface* sound)
{
m_main = main;
m_interface = m_main->GetInterface();
m_engine = engine;
m_objMan = objMan;
m_sound = sound;
}
CDebugMenu::~CDebugMenu()
{
}
void CDebugMenu::ToggleInterface()
{
if (!IsActive())
{
CreateInterface();
CLabel* pl = m_interface->CreateLabel({ 0.0f, 0.9f }, { 1.0f, 0.1f }, -1, EVENT_LABEL19, "??");
pl->SetFontType(Gfx::FONT_STUDIO);
}
else
{
m_interface->DeleteControl(EVENT_LABEL19);
DestroyInterface();
}
}
constexpr glm::vec2 dim = { 33.0f / 640.0f, 33.0f / 480.0f };
constexpr float ox = 3.0f/640.0f, oy = 3.0f/480.0f;
constexpr float /*sx = 33.0f/640.0f,*/ sy = 33.0f/480.0f;
void CDebugMenu::CreateInterface()
{
CWindow* pw = m_interface->CreateWindows({ 0, 0 }, { 0, 0 }, 0, EVENT_WINDOW7);
glm::vec2 pos, ddim;
CCheck* pc;
CButton* pb;
ddim.x = 4*dim.x+4*ox;
ddim.y = 222.0f/480.0f;
pos.x = 1.0f-ddim.x;
pos.y = oy+sy*3.0f;
pw->CreateGroup(pos, ddim, 6, EVENT_WINDOW7);
ddim.x = ddim.x - 4*ox;
ddim.y = dim.y*0.5f;
pos.x += 2*ox;
pos.y = oy+sy*9.0f;
pb = pw->CreateButton(pos, ddim, -1, EVENT_DBG_SPAWN_OBJ);
pb->SetName("Spawn object");
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_DBG_TELEPORT);
pb->SetName("Teleport");
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_DBG_LIGHTNING);
pb->SetName("Lightning");
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_DBG_STATS);
pc->SetName("Display stats");
pos.y -= 0.048f;
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_DBG_RESOURCES);
pc->SetName("Underground resources");
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_DBG_GOTO);
pc->SetName("Render goto() path");
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_DBG_CRASHSPHERES);
pc->SetName("Render crash spheres");
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_DBG_LIGHTS);
pc->SetName("Render dynamic lights");
pos.y -= 0.048f;
pb = pw->CreateButton(pos, ddim, -1, EVENT_DBG_LIGHTS_DUMP);
pb->SetName("Dump lights to log");
UpdateInterface();
}
void CDebugMenu::CreateSpawnInterface()
{
CWindow* pw = m_interface->CreateWindows({ 0, 0 }, { 0, 0 }, 0, EVENT_WINDOW7);
glm::vec2 pos, ddim;
CButton* pb;
ddim.x = 4*dim.x+4*ox;
ddim.y = 222.0f/480.0f;
pos.x = 1.0f-ddim.x;
pos.y = oy+sy*3.0f;
pw->CreateGroup(pos, ddim, 6, EVENT_WINDOW7);
ddim.x = ddim.x - 4*ox;
ddim.y = dim.y*0.5f;
pos.x += 2*ox;
pos.y = oy+sy*9.0f;
pb = pw->CreateButton(pos, ddim, -1, EVENT_SPAWN_CANCEL);
pb->SetName("Cancel");
pos.y -= ddim.y;
pos.y -= dim.y;
pw->CreateButton(pos, dim, 128+8, EVENT_SPAWN_ME);
pos.x += dim.x;
pw->CreateButton(pos, dim, 128+9, EVENT_SPAWN_WHEELEDGRABBER);
pos.x += dim.x;
pw->CreateButton(pos, dim, 128+15, EVENT_SPAWN_WHEELEDSHOOTER);
pos.x += dim.x;
pw->CreateButton(pos, dim, 128+19, EVENT_SPAWN_PHAZERSHOOTER);
pos.x -= 3*dim.x;
pos.y -= dim.y;
pw->CreateButton(pos, dim, 128+32, EVENT_SPAWN_BOTFACTORY);
pos.x += dim.x;
pw->CreateButton(pos, dim, 128+34, EVENT_SPAWN_CONVERTER);
pos.x += dim.x;
pw->CreateButton(pos, dim, 128+33, EVENT_SPAWN_DERRICK);
pos.x += dim.x;
pw->CreateButton(pos, dim, 128+36, EVENT_SPAWN_POWERSTATION);
pos.x -= 3*dim.x;
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_SPAWN_TITANIUM);
pb->SetName("Titanium");
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_SPAWN_TITANIUMORE);
pb->SetName("TitaniumOre");
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_SPAWN_URANIUMORE);
pb->SetName("UraniumOre");
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_SPAWN_POWERCELL);
pb->SetName("PowerCell");
pos.y -= ddim.y;
pb = pw->CreateButton(pos, ddim, -1, EVENT_SPAWN_NUCLEARCELL);
pb->SetName("NuclearCell");
}
const std::map<EventType, ObjectType> SPAWN_TYPES = {
{EVENT_SPAWN_ME, OBJECT_HUMAN},
{EVENT_SPAWN_WHEELEDGRABBER, OBJECT_MOBILEwa},
{EVENT_SPAWN_WHEELEDSHOOTER, OBJECT_MOBILEwc},
{EVENT_SPAWN_PHAZERSHOOTER, OBJECT_MOBILErc},
{EVENT_SPAWN_BOTFACTORY, OBJECT_FACTORY},
{EVENT_SPAWN_CONVERTER, OBJECT_CONVERT},
{EVENT_SPAWN_DERRICK, OBJECT_DERRICK},
{EVENT_SPAWN_POWERSTATION, OBJECT_STATION},
{EVENT_SPAWN_TITANIUM, OBJECT_METAL},
{EVENT_SPAWN_TITANIUMORE, OBJECT_STONE},
{EVENT_SPAWN_URANIUMORE, OBJECT_URANIUM},
{EVENT_SPAWN_POWERCELL, OBJECT_POWER},
{EVENT_SPAWN_NUCLEARCELL, OBJECT_ATOMIC},
};
void CDebugMenu::UpdateInterface()
{
CCheck* pc;
CButton* pb;
CWindow* pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW7));
if (pw == nullptr) return;
pb = static_cast<CButton*>(pw->SearchControl(EVENT_DBG_LIGHTNING));
if (pb != nullptr)
{
pb->SetName(m_lightningActive ? "Disable lightning" : "Lightning");
}
pb = static_cast<CButton*>(pw->SearchControl(EVENT_DBG_TELEPORT));
if (pb != nullptr)
{
pb->SetName(m_teleportActive ? "Abort teleport" : "Teleport");
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_DBG_STATS));
if (pc != nullptr)
{
pc->SetState(STATE_CHECK, m_engine->GetShowStats());
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_DBG_RESOURCES));
if (pc != nullptr)
{
pc->SetState(STATE_CHECK, m_engine->GetDebugResources());
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_DBG_GOTO));
if (pc != nullptr)
{
pc->SetState(STATE_CHECK, m_engine->GetDebugGoto());
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_DBG_CRASHSPHERES));
if (pc != nullptr)
{
pc->SetState(STATE_CHECK, m_main->GetDebugCrashSpheres());
}
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_DBG_LIGHTS));
if (pc != nullptr)
{
pc->SetState(STATE_CHECK, m_engine->GetDebugLights());
}
for (const auto& it : SPAWN_TYPES)
{
pb = static_cast<CButton*>(pw->SearchControl(it.first));
if (pb != nullptr)
{
pb->SetState(STATE_ENABLE, it.second != m_spawningType);
}
}
}
void CDebugMenu::DestroyInterface()
{
m_interface->DeleteControl(EVENT_WINDOW7);
m_spawningType = OBJECT_NULL;
}
bool CDebugMenu::EventProcess(const Event &event)
{
switch (event.type)
{
case EVENT_DBG_STATS:
m_engine->SetShowStats(!m_engine->GetShowStats());
UpdateInterface();
break;
case EVENT_DBG_SPAWN_OBJ:
DestroyInterface();
CreateSpawnInterface();
break;
case EVENT_DBG_TELEPORT:
if (!m_teleportActive)
{
if (m_main->GetSelect() != nullptr)
m_teleportActive = true;
else
m_sound->Play(SOUND_CLICK);
}
else
{
m_teleportActive = false;
}
UpdateInterface();
break;
case EVENT_DBG_LIGHTNING:
m_lightningActive = !m_lightningActive;
UpdateInterface();
break;
case EVENT_DBG_RESOURCES:
m_engine->SetDebugResources(!m_engine->GetDebugResources());
UpdateInterface();
break;
case EVENT_DBG_GOTO:
m_engine->SetDebugGoto(!m_engine->GetDebugGoto());
UpdateInterface();
break;
case EVENT_DBG_CRASHSPHERES:
m_main->SetDebugCrashSpheres(!m_main->GetDebugCrashSpheres());
UpdateInterface();
break;
case EVENT_DBG_LIGHTS:
m_engine->SetDebugLights(!m_engine->GetDebugLights());
UpdateInterface();
break;
case EVENT_DBG_LIGHTS_DUMP:
m_engine->DebugDumpLights();
break;
case EVENT_SPAWN_CANCEL:
DestroyInterface();
CreateInterface();
break;
case EVENT_SPAWN_ME:
case EVENT_SPAWN_WHEELEDGRABBER:
case EVENT_SPAWN_WHEELEDSHOOTER:
case EVENT_SPAWN_PHAZERSHOOTER:
case EVENT_SPAWN_BOTFACTORY:
case EVENT_SPAWN_CONVERTER:
case EVENT_SPAWN_DERRICK:
case EVENT_SPAWN_POWERSTATION:
case EVENT_SPAWN_TITANIUM:
case EVENT_SPAWN_TITANIUMORE:
case EVENT_SPAWN_URANIUMORE:
case EVENT_SPAWN_POWERCELL:
case EVENT_SPAWN_NUCLEARCELL:
m_spawningType = SPAWN_TYPES.at(event.type);
UpdateInterface();
break;
case EVENT_MOUSE_BUTTON_DOWN:
if (event.GetData<MouseButtonEventData>()->button == MOUSE_BUTTON_LEFT)
{
if (m_lightningActive)
{
return !HandleLightning(event.mousePos);
}
if (m_teleportActive)
{
return !HandleTeleport(event.mousePos);
}
if (m_spawningType != OBJECT_NULL)
{
return !HandleSpawnObject(m_spawningType, event.mousePos);
}
}
break;
case EVENT_MOUSE_MOVE:
if (m_spawningType != OBJECT_NULL || m_teleportActive || m_lightningActive)
{
return false;
}
break;
case EVENT_FRAME:
HandleFrameUpdate(event);
break;
case EVENT_KEY_DOWN:
if (event.GetData<KeyEventData>()->key == KEY(c) && (event.kmodState & KMOD_CTRL) != 0)
{
if (IsActive())
{
return !HandleCopy(event.mousePos);
}
}
default:
break;
}
return true;
}
bool CDebugMenu::HandleSpawnObject(ObjectType type, const glm::vec2& mousePos)
{
glm::vec3 pos;
if (m_engine->DetectObject(mousePos, pos, true) == -1)
{
m_sound->Play(SOUND_CLICK, 1.0f, 0.5f);
return false;
}
ObjectCreateParams params;
params.pos = pos;
params.type = type;
params.power = 100.0f;
m_objMan->CreateObject(params);
// Update shortcuts in the upper-left corner
m_main->CreateShortcuts();
m_sound->Play(SOUND_RADAR, 2.0f, 1.5f);
return true;
}
bool CDebugMenu::HandleLightning(const glm::vec2& mousePos)
{
glm::vec3 pos;
if (m_engine->DetectObject(mousePos, pos, true) == -1)
{
m_sound->Play(SOUND_CLICK, 1.0f, 0.5f);
return false;
}
m_engine->GetLightning()->StrikeAtPos(pos);
return true;
}
bool CDebugMenu::HandleTeleport(const glm::vec2& mousePos)
{
CObject* select = m_main->GetSelect();
glm::vec3 pos;
if (m_engine->DetectObject(mousePos, pos, true) == -1 || !m_engine->GetTerrain()->AdjustToFloor(pos) || select == nullptr)
{
m_sound->Play(SOUND_CLICK, 1.0f, 0.5f);
m_teleportActive = false;
UpdateInterface();
return false;
}
select->SetPosition(pos);
m_sound->Play(SOUND_BUILD, 4.0f, 0.75f);
m_sound->Play(SOUND_BUILD, pos, 4.0f, 0.75f);
m_teleportActive = false;
UpdateInterface();
return true;
}
void CDebugMenu::HandleFrameUpdate(const Event &event)
{
std::string str = "-";
glm::vec3 pos;
int obj;
if ((obj = m_engine->DetectObject(event.mousePos, pos, true)) != -1)
str = StrUtils::Format("pos=% 3.2f; % 3.2f height=% 3.2f objId=% 4d", pos.x, pos.z, pos.y, obj);
CLabel* pl = static_cast<CLabel*>(m_interface->SearchControl(EVENT_LABEL19));
if (pl == nullptr) return;
pl->SetName(str.c_str());
}
bool CDebugMenu::HandleCopy(const glm::vec2& mousePos)
{
glm::vec3 pos;
if (m_engine->DetectObject(mousePos, pos, true) == -1)
{
m_sound->Play(SOUND_CLICK, 1.0f, 0.5f);
return false;
}
std::string str = StrUtils::Format("pos=%.2f;%.2f", pos.x, pos.z);
GetLogger()->Debug("%s\n", str.c_str());
SDL_SetClipboardText(str.c_str());
return true;
}
bool CDebugMenu::IsActive()
{
return m_interface->SearchControl(EVENT_WINDOW7) != nullptr;
}
}