feat(editor): full screen startup, resizable splitters, responsive light presets grid, professional studio toolbar

This commit is contained in:
Artvasil09 2026-10-24 21:02:29 +03:00
parent 432fccfd60
commit d3e4efc39b
7 changed files with 321 additions and 165 deletions

Binary file not shown.

View file

@ -1535,6 +1535,98 @@ void EditorApp::HandleHotkeys() {
if (ImGui::IsKeyPressed(ImGuiKey_L, false)) currentTool = TOOL_LIGHT_SPAWN;
}
// -----------------------------------------------------------------------------
// Interactive Splitters (Unreal Engine / Unity style with <--> cursor)
// -----------------------------------------------------------------------------
static bool DrawSplitter(const char* id, float* width, bool isLeftEdge, float minWidth, float maxWidth, float thickness = 6.0f) {
ImVec2 screenPos = ImGui::GetCursorScreenPos();
float height = ImGui::GetContentRegionAvail().y;
if (height < 20.0f) height = 100.0f;
ImGui::PushID(id);
ImGui::InvisibleButton("##split", ImVec2(thickness, height));
bool hovered = ImGui::IsItemHovered();
bool active = ImGui::IsItemActive();
if (hovered || active) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
if (active) {
float mouseDelta = ImGui::GetIO().MouseDelta.x;
if (mouseDelta != 0.0f) {
float newW = isLeftEdge ? (*width - mouseDelta) : (*width + mouseDelta);
if (newW < minWidth) newW = minWidth;
if (newW > maxWidth) newW = maxWidth;
*width = newW;
}
}
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImU32 col = active ? IM_COL32(20, 150, 255, 255) :
hovered ? IM_COL32(70, 110, 180, 200) :
IM_COL32(36, 40, 48, 255);
drawList->AddRectFilled(screenPos, ImVec2(screenPos.x + thickness, screenPos.y + height), col);
// Center handle indicator (<--> grip)
float midY = screenPos.y + height * 0.5f;
float midX = screenPos.x + thickness * 0.5f;
if (hovered || active) {
drawList->AddLine(ImVec2(midX, midY - 18.0f), ImVec2(midX, midY + 18.0f), IM_COL32(255, 255, 255, 240), 2.0f);
} else {
drawList->AddCircleFilled(ImVec2(midX, midY - 6.0f), 1.5f, IM_COL32(110, 120, 140, 180));
drawList->AddCircleFilled(ImVec2(midX, midY), 1.5f, IM_COL32(110, 120, 140, 180));
drawList->AddCircleFilled(ImVec2(midX, midY + 6.0f), 1.5f, IM_COL32(110, 120, 140, 180));
}
ImGui::PopID();
return active;
}
static bool DrawRatioSplitter(const char* id, float* ratio, float totalWidth, float minRatio = 0.15f, float maxRatio = 0.85f, float thickness = 6.0f) {
ImVec2 screenPos = ImGui::GetCursorScreenPos();
float height = ImGui::GetContentRegionAvail().y;
if (height < 20.0f) height = 100.0f;
ImGui::PushID(id);
ImGui::InvisibleButton("##split_ratio", ImVec2(thickness, height));
bool hovered = ImGui::IsItemHovered();
bool active = ImGui::IsItemActive();
if (hovered || active) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
if (active && totalWidth > 10.0f) {
float mouseDelta = ImGui::GetIO().MouseDelta.x;
if (mouseDelta != 0.0f) {
float newRatio = *ratio + (mouseDelta / totalWidth);
if (newRatio < minRatio) newRatio = minRatio;
if (newRatio > maxRatio) newRatio = maxRatio;
*ratio = newRatio;
}
}
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImU32 col = active ? IM_COL32(20, 150, 255, 255) :
hovered ? IM_COL32(70, 110, 180, 200) :
IM_COL32(36, 40, 48, 255);
drawList->AddRectFilled(screenPos, ImVec2(screenPos.x + thickness, screenPos.y + height), col);
float midY = screenPos.y + height * 0.5f;
float midX = screenPos.x + thickness * 0.5f;
if (hovered || active) {
drawList->AddLine(ImVec2(midX, midY - 18.0f), ImVec2(midX, midY + 18.0f), IM_COL32(255, 255, 255, 240), 2.0f);
} else {
drawList->AddCircleFilled(ImVec2(midX, midY - 6.0f), 1.5f, IM_COL32(110, 120, 140, 180));
drawList->AddCircleFilled(ImVec2(midX, midY), 1.5f, IM_COL32(110, 120, 140, 180));
drawList->AddCircleFilled(ImVec2(midX, midY + 6.0f), 1.5f, IM_COL32(110, 120, 140, 180));
}
ImGui::PopID();
return active;
}
// -----------------------------------------------------------------------------
// UI Rendering
// -----------------------------------------------------------------------------
@ -1560,29 +1652,40 @@ void EditorApp::RenderUI() {
// Main layout
ImGuiViewport* vp = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(vp->Pos.x, vp->Pos.y + 60));
ImGui::SetNextWindowSize(ImVec2(vp->Size.x, vp->Size.y - 85));
ImGui::SetNextWindowPos(ImVec2(vp->Pos.x, vp->Pos.y + 64));
ImGui::SetNextWindowSize(ImVec2(vp->Size.x, vp->Size.y - 89));
ImGui::Begin("Workspace", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus);
// Columns: Left = Hierarchy & Tile Palette, Middle = Workspace Viewport/IDE, Right = Inspector
float leftWidth = 260.0f;
float rightWidth = 320.0f;
float canvasWidth = ImGui::GetContentRegionAvail().x - leftWidth - rightWidth - 20.0f;
float totalAvailX = ImGui::GetContentRegionAvail().x;
const float splitterW = 6.0f;
const float spacingX = ImGui::GetStyle().ItemSpacing.x;
float maxLeftW = (std::max)(180.0f, totalAvailX - rightPanelWidth - 320.0f);
if (leftPanelWidth > maxLeftW) leftPanelWidth = maxLeftW;
if (leftPanelWidth < 180.0f) leftPanelWidth = 180.0f;
float maxRightW = (std::max)(240.0f, totalAvailX - leftPanelWidth - 320.0f);
if (rightPanelWidth > maxRightW) rightPanelWidth = maxRightW;
if (rightPanelWidth < 240.0f) rightPanelWidth = 240.0f;
float canvasWidth = totalAvailX - leftPanelWidth - rightPanelWidth - (splitterW * 2.0f) - (spacingX * 4.0f);
if (canvasWidth < 200.0f) canvasWidth = 200.0f;
// Left Panel (Unity / Unreal Docked Tabs)
ImGui::BeginChild("LeftPanel", ImVec2(leftWidth, 0), true);
ImGui::BeginChild("LeftPanel", ImVec2(leftPanelWidth, 0), true);
if (ImGui::BeginTabBar("LeftPanelTabs", ImGuiTabBarFlags_None)) {
if (ImGui::BeginTabItem("🗂 Сцена")) {
if (ImGui::BeginTabItem("Сцена")) {
RenderHierarchy();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("🎨 Палитра")) {
if (ImGui::BeginTabItem("Палитра")) {
RenderTilePalette();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("📁 Файлы")) {
if (ImGui::BeginTabItem("Файлы")) {
ImGui::TextColored(ImVec4(0.2f, 0.8f, 1.0f, 1.0f), "Браузер ассетов");
ImGui::Separator();
RenderAssetManagerContents();
@ -1592,6 +1695,8 @@ void EditorApp::RenderUI() {
}
ImGui::EndChild();
ImGui::SameLine();
DrawSplitter("LeftSplitter", &leftPanelWidth, false, 180.0f, maxLeftW, splitterW);
ImGui::SameLine();
// Center Panel (Grid Canvas / 3D Scene Viewport / MASM32 IDE / Asset Browser)
@ -1616,15 +1721,15 @@ void EditorApp::RenderUI() {
ImGui::PopStyleColor(2);
};
ModeTabButton("🗺 2D Карта", 2);
ModeTabButton("2D Карта", 2);
ImGui::SameLine();
ModeTabButton("🎮 3D Сцена", 1);
ModeTabButton("3D Сцена", 1);
ImGui::SameLine();
ModeTabButton("🪟 2D + 3D", 0);
ModeTabButton("2D + 3D", 0);
ImGui::SameLine();
ModeTabButton("💻 Скрипты (IDE)", 3);
ModeTabButton("Код / IDE", 3);
ImGui::SameLine();
ModeTabButton("📦 Ассеты", 4);
ModeTabButton("Ассеты", 4);
if (viewportMode == 0 || viewportMode == 2) {
ImGui::SameLine();
@ -1647,19 +1752,36 @@ void EditorApp::RenderUI() {
} else {
float timelineHeight = showCutsceneTimeline ? (std::min)(340.0f, ImGui::GetContentRegionAvail().y * 0.48f) : 0.0f;
ImGui::BeginChild("CanvasPanel", ImVec2(0, showCutsceneTimeline ? -timelineHeight - 6 : 0), true);
float sceneWidth = viewportMode == 0 ? ImGui::GetContentRegionAvail().x * 0.5f : 0;
if (viewportMode != 2) {
ImGui::BeginChild("SceneViewport", ImVec2(sceneWidth,0), true);
float availCanvasW = ImGui::GetContentRegionAvail().x;
if (viewportMode == 0) {
float availForViews = availCanvasW - splitterW - (spacingX * 2.0f);
float sceneW = availForViews * centerSplitRatio;
if (sceneW < 120.0f) sceneW = 120.0f;
if (sceneW > availForViews - 120.0f) sceneW = availForViews - 120.0f;
ImGui::BeginChild("SceneViewport", ImVec2(sceneW, 0), true);
RenderSceneViewport();
ImGui::EndChild();
if (viewportMode == 0) ImGui::SameLine();
}
if (viewportMode != 1) {
ImGui::BeginChild("MapViewport", ImVec2(0,0), false);
ImGui::SameLine();
DrawRatioSplitter("CenterRatioSplitter", &centerSplitRatio, availForViews, 0.15f, 0.85f, splitterW);
ImGui::SameLine();
ImGui::BeginChild("MapViewport", ImVec2(0, 0), false);
RenderGridCanvas();
ImGui::EndChild();
} else if (viewportMode == 1) {
ImGui::BeginChild("SceneViewport", ImVec2(0, 0), true);
RenderSceneViewport();
ImGui::EndChild();
} else {
ImGui::BeginChild("MapViewport", ImVec2(0, 0), false);
RenderGridCanvas();
ImGui::EndChild();
}
ImGui::EndChild();
if (showCutsceneTimeline) {
ImGui::BeginChild("CutsceneTimeline", ImVec2(0, 0), true);
RenderCutsceneTimeline();
@ -1668,10 +1790,12 @@ void EditorApp::RenderUI() {
}
ImGui::EndChild();
ImGui::SameLine();
DrawSplitter("RightSplitter", &rightPanelWidth, true, 240.0f, maxRightW, splitterW);
ImGui::SameLine();
// Right Panel (Unity / Unreal Component Inspector)
ImGui::BeginChild("RightPanel", ImVec2(rightWidth, 0), true);
ImGui::BeginChild("RightPanel", ImVec2(rightPanelWidth, 0), true);
RenderInspector();
ImGui::EndChild();
@ -1811,168 +1935,177 @@ void EditorApp::RenderMenuBar() {
void EditorApp::RenderToolbar() {
ImGuiViewport* vp = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(vp->Pos.x, vp->Pos.y + 20));
ImGui::SetNextWindowSize(ImVec2(vp->Size.x, 38));
ImGui::SetNextWindowSize(ImVec2(vp->Size.x, 42));
ImGui::Begin("Toolbar", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
// Play / Stop Button
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(5.0f, 4.0f));
// 1. PLAY / STOP
if (!isEngineRunning) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.65f, 0.25f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20f, 0.80f, 0.30f, 1.0f));
if (ImGui::Button(" ▶ ЗАПУСК (F5) ", ImVec2(130, 24))) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.12f, 0.60f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.18f, 0.72f, 0.32f, 1.0f));
if (ImGui::Button(" ▶ Запуск (F5) ", ImVec2(110, 26))) {
LaunchPlaytest();
}
ImGui::PopStyleColor(2);
} else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.80f, 0.20f, 0.20f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.95f, 0.25f, 0.25f, 1.0f));
if (ImGui::Button(" ■ СТОП (F5) ", ImVec2(130, 24))) {
if (ImGui::Button(" ■ Стоп (F5) ", ImVec2(110, 26))) {
StopPlaytest();
}
ImGui::PopStyleColor(2);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Запустить игру в тестовом режиме (F5)");
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// Game Mode Quick Selector (Peaceful Adventure vs Action Shooter)
// GAME GENRE BADGE
if (project && project->gameMode == 0) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.12f, 0.45f, 0.40f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.18f, 0.58f, 0.50f, 1.0f));
if (ImGui::Button(" ☮ КВЕСТ / БЕЗ ОРУЖИЯ ")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.14f, 0.38f, 0.44f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20f, 0.48f, 0.55f, 1.0f));
if (ImGui::Button(" Квест (Без оружия) ", ImVec2(0, 26))) {
project->gameMode = 1;
project->showWeapons = true;
char module[MAX_PATH]{};
GetModuleFileNameA(nullptr, module, MAX_PATH);
std::string projPath = (fs::path(module).parent_path() / "project.eblanproj").string();
project->Save(projPath);
statusMessage = "Режим игры: ЭКШЕН / ШУТЕР (с оружием и стрельбой)";
statusMessage = "Режим игры переключён: Экшен / Шутер (с оружием)";
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Текущий режим: Мирное приключение / Квест. Нажмите для переключения на Экшен / Шутер.");
} else if (project) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.55f, 0.30f, 0.10f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.70f, 0.38f, 0.12f, 1.0f));
if (ImGui::Button(" ⚔ ЭКШЕН / С ОРУЖИЕМ ")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.55f, 0.30f, 0.12f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.68f, 0.38f, 0.16f, 1.0f));
if (ImGui::Button(" Экшен (С оружием) ", ImVec2(0, 26))) {
project->gameMode = 0;
project->showWeapons = false;
char module[MAX_PATH]{};
GetModuleFileNameA(nullptr, module, MAX_PATH);
std::string projPath = (fs::path(module).parent_path() / "project.eblanproj").string();
project->Save(projPath);
statusMessage = "Режим игры: КВЕСТ / МИРНЫЙ (без оружия)";
statusMessage = "Режим игры переключён: Мирное исследование / Квест (без оружия)";
}
ImGui::PopStyleColor(2);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Нажмите для быстрого переключения жанра игры (Квест без оружия / Шутер с оружием)");
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Текущий режим: Боевик / Шутер с арсеналом. Нажмите для переключения на Квест.");
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// Lighting Controls in Toolbar
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.80f, 0.50f, 0.05f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.95f, 0.65f, 0.10f, 1.0f));
if (ImGui::Button(" 💡 ЗАПЕЧЬ СВЕТ ")) {
// 2. TOOLS SELECTOR (Photoshop / Unreal style button group)
auto ToolButton = [&](const char* label, ToolMode mode, const char* tooltip) {
bool active = (currentTool == mode);
if (active) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.08f, 0.48f, 0.88f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.14f, 0.58f, 0.98f, 1.0f));
} else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.18f, 0.20f, 0.25f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.26f, 0.30f, 0.38f, 1.0f));
}
if (ImGui::Button(label, ImVec2(0, 26))) currentTool = mode;
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", tooltip);
ImGui::SameLine();
};
ToolButton(" [B] Кисть ", TOOL_BRUSH, "Кисть для рисования стен [B]");
ToolButton(" [E] Ластик ", TOOL_ERASER, "Ластик для очистки ячеек [E]");
ToolButton(" [F] Заливка ", TOOL_FILL, "Заливка замкнутой области стен [F]");
ToolButton(" [P] Игрок ", TOOL_PLAYER_SPAWN, "Точка старта игрока [P]");
ToolButton(" [S] Спрайт ", TOOL_SPRITE_SPAWN, "Размещение сущностей и спрайтов [S]");
ToolButton(" [L] Свет ", TOOL_LIGHT_SPAWN, "Размещение источников освещения [L]");
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// 3. LIGHTING SUBSYSTEM
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.80f, 0.48f, 0.05f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.95f, 0.60f, 0.10f, 1.0f));
if (ImGui::Button(" Запечь свет ", ImVec2(0, 26))) {
PushUndo(); BakeLighting();
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Запечь постоянное освещение (Lightmap) для всей карты с учетом стен и препятствий");
ImGui::SameLine();
const char* lModeBtn = (lightingMode == 0) ? " ☀ Свет: ВЫКЛ " :
(lightingMode == 1) ? " 🕯 Свет: ЗАПЕЧЁН " :
" ⚡ Свет: ДИНАМИКА ";
if (ImGui::Button(lModeBtn)) {
const char* lModeBtn = (lightingMode == 0) ? " Свет: Выкл " :
(lightingMode == 1) ? " Свет: Запечён " :
" Свет: Динамика ";
if (ImGui::Button(lModeBtn, ImVec2(0, 26))) {
PushUndo(); lightingMode = (lightingMode + 1) % 3;
statusMessage = (lightingMode == 0) ? "Режим света: ВЫКЛ (Wolf3D flat)" :
(lightingMode == 1) ? "Режим света: ЗАПЕЧЁННЫЙ (Lightmap)" :
"Режим света: ДИНАМИЧЕСКИЙ (Point lights + Flashlight)";
statusMessage = (lightingMode == 0) ? "Режим света: Выкл (Wolf3D fullbright)" :
(lightingMode == 1) ? "Режим света: Запечённый (Lightmap)" :
"Режим света: Динамический (Point lights + Flashlight)";
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Переключение режима освещения (Без света / Запечённый / Динамический)");
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Режим освещения: Без света / Запечённый / Динамический с тенями");
ImGui::SameLine();
if (isFlashlightOn) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.18f, 0.55f, 0.70f, 1.0f));
if (ImGui::Button(" 🔦 Фонарик: ВКЛ ")) {PushUndo();isFlashlightOn = false;}
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.14f, 0.50f, 0.68f, 1.0f));
if (ImGui::Button(" Фонарик [F] ", ImVec2(0, 26))) { PushUndo(); isFlashlightOn = false; }
ImGui::PopStyleColor();
} else {
if (ImGui::Button(" 🔦 Фонарик: ВЫКЛ ")) {PushUndo();isFlashlightOn = true;}
if (ImGui::Button(" Фонарик: Выкл ", ImVec2(0, 26))) { PushUndo(); isFlashlightOn = true; }
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Переключить фонарик игрока [F]");
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Включить/выключить фонарик игрока [F]");
ImGui::SameLine();
if (showLightHeatmap) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.70f, 0.40f, 0.10f, 1.0f));
if (ImGui::Button(" 🗺 Карта: ВКЛ ")) showLightHeatmap = false;
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.68f, 0.38f, 0.08f, 1.0f));
if (ImGui::Button(" Хитмап 2D ", ImVec2(0, 26))) showLightHeatmap = false;
ImGui::PopStyleColor();
} else {
if (ImGui::Button(" 🗺 Карта: ВЫКЛ ")) showLightHeatmap = true;
if (ImGui::Button(" Хитмап: Выкл ", ImVec2(0, 26))) showLightHeatmap = true;
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Отображать световую карту (Heatmap) на 2D сетке");
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Отображение тепловой карты освещения на 2D сетке");
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// Tools
auto ToolButton = [&](const char* label, ToolMode mode) {
bool active = (currentTool == mode);
if (active) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.25f, 0.45f, 0.75f, 1.0f));
if (ImGui::Button(label)) currentTool = mode;
if (active) ImGui::PopStyleColor();
ImGui::SameLine();
};
ToolButton(" [B] Кисть ", TOOL_BRUSH);
ToolButton(" [E] Ластик ", TOOL_ERASER);
ToolButton(" [F] Заливка ", TOOL_FILL);
ToolButton(" [P] Игрок ", TOOL_PLAYER_SPAWN);
ToolButton(" [S] Спрайт ", TOOL_SPRITE_SPAWN);
ToolButton(" [L] Свет ", TOOL_LIGHT_SPAWN);
// 4. STUDIO WINDOWS / MODULES
if (ImGui::Button(" Код (F7) ", ImVec2(0, 26))) showCodeEditorModal = !showCodeEditorModal;
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Открыть встроенную среду разработки скриптов MASM32 (F7)");
ImGui::SameLine();
if (ImGui::Button(" Кат-сцены (F6) ", ImVec2(0, 26))) showCutsceneTimeline = !showCutsceneTimeline;
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Открыть таймлайн кинематографических кат-сцен (F6)");
ImGui::SameLine();
if (ImGui::Button(" Ассеты (F4) ", ImVec2(0, 26))) showAssetManagerModal = !showAssetManagerModal;
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Менеджер текстур и аудиофайлов (F4)");
ImGui::SameLine();
if (ImGui::Button(" Экспорт (.exe) ", ImVec2(0, 26))) showExportGameModal = true;
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Экспорт готовой автономной игры (.exe)");
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// Undo / Redo buttons
if (ImGui::Button(" Отмена ")) Undo();
// 5. HISTORY & SAVE
if (ImGui::Button(" Отмена ", ImVec2(0, 26))) Undo();
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Отменить последнее действие (Ctrl+Z)");
ImGui::SameLine();
if (ImGui::Button(" Повтор ")) Redo();
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
if (ImGui::Button(" Повтор ", ImVec2(0, 26))) Redo();
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Повторить отменённое действие (Ctrl+Y)");
ImGui::SameLine();
if (ImGui::Button("Кат-сцена (F6)")) showCutsceneTimeline = !showCutsceneTimeline;
ImGui::SameLine();
// Asset Manager button
if (ImGui::Button(" 🖼 Ассеты (F4) ")) {
showAssetManagerModal = !showAssetManagerModal;
}
ImGui::SameLine();
// Code Editor button
if (ImGui::Button(" 💻 Код (F7) ")) {
showCodeEditorModal = !showCodeEditorModal;
}
ImGui::SameLine();
// Standalone Exporter button
if (ImGui::Button(" 📦 Экспорт (.exe) ")) {
showExportGameModal = true;
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
if (ImGui::Button(" Сохранить (Ctrl+S) ")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.18f, 0.44f, 0.70f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.24f, 0.54f, 0.84f, 1.0f));
if (ImGui::Button(" Сохранить (Ctrl+S) ", ImVec2(0, 26))) {
SaveLevel(currentFilePath);
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Сохранить уровень в файл (Ctrl+S)");
ImGui::PopStyleVar(2);
ImGui::End();
}
@ -2451,7 +2584,7 @@ void EditorApp::RenderHierarchy() {
ImGui::Separator();
// Search filter + Quick Add button (Unity / Unreal style)
ImGui::InputTextWithHint("##HierarchySearch", "🔍 Поиск...", searchHierarchyBuf, sizeof(searchHierarchyBuf));
ImGui::InputTextWithHint("##HierarchySearch", "Поиск...", searchHierarchyBuf, sizeof(searchHierarchyBuf));
ImGui::SameLine();
if (ImGui::Button("+ Добавить")) {
ImGui::OpenPopup("QuickEntitySpawnPopup");
@ -2483,14 +2616,14 @@ void EditorApp::RenderHierarchy() {
}
}
};
AddEntityItem("⚔ Враг (Guard AI)", ENTITY_ARCHETYPE_GUARD);
AddEntityItem("🩹 Аптечка (+25 HP)", ENTITY_ARCHETYPE_MEDKIT);
AddEntityItem("📦 Патроны (+20)", ENTITY_ARCHETYPE_AMMO);
AddEntityItem("🔑 Ключ от двери", ENTITY_ARCHETYPE_KEY);
AddEntityItem("🕹 Рычаг / Переключатель", ENTITY_ARCHETYPE_SWITCH);
AddEntityItem("💥 Взрывная бочка", ENTITY_ARCHETYPE_BARREL);
AddEntityItem("🏺 Декорация / Проп", ENTITY_ARCHETYPE_PROP);
if (ImGui::MenuItem("💡 Источник света [L]")) {
AddEntityItem("Враг (Guard AI)", ENTITY_ARCHETYPE_GUARD);
AddEntityItem("Аптечка (+25 HP)", ENTITY_ARCHETYPE_MEDKIT);
AddEntityItem("Патроны (+20)", ENTITY_ARCHETYPE_AMMO);
AddEntityItem("Ключ от двери", ENTITY_ARCHETYPE_KEY);
AddEntityItem("Рычаг / Переключатель", ENTITY_ARCHETYPE_SWITCH);
AddEntityItem("Взрывная бочка", ENTITY_ARCHETYPE_BARREL);
AddEntityItem("Декорация / Проп", ENTITY_ARCHETYPE_PROP);
if (ImGui::MenuItem("Источник света [L]")) {
currentTool = TOOL_LIGHT_SPAWN;
statusMessage = "Режим размещения света: кликните на карте для создания источника.";
}
@ -2510,7 +2643,7 @@ void EditorApp::RenderHierarchy() {
// 1. Player Spawn (Always at top of hierarchy)
char playerLabel[96];
snprintf(playerLabel, sizeof(playerLabel), "👤 Игрок [Spawn: (%d, %d)]", playerStartX, playerStartY);
snprintf(playerLabel, sizeof(playerLabel), "Игрок [Spawn: (%d, %d)]", playerStartX, playerStartY);
if (MatchesSearch(playerLabel) || MatchesSearch("player") || MatchesSearch("игрок")) {
bool isPlayerSelected = (selectedSpriteIdx == -1 && selectedTransitionIdx == -1 && selectedLightIdx == -1);
if (ImGui::Selectable(playerLabel, isPlayerSelected)) {
@ -2518,7 +2651,7 @@ void EditorApp::RenderHierarchy() {
selectedTransitionIdx = -1;
}
if (ImGui::BeginPopupContextItem("PlayerContextMenu")) {
if (ImGui::MenuItem("🎯 Сфокусировать камеру", "F")) {
if (ImGui::MenuItem("Сфокусировать камеру", "F")) {
FocusSelectedEntity();
}
ImGui::EndPopup();
@ -2561,16 +2694,16 @@ void EditorApp::RenderHierarchy() {
FocusSelectedEntity();
}
if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("📋 Дублировать", "Ctrl+D")) {
if (ImGui::MenuItem("Дублировать", "Ctrl+D")) {
selectedSpriteIdx = static_cast<int>(i); selectedLightIdx=-1;
DuplicateSelectedEntity();
}
if (ImGui::MenuItem("🎯 Сфокусировать", "F")) {
if (ImGui::MenuItem("Сфокусировать", "F")) {
selectedSpriteIdx = static_cast<int>(i); selectedLightIdx=-1;
FocusSelectedEntity();
}
ImGui::Separator();
if (ImGui::MenuItem("🗑 Удалить", "Del")) {
if (ImGui::MenuItem("Удалить", "Del")) {
selectedSpriteIdx = static_cast<int>(i); selectedLightIdx=-1;
DeleteSelectedEntity();
}
@ -2580,7 +2713,7 @@ void EditorApp::RenderHierarchy() {
// Enemies Category
char enemyHdr[64];
snprintf(enemyHdr, sizeof(enemyHdr), "⚔ Враги и Guard AI (%zu)", guardCount);
snprintf(enemyHdr, sizeof(enemyHdr), "Враги и Guard AI (%zu)", guardCount);
if (ImGui::TreeNodeEx(enemyHdr, ImGuiTreeNodeFlags_DefaultOpen)) {
for (size_t i = 0; i < sprites.size(); ++i) {
if (GetSpriteEntityType(sprites[i]) == ENTITY_ARCHETYPE_GUARD) RenderSpriteItem(i);
@ -2590,7 +2723,7 @@ void EditorApp::RenderHierarchy() {
// Pickups Category
char pickupHdr[64];
snprintf(pickupHdr, sizeof(pickupHdr), "📦 Лут и предметы (%zu)", pickupCount);
snprintf(pickupHdr, sizeof(pickupHdr), "Лут и предметы (%zu)", pickupCount);
if (ImGui::TreeNodeEx(pickupHdr, ImGuiTreeNodeFlags_DefaultOpen)) {
for (size_t i = 0; i < sprites.size(); ++i) {
uint8_t a = GetSpriteEntityType(sprites[i]);
@ -2601,7 +2734,7 @@ void EditorApp::RenderHierarchy() {
// Interactive & Props Category
char objHdr[64];
snprintf(objHdr, sizeof(objHdr), "⚙ Объекты и окружение (%zu)", objectCount);
snprintf(objHdr, sizeof(objHdr), "Объекты и окружение (%zu)", objectCount);
if (ImGui::TreeNodeEx(objHdr, ImGuiTreeNodeFlags_DefaultOpen)) {
for (size_t i = 0; i < sprites.size(); ++i) {
uint8_t a = GetSpriteEntityType(sprites[i]);
@ -2610,7 +2743,7 @@ void EditorApp::RenderHierarchy() {
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("💡 Источники света", ImGuiTreeNodeFlags_DefaultOpen)) {
if(ImGui::TreeNodeEx("Источники света", ImGuiTreeNodeFlags_DefaultOpen)) {
for(int i = 0; i < static_cast<int>(lightSources.size()); ++i) {
const auto& ls = lightSources[i];
const char* typeName = (ls.lightType == LIGHT_TYPE_STATIC) ? "Постоянный" :
@ -2631,19 +2764,19 @@ void EditorApp::RenderHierarchy() {
selectedSpriteIdx = selectedTransitionIdx = -1;
}
if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("🎯 Сфокусировать", "F")) {
if (ImGui::MenuItem("Сфокусировать", "F")) {
selectedLightIdx = i; selectedSpriteIdx = selectedTransitionIdx = -1;
FocusSelectedEntity();
}
if (ImGui::MenuItem("📋 Дублировать", "Ctrl+D")) {
if (ImGui::MenuItem("Дублировать", "Ctrl+D")) {
selectedLightIdx = i; selectedSpriteIdx = selectedTransitionIdx = -1;
DuplicateSelectedEntity();
}
if (ImGui::MenuItem("🧲 Выровнять по центру клетки")) {
if (ImGui::MenuItem("Выровнять по центру клетки")) {
SnapLightToGrid(i);
}
ImGui::Separator();
if (ImGui::MenuItem("🗑 Удалить источник", "Del")) {
if (ImGui::MenuItem("Удалить источник", "Del")) {
selectedLightIdx = i;
DeleteSelectedEntity();
}
@ -2659,7 +2792,7 @@ void EditorApp::RenderHierarchy() {
}
// 3. Sublevel Transitions Category
char transHdr[64];
snprintf(transHdr, sizeof(transHdr), "🌀 Порталы перехода (%zu)", transitions.size());
snprintf(transHdr, sizeof(transHdr), "Порталы перехода (%zu)", transitions.size());
if (ImGui::TreeNodeEx(transHdr, ImGuiTreeNodeFlags_DefaultOpen)) {
for (size_t i = 0; i < transitions.size(); ++i) {
char label[96];
@ -2675,13 +2808,13 @@ void EditorApp::RenderHierarchy() {
selectedSpriteIdx = -1;
}
if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("🎯 Сфокусировать", "F")) {
if (ImGui::MenuItem("Сфокусировать", "F")) {
selectedTransitionIdx = static_cast<int>(i); selectedLightIdx=-1;
canvasPanX = 150.0f - (transitions[i].triggerCellX * canvasZoom);
canvasPanY = 150.0f - (transitions[i].triggerCellY * canvasZoom);
}
ImGui::Separator();
if (ImGui::MenuItem("🗑 Удалить портал", "Del")) {
if (ImGui::MenuItem("Удалить портал", "Del")) {
selectedTransitionIdx = static_cast<int>(i); selectedLightIdx=-1;
DeleteSelectedEntity();
}
@ -2720,15 +2853,15 @@ void EditorApp::RenderInspector() {
// Entity Header Card (Unity / Unreal style)
ImGui::TextColored(ImVec4(0.4f, 0.9f, 1.0f, 1.0f), "Сущность #%d: %s", selectedSpriteIdx + 1, typeName);
if (ImGui::SmallButton("📋 Дублировать (Ctrl+D)")) { DuplicateSelectedEntity(); }
if (ImGui::SmallButton("Дублировать (Ctrl+D)")) { DuplicateSelectedEntity(); }
ImGui::SameLine();
if (ImGui::SmallButton("🎯 Фокус (F)")) { FocusSelectedEntity(); }
if (ImGui::SmallButton("Фокус (F)")) { FocusSelectedEntity(); }
ImGui::SameLine();
if (ImGui::SmallButton("🗑 Удалить (Del)")) { DeleteSelectedEntity(); return; }
if (ImGui::SmallButton("Удалить (Del)")) { DeleteSelectedEntity(); return; }
ImGui::Separator();
// Component 1: Transform
if (ImGui::CollapsingHeader("📍 Transform (Координаты и масштаб)", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::CollapsingHeader("Трансформ (Координаты и масштаб)", ImGuiTreeNodeFlags_DefaultOpen)) {
float sx = spr.x / 64.0f;
float sy = spr.y / 64.0f;
if (ImGui::SliderFloat("Позиция X", &sx, 0.0f, MAP_GRID_W - 0.01f, "%.2f ячеек")) {
@ -2746,7 +2879,7 @@ void EditorApp::RenderInspector() {
}
// Component 2: Sprite Renderer
if (ImGui::CollapsingHeader("🖼 Sprite Renderer (Текстура)", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::CollapsingHeader("Спрайт / Текстура", ImGuiTreeNodeFlags_DefaultOpen)) {
int tid = static_cast<int>(spr.textureId & 15);
if (ImGui::Combo("Слот текстуры", &tid, g_TexSlotNames, 16)) {
spr.textureId = static_cast<uint32_t>(tid);
@ -2763,7 +2896,7 @@ void EditorApp::RenderInspector() {
}
// Component 3: Gameplay & AI Controller
if (ImGui::CollapsingHeader("🧠 Gameplay & AI Controller", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::CollapsingHeader("Параметры сущности / AI", ImGuiTreeNodeFlags_DefaultOpen)) {
static const char* s_ArchetypeNames[7] = {
"0: Декорация / Проп (Static)",
"1: Враг (Enemy Guard AI)",
@ -2806,13 +2939,13 @@ void EditorApp::RenderInspector() {
ImGui::TextColored(ImVec4(1.0f, 0.45f, 0.15f, 1.0f), "● Взрывается от пуль, нанося 60 AOE урона вокруг!");
}
if (ImGui::Button("💻 Открыть логику в MASM32 IDE (F7)", ImVec2(-1, 28))) {
if (ImGui::Button("Открыть логику в MASM32 IDE (F7)", ImVec2(-1, 28))) {
viewportMode = 3;
}
}
// Component 4: Interaction & Lore Notes
if (ImGui::CollapsingHeader("💬 Интерактивный диалог / Записка", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::CollapsingHeader("Интерактивный диалог / Записка", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::TextDisabled("При взаимодействии [E] вызывается ShowGameDialog:");
static char szSpeaker[32] = "ПЕРСОНАЖ";
static char szDialog[128] = "ПРИВЕТСТВУЮ В EBLAN ENGINE 3D!";
@ -2830,31 +2963,31 @@ void EditorApp::RenderInspector() {
ImGui::OpenPopup("AddComponentPopup");
}
if (ImGui::BeginPopup("AddComponentPopup")) {
if (ImGui::MenuItem("🧠 Guard AI Controller")) {
if (ImGui::MenuItem("Guard AI Controller")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_GUARD, 60, 2, 6);
}
if (ImGui::MenuItem("🩹 Medkit Heal Component")) {
if (ImGui::MenuItem("Medkit Heal Component")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_MEDKIT, 100, 0, 0);
}
if (ImGui::MenuItem("📦 Ammo Pickup Component")) {
if (ImGui::MenuItem("Ammo Pickup Component")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_AMMO, 100, 0, 0);
}
if (ImGui::MenuItem("🔑 Keycard Door Component")) {
if (ImGui::MenuItem("Keycard Door Component")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_KEY, 100, 0, 0);
}
if (ImGui::MenuItem("🕹 Switch Trigger Component")) {
if (ImGui::MenuItem("Switch Trigger Component")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_SWITCH, 100, 0, 0);
}
if (ImGui::MenuItem("💥 Explosive Barrel Component")) {
if (ImGui::MenuItem("Explosive Barrel Component")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_BARREL, 100, 0, 0);
}
if (ImGui::MenuItem("🏺 Static Prop Decorator")) {
if (ImGui::MenuItem("Static Prop Decorator")) {
PushUndo();
SetSpriteEntityData(spr, ENTITY_ARCHETYPE_PROP, 100, 0, 0);
}
@ -2865,7 +2998,7 @@ void EditorApp::RenderInspector() {
// Case 2: Portal Selected
if (selectedTransitionIdx >= 0 && selectedTransitionIdx < static_cast<int>(transitions.size())) {
if (ImGui::CollapsingHeader("🌀 Портал перехода (Sublevel Portal)", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::CollapsingHeader("Портал перехода (Sublevel Portal)", ImGuiTreeNodeFlags_DefaultOpen)) {
auto& trans = transitions[selectedTransitionIdx];
ImGui::TextColored(ImVec4(0.7f, 0.4f, 1.0f, 1.0f), "Триггер-ячейка: (%d, %d)", trans.triggerCellX, trans.triggerCellY);

View file

@ -152,6 +152,11 @@ public:
bool LightingNeedsBake() const;
LightingSettings GetLightingSettings() const;
// Panel Dimensions & Splitters (Interactive Resizing like Unreal / Unity)
float leftPanelWidth = 260.0f;
float rightPanelWidth = 350.0f;
float centerSplitRatio = 0.5f;
// Viewport pan & zoom
float canvasZoom = 20.0f; // pixels per tile
float canvasPanX = 20.0f;

View file

@ -131,34 +131,52 @@ void EditorApp::RenderLightingInspector() {
(light.lightType == LIGHT_TYPE_PULSE) ? "Пульсирующий (Сирена)" :
(light.flags & LIGHT_FLAG_FLICKER) ? "Мерцающий факел" : "Точечный (Динамический)";
ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.25f, 1.0f), "💡 Источник света #%d из %d: %s",
ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.25f, 1.0f), "Источник света #%d из %d: %s",
selectedLightIdx + 1, static_cast<int>(lightSources.size()), typeDesc);
ImGui::Separator();
// Quick Action Bar (Unity / Unreal style)
if (ImGui::SmallButton("🎯 Фокус (F)")) { FocusSelectedEntity(); }
// Quick Action Bar (2x2 Grid)
float colW2 = (ImGui::GetContentRegionAvail().x - 6.0f) * 0.5f;
if (ImGui::Button("Фокус (F)", ImVec2(colW2, 24))) { FocusSelectedEntity(); }
ImGui::SameLine();
if (ImGui::SmallButton("📋 Дублировать (Ctrl+D)")) { DuplicateSelectedEntity(); }
if (ImGui::Button("Дублировать (Ctrl+D)", ImVec2(colW2, 24))) { DuplicateSelectedEntity(); }
if (ImGui::Button("К сетке (Snap)", ImVec2(colW2, 24))) { SnapLightToGrid(selectedLightIdx); }
ImGui::SameLine();
if (ImGui::SmallButton("🧲 К сетке (Snap)")) { SnapLightToGrid(selectedLightIdx); }
ImGui::SameLine();
if (ImGui::SmallButton("🗑 Удалить (Del)")) { DeleteSelectedEntity(); return; }
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.55f, 0.15f, 0.15f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.75f, 0.20f, 0.20f, 1.0f));
if (ImGui::Button("Удалить (Del)", ImVec2(colW2, 24))) {
ImGui::PopStyleColor(2);
DeleteSelectedEntity();
return;
}
ImGui::PopStyleColor(2);
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.4f, 0.9f, 1.0f, 1.0f), "Быстрые пресеты:");
ImGui::TextColored(ImVec4(0.4f, 0.85f, 1.0f, 1.0f), "Быстрые пресеты источников:");
// Preset Buttons
if (ImGui::SmallButton("🕯 Факел")) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_TORCH);
// Preset Buttons: Clean 3-Column Grid (all 7 presets visible!)
float colW3 = (ImGui::GetContentRegionAvail().x - 12.0f) / 3.0f;
if (ImGui::Button("Факел", ImVec2(colW3, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_TORCH);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Факел: Тёплый огонь (оранжевый, 240px, мерцание, тени)");
ImGui::SameLine();
if (ImGui::SmallButton("💡 Лампа")) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_LAMP);
if (ImGui::Button("Лампа", ImVec2(colW3, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_LAMP);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Лампа: Запекаемый дневной свет (280px, статика)");
ImGui::SameLine();
if (ImGui::SmallButton("🚨 Сирена")) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_ALARM);
if (ImGui::Button("Сирена", ImVec2(colW3, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_ALARM);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Сирена: Аварийный маяк (красный, 300px, пульсация)");
if (ImGui::Button("Свеча", ImVec2(colW3, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_CANDLE);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Свеча: Мягкий тёплый огонёк (140px, мерцание)");
ImGui::SameLine();
if (ImGui::SmallButton("🕯 Свеча")) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_CANDLE);
if (ImGui::Button("Неон", ImVec2(colW3, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_NEON);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Неон: Циановый неоновый свет (260px, динамика)");
ImGui::SameLine();
if (ImGui::SmallButton("🔮 Неон")) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_NEON);
ImGui::SameLine();
if (ImGui::SmallButton("🔦 Прожектор")) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_SPOTLIGHT);
if (ImGui::Button("Токсик", ImVec2(colW3, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_TOXIC);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Токсик: Зелёное радиационное свечение (250px, пульс)");
if (ImGui::Button("Прожектор (Направленный)", ImVec2(-1, 24))) ApplyLightPreset(selectedLightIdx, LIGHT_PRESET_SPOTLIGHT);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Прожектор: Мощный белый свет (380px, статика)");
ImGui::Separator();

View file

@ -95,7 +95,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
return 1;
}
ShowWindow(hWnd, SW_SHOWDEFAULT);
ShowWindow(hWnd, SW_MAXIMIZE);
UpdateWindow(hWnd);
DragAcceptFiles(hWnd, TRUE);

Binary file not shown.

BIN
main.obj

Binary file not shown.