diff --git a/AGENTS.md b/AGENTS.md index 431170a..6fdc049 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -259,3 +259,16 @@ EblanStudio has evolved from a pure level editor into a **Full Game Engine Studi - `Game_OnCustomHUD`: custom 2D canvas drawing (health bars, gauges, quest markers). - `PlayGameSFX(pszRelativePath)`: safe asynchronous WinMM audio player. - Built-in automatic gameplay toasts for keys, medkits, ammo, switches, doors, and explosions. + +6. **Game Genres, Peaceful Adventure Mode & Immersive Clean HUD**: + - `GAME_MODE_ADVENTURE (0)`: pure exploration / quest / horror / puzzle mode without weapons, ammo, crosshair, or combat overlays. + - `GAME_MODE_ACTION (1)`: full combat shooter mode with weapon models, crosshair, health bar, and ammo counter. + - `HUD_STYLE_CLEAN (0)`: 100% immersive full-screen view (no debug banners, no text collisions, no bottom controls clutter). + - `HUD_STYLE_DEBUG (1)`: developer mode displaying FPS counter at top-right (X=250), engine title, score, and controls hints. + - Instant in-game switching: + - `[ESC] -> [2]`: toggle weapons / peaceful mode on the fly with toast notifications. + - `[ESC] -> [3]`: toggle clean vs debug HUD. + - `[F1]` or `[H]`: quick hotkey to toggle clean HUD vs debug HUD. + - Editor toolbar 1-click genre switcher: `☮ КВЕСТ / БЕЗ ОРУЖИЯ` vs `⚔ ЭКШЕН / С ОРУЖИЕМ`. + - Project settings (`project.eblanproj`) persist `GameMode`, `ShowWeapons`, and `ShowCrosshair`. + diff --git a/editor/editor.exe b/editor/editor.exe index af254ee..215fd83 100644 Binary files a/editor/editor.exe and b/editor/editor.exe differ diff --git a/editor/imgui.ini b/editor/imgui.ini index d7548d4..5f4c428 100644 --- a/editor/imgui.ini +++ b/editor/imgui.ini @@ -1,22 +1,22 @@ [Window][Workspace] Pos=0,60 Size=2560,1292 -LastUsed=20261022 +LastUsed=20261023 [Window][Debug##Default] Pos=60,60 Size=400,400 -LastUsed=20261022 +LastUsed=20261023 [Window][Toolbar] Pos=0,20 Size=2560,38 -LastUsed=20261022 +LastUsed=20261023 [Window][StatusBar] Pos=0,1352 Size=2560,32 -LastUsed=20261022 +LastUsed=20261023 [Window][Не удалось запустить игру] Pos=649,356 @@ -29,7 +29,12 @@ Size=900,650 LastUsed=20261022 [Window][Менеджер ассетов (Texture & Audio Manager)] -Pos=60,60 +Pos=61,60 Size=820,580 -LastUsed=20261022 +LastUsed=20261023 + +[Window][Редактор кода MASM32 & Игровых скриптов (F7)] +Pos=60,60 +Size=1020,680 +LastUsed=20261023 diff --git a/editor/src/editor_app.cpp b/editor/src/editor_app.cpp index 6ff8635..c386fa3 100644 --- a/editor/src/editor_app.cpp +++ b/editor/src/editor_app.cpp @@ -255,6 +255,12 @@ bool EditorApp::PreparePlaytest(std::string& startMap, std::string& enginePath) if (ec) return copyFailure(); if (fs::is_directory(sounds, ec)) fs::copy(sounds, sandbox / "assets/sounds", fs::copy_options::recursive, ec); if (ec) return copyFailure(); + char modulePath[MAX_PATH]{}; + GetModuleFileNameA(nullptr, modulePath, MAX_PATH); + fs::path projFile = fs::path(modulePath).parent_path() / "project.eblanproj"; + if (fs::exists(projFile, ec)) { + fs::copy_file(projFile, sandbox / "project.eblanproj", fs::copy_options::overwrite_existing, ec); + } // Validate all reachable maps and destination spawns, including reverse links. std::vector pending{source.filename().string()}; std::set visited; @@ -1617,6 +1623,42 @@ void EditorApp::RenderToolbar() { ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); + // Game Mode Quick Selector (Peaceful Adventure vs Action Shooter) + 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(" ☮ КВЕСТ / БЕЗ ОРУЖИЯ ")) { + 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 = "Режим игры: ЭКШЕН / ШУТЕР (с оружием и стрельбой)"; + } + ImGui::PopStyleColor(2); + } 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(" ⚔ ЭКШЕН / С ОРУЖИЕМ ")) { + 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 = "Режим игры: КВЕСТ / МИРНЫЙ (без оружия)"; + } + ImGui::PopStyleColor(2); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Нажмите для быстрого переключения жанра игры (Квест без оружия / Шутер с оружием)"); + } + + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + ImGui::SameLine(); + // Tools auto ToolButton = [&](const char* label, ToolMode mode) { bool active = (currentTool == mode); @@ -2292,12 +2334,18 @@ void EditorApp::RenderProjectSettingsModal() { static char authorBuf[128] = ""; static char versionBuf[32] = ""; static char windowTitleBuf[128] = ""; + static int modeChoice = 1; + static bool showWeap = true; + static bool showCross = true; static bool init = true; if (init) { strncpy_s(titleBuf, project->title.c_str(), sizeof(titleBuf) - 1); strncpy_s(authorBuf, project->author.c_str(), sizeof(authorBuf) - 1); strncpy_s(versionBuf, project->version.c_str(), sizeof(versionBuf) - 1); strncpy_s(windowTitleBuf, project->windowTitle.c_str(), sizeof(windowTitleBuf) - 1); + modeChoice = project->gameMode; + showWeap = project->showWeapons; + showCross = project->showCrosshair; init = false; } @@ -2309,6 +2357,20 @@ void EditorApp::RenderProjectSettingsModal() { ImGui::InputText("Версия", versionBuf, sizeof(versionBuf)); ImGui::InputText("Заголовок окна", windowTitleBuf, sizeof(windowTitleBuf)); + ImGui::Spacing(); + ImGui::TextColored(ImVec4(0.2f, 0.8f, 1.0f, 1.0f), "Жанр и режим геймплея:"); + ImGui::RadioButton("Квест / Приключение / Хоррор (Мирный режим, без оружия)", &modeChoice, 0); + ImGui::RadioButton("Экшен / Шутер (С оружием, патронами и перестрелками)", &modeChoice, 1); + + if (modeChoice == 1) { + ImGui::Checkbox("Отображать оружие от 1-го лица", &showWeap); + ImGui::Checkbox("Отображать перекрестие прицела", &showCross); + } else { + showWeap = false; + showCross = false; + ImGui::TextDisabled("В мирном режиме оружие, прицел и счетчик патронов скрыты."); + } + ImGui::Spacing(); ImGui::Text("Стартовая карта: %s", project->startupMap.c_str()); @@ -2318,6 +2380,9 @@ void EditorApp::RenderProjectSettingsModal() { project->author = authorBuf; project->version = versionBuf; project->windowTitle = windowTitleBuf; + project->gameMode = modeChoice; + project->showWeapons = showWeap; + project->showCrosshair = showCross; char module[MAX_PATH]{}; GetModuleFileNameA(nullptr, module, MAX_PATH); diff --git a/editor/src/project_manager.h b/editor/src/project_manager.h index e91e9d0..70b2ba5 100644 --- a/editor/src/project_manager.h +++ b/editor/src/project_manager.h @@ -16,6 +16,9 @@ struct EblanProject { std::string windowTitle = "EBLAN ENGINE 3D - Standalone Retro Game"; int defaultWidth = 1280; int defaultHeight = 960; + int gameMode = 1; // 0 = Adventure (No weapons / Peaceful), 1 = Action (Weapons) + bool showWeapons = true; + bool showCrosshair = true; bool Save(const std::string& path) const { HANDLE hFile = CreateFileA(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -31,6 +34,10 @@ struct EblanProject { content += std::string("DefaultWidth=") + numBuf + "\r\n"; snprintf(numBuf, sizeof(numBuf), "%d", defaultHeight); content += std::string("DefaultHeight=") + numBuf + "\r\n"; + snprintf(numBuf, sizeof(numBuf), "%d", gameMode); + content += std::string("GameMode=") + numBuf + "\r\n"; + content += std::string("ShowWeapons=") + (showWeapons ? "1" : "0") + "\r\n"; + content += std::string("ShowCrosshair=") + (showCrosshair ? "1" : "0") + "\r\n"; DWORD written = 0; WriteFile(hFile, content.data(), static_cast(content.size()), &written, nullptr); CloseHandle(hFile); @@ -65,6 +72,9 @@ struct EblanProject { else if (key == "WindowTitle") windowTitle = val; else if (key == "DefaultWidth") defaultWidth = std::atoi(val.c_str()); else if (key == "DefaultHeight") defaultHeight = std::atoi(val.c_str()); + else if (key == "GameMode") gameMode = std::atoi(val.c_str()); + else if (key == "ShowWeapons") showWeapons = (val == "1" || val == "true"); + else if (key == "ShowCrosshair") showCrosshair = (val == "1" || val == "true"); } } diff --git a/engine.exe b/engine.exe index e9f985d..979ec24 100644 Binary files a/engine.exe and b/engine.exe differ diff --git a/include/engine_combat.inc b/include/engine_combat.inc index 4a18045..92a3f2f 100644 --- a/include/engine_combat.inc +++ b/include/engine_combat.inc @@ -37,11 +37,17 @@ UpdateCombat PROC uses ebx ecx edx esi edi dec playerHitFlash @@: - ; If player is dead or game is paused, cannot attack + ; If player is dead, game is paused, or in peaceful adventure mode, cannot attack cmp isPlayerDead, 0 jne combat_done cmp isGamePaused, 0 jne combat_done + cmp gameMode, GAME_MODE_ADVENTURE + je combat_done + cmp enableWeapons, 0 + je combat_done + cmp playerWeapon, WEAPON_NONE + je combat_done ; 2. Check attack input cmp keyAttack, 0 @@ -383,10 +389,21 @@ UpdateCombat ENDP RenderCombatHUD PROC uses ebx ecx edx esi edi LOCAL barW:DWORD, gunY:DWORD + ; If in adventure mode, unarmed, or weapons disabled, skip weapon & combat stats + cmp gameMode, GAME_MODE_ADVENTURE + je hud_damage_flash + cmp enableWeapons, 0 + je hud_damage_flash + cmp playerWeapon, WEAPON_NONE + je hud_damage_flash + ; 1. Center Crosshair (subtle cross at 160, 120) + cmp showCrosshair, 0 + je skip_crosshair invoke DrawRect, 156, 120, 9, 1, 00FFFFFFh invoke DrawRect, 160, 116, 1, 9, 00FFFFFFh invoke DrawRect, 160, 120, 1, 1, 0000FF00h +skip_crosshair: ; 2. Render Active Weapon at bottom center cmp playerWeapon, WEAPON_KNIFE @@ -470,15 +487,15 @@ draw_pistol_barrel: invoke DrawRect, 152, eax, 17, 22, 00453020h ; brown wood grip plates hud_stats_draw: - ; 3. Health Bar - invoke DrawString8x8, 8, 42, OFFSET strHpLabel, COLOR_GREEN - invoke DrawNumber8x8, 34, 42, playerHealth, COLOR_WHITE + ; 3. Health Bar at top-left: X=8, Y=8 + invoke DrawString8x8, 8, 8, OFFSET strHpLabel, COLOR_GREEN + invoke DrawNumber8x8, 34, 8, playerHealth, COLOR_WHITE - invoke DrawRect, 68, 42, 60, 8, 00441010h - invoke DrawRectOutline, 68, 42, 60, 8, COLOR_WHITE + invoke DrawRect, 66, 8, 50, 8, 00441010h + invoke DrawRectOutline, 66, 8, 50, 8, COLOR_WHITE mov eax, playerHealth - imul eax, 58 + imul eax, 48 xor edx, edx mov ecx, playerMaxHealth test ecx, ecx @@ -489,57 +506,63 @@ hud_stats_draw: mov barW, eax cmp barW, 0 jle hud_ammo_draw - cmp barW, 58 + cmp barW, 48 jle @F - mov barW, 58 + mov barW, 48 @@: mov edx, 0000CC33h ; Green cmp playerHealth, 30 jg @F mov edx, 00FF2222h ; Red warning @@: - invoke DrawRect, 69, 43, barW, 6, edx + invoke DrawRect, 67, 9, barW, 6, edx hud_ammo_draw: - ; 4. Ammo Display - invoke DrawString8x8, 140, 42, OFFSET strAmmoLabel, COLOR_YELLOW - invoke DrawNumber8x8, 182, 42, playerAmmo, COLOR_YELLOW + ; 4. Ammo Display: X=126, Y=8 + invoke DrawString8x8, 126, 8, OFFSET strAmmoLabel, COLOR_YELLOW + invoke DrawNumber8x8, 168, 8, playerAmmo, COLOR_YELLOW - ; 5. Score Display - invoke DrawString8x8, 222, 42, OFFSET strScoreLabel, COLOR_CYAN - invoke DrawNumber8x8, 272, 42, playerScore, COLOR_CYAN + ; 5. Keys: X=204, Y=8 (if collected) + cmp playerKeys, 0 + jle hud_score_draw + invoke DrawString8x8, 204, 8, OFFSET strKeysLabel, COLOR_MAGENTA + invoke DrawNumber8x8, 238, 8, playerKeys, COLOR_MAGENTA + +hud_score_draw: + ; 6. Score Display: only in Debug HUD style + cmp hudStyle, HUD_STYLE_CLEAN + je draw_weapon_badge + invoke DrawString8x8, 8, 20, OFFSET strScoreLabel, COLOR_CYAN + invoke DrawNumber8x8, 58, 20, playerScore, COLOR_CYAN + +draw_weapon_badge: + ; 7. Active Weapon Badge at bottom-right corner: X=228, Y=222 + invoke DrawRect, 228, 222, 88, 14, 00141414h + invoke DrawRectOutline, 228, 222, 88, 14, 00444444h - ; 6. Active Weapon Name & Slot Indicators at top left - invoke DrawString8x8, 8, 26, OFFSET strWeaponLabel, COLOR_WHITE cmp playerWeapon, WEAPON_KNIFE jne @F - invoke DrawString8x8, 42, 26, OFFSET strWeaponKnife, COLOR_CYAN + invoke DrawString8x8, 233, 225, OFFSET strWeaponKnife, COLOR_CYAN jmp draw_weapon_slots @@: cmp playerWeapon, WEAPON_SHOTGUN jne @F - invoke DrawString8x8, 42, 26, OFFSET strWeaponShotgun, 00FFAA00h + invoke DrawString8x8, 233, 225, OFFSET strWeaponShotgun, 00FFAA00h jmp draw_weapon_slots @@: - invoke DrawString8x8, 42, 26, OFFSET strWeaponPistol, COLOR_YELLOW + invoke DrawString8x8, 233, 225, OFFSET strWeaponPistol, COLOR_YELLOW draw_weapon_slots: ; Draw small weapon slot indicator boxes: [1] [2] [3] - invoke DrawRectOutline, 100, 26, 8, 8, 00555555h - invoke DrawRectOutline, 112, 26, 8, 8, 00555555h - invoke DrawRectOutline, 124, 26, 8, 8, 00555555h + invoke DrawRectOutline, 287, 225, 6, 8, 00555555h + invoke DrawRectOutline, 295, 225, 6, 8, 00555555h + invoke DrawRectOutline, 303, 225, 6, 8, 00555555h ; Highlight active slot mov eax, playerWeapon - imul eax, 12 - add eax, 100 - invoke DrawRectOutline, eax, 26, 8, 8, COLOR_YELLOW - - ; 7. Keys (if collected) - cmp playerKeys, 0 - jle hud_damage_flash - invoke DrawString8x8, 222, 10, OFFSET strKeysLabel, COLOR_MAGENTA - invoke DrawNumber8x8, 258, 10, playerKeys, COLOR_MAGENTA + imul eax, 8 + add eax, 287 + invoke DrawRectOutline, eax, 225, 6, 8, COLOR_YELLOW hud_damage_flash: ; 8. Damage Screen Flash @@ -571,19 +594,20 @@ RenderPauseMenu PROC uses ebx ecx edx cmp isGamePaused, 0 je pause_menu_done - ; Semi-transparent dark overlay panel (centered 200x105 at 60, 65) - invoke DrawRect, 60, 65, 200, 105, 00141416h - invoke DrawRectOutline, 60, 65, 200, 105, COLOR_CYAN - invoke DrawRectOutline, 62, 67, 196, 101, COLOR_WHITE + ; Centered semi-transparent panel 220x112 at (50, 64) + invoke DrawRect, 50, 64, 220, 112, 00141416h + invoke DrawRectOutline, 50, 64, 220, 112, COLOR_CYAN + invoke DrawRectOutline, 52, 66, 216, 108, COLOR_WHITE ; Header - invoke DrawString8x8, 112, 75, OFFSET strPauseTitle, COLOR_YELLOW + invoke DrawString8x8, 112, 72, OFFSET strPauseTitle, COLOR_YELLOW ; Menu Options - invoke DrawString8x8, 76, 95, OFFSET strPauseOpt1, COLOR_GREEN - invoke DrawString8x8, 76, 110, OFFSET strPauseOpt2, COLOR_WHITE - invoke DrawString8x8, 76, 125, OFFSET strPauseOpt3, COLOR_CYAN - invoke DrawString8x8, 76, 140, OFFSET strPauseOpt4, 00FF4444h + invoke DrawString8x8, 64, 88, OFFSET strPauseOpt1, COLOR_GREEN + invoke DrawString8x8, 64, 102, OFFSET strPauseOpt2, COLOR_WHITE + invoke DrawString8x8, 64, 116, OFFSET strPauseOpt3, COLOR_CYAN + invoke DrawString8x8, 64, 130, OFFSET strPauseOpt4, COLOR_YELLOW + invoke DrawString8x8, 64, 146, OFFSET strPauseOpt5, 00FF4444h pause_menu_done: ret diff --git a/include/engine_data.inc b/include/engine_data.inc index a3e6618..a36f881 100644 --- a/include/engine_data.inc +++ b/include/engine_data.inc @@ -69,10 +69,18 @@ keyWeapon3 db 0 keyWeapon4 db 0 keyPause db 0 keyPauseHeld db 0 +keyHudStyleHeld db 0 +keyMenuHeld db 0 isGamePaused dd 0 +; Game Mode & HUD Configuration +gameMode dd GAME_MODE_ACTION ; 0 = Adventure (No weapons), 1 = Action / Combat +hudStyle dd HUD_STYLE_CLEAN ; 0 = Clean / Immersive, 1 = Debug / Classic +enableWeapons dd 1 ; 1 = Weapons active, 0 = Pure exploration +showCrosshair dd 1 ; 1 = Show crosshair in Action mode, 0 = Hidden + ; Player Combat & Gameplay Stats -playerWeapon dd 1 ; Default: WEAPON_PISTOL +playerWeapon dd 1 ; Default: WEAPON_PISTOL playerHealth dd 100 playerMaxHealth dd 100 playerAmmo dd 50 @@ -129,12 +137,13 @@ strWeaponLabel db "WPN:",0 strWeaponKnife db "KNIFE",0 strWeaponPistol db "PISTOL",0 strWeaponShotgun db "SHOTGUN",0 -strPauseTitle db "== PAUSED ==",0 -strPauseOpt1 db "[1] RESUME",0 -strPauseOpt2 db "[2] RESTART LEVEL",0 -strPauseOpt3 db "[3] MINIMAP RADAR",0 -strPauseOpt4 db "[4] QUIT TO WINDOWS",0 -strYouDied db "YOU DIED! PRESS [R] TO RESTART",0 +strPauseTitle db "== ПАУЗА ==",0 +strPauseOpt1 db "[1] ПРОДОЛЖИТЬ",0 +strPauseOpt2 db "[2] ОРУЖИЕ: ВКЛ/ВЫКЛ",0 +strPauseOpt3 db "[3] ИНТЕРФЕЙС: ЧИСТЫЙ/ИНФО",0 +strPauseOpt4 db "[4] ПЕРЕЗАПУСК УРОВНЯ",0 +strPauseOpt5 db "[5] ВЫХОД В WINDOWS",0 +strYouDied db "ВЫ ПОГИБЛИ! НАЖМИТЕ [R]",0 cutsceneCount dd 0 cutsceneStringCount dd 0 cutsceneFlags dd 0 @@ -262,3 +271,17 @@ szSwitchOffToast db "ПЕРЕКЛЮЧАТЕЛЬ: ВЫКЛ", 0 szDoorOpenedToast db "ДВЕРЬ ОТКРЫТА", 0 szDoorClosedToast db "ДВЕРЬ ЗАКРЫТА", 0 szBarrelExplodedToast db "ВЗРЫВ БОЧКИ!", 0 +szWeaponsEnabledToast db "РЕЖИМ: ОРУЖИЕ ВКЛЮЧЕНО", 0 +szWeaponsDisabledToast db "РЕЖИМ: БЕЗ ОРУЖИЯ (КВЕСТ)", 0 +szHudCleanToast db "ИНТЕРФЕЙС: ЧИСТЫЙ ВИД", 0 +szHudDebugToast db "ИНТЕРФЕЙС: РЕЖИМ ОТЛАДКИ", 0 +szProjectFile db "project.eblanproj", 0 +szProjGameMode0 db "GameMode=0", 0 +szProjGameMode1 db "GameMode=1", 0 +szProjShowWeapons0 db "ShowWeapons=0", 0 +szFlagNoWeapons db "noweapons", 0 +szFlagAdventure db "adventure", 0 +szFlagAction db "action", 0 +szFlagDebugHud db "debug-hud", 0 +szFlagCleanHud db "clean-hud", 0 +previewPrefix db "--preview:", 0 diff --git a/include/engine_defs.inc b/include/engine_defs.inc index 55546e9..9081565 100644 --- a/include/engine_defs.inc +++ b/include/engine_defs.inc @@ -71,8 +71,20 @@ VK_KEY_1 equ 31h VK_KEY_2 equ 32h VK_KEY_3 equ 33h VK_KEY_4 equ 34h +VK_KEY_5 equ 35h +VK_KEY_F1 equ 70h +VK_KEY_H equ 48h + +; Game Modes +GAME_MODE_ADVENTURE equ 0 ; Exploration / Quest / Horror / Puzzle (No weapons, clean view) +GAME_MODE_ACTION equ 1 ; Combat Action (Weapons, Crosshair, Ammo, Health bar) + +; HUD Styles +HUD_STYLE_CLEAN equ 0 ; Clean / Immersive (No debug watermark, no bottom bar) +HUD_STYLE_DEBUG equ 1 ; Debug Mode (FPS counter, Engine watermark, Minimap) ; Weapon Archetypes +WEAPON_NONE equ -1 ; No weapon (Unarmed / Peaceful / Quest) WEAPON_KNIFE equ 0 ; Melee blade (no ammo, fast, 30 damage) WEAPON_PISTOL equ 1 ; Handgun (1 ammo, accurate, 35 damage) WEAPON_SHOTGUN equ 2 ; Pump Shotgun (2 ammo, 3-ray spread, 70 damage) diff --git a/include/engine_gameplay.inc b/include/engine_gameplay.inc index b017af2..320f255 100644 --- a/include/engine_gameplay.inc +++ b/include/engine_gameplay.inc @@ -240,3 +240,104 @@ RenderGameHUDOverlays PROC call Game_OnCustomHUD ret RenderGameHUDOverlays ENDP + +; ------------------------------------------------------------------------------ +; CheckCmdFlag: Case-insensitive substring search (EAX = 1 if found, 0 if not) +; ------------------------------------------------------------------------------ +CheckCmdFlag PROC uses esi edi ecx, pszCmd:DWORD, pszFlag:DWORD + mov esi, pszCmd + test esi, esi + jz flag_not_found +check_next_char: + cmp byte ptr [esi], 0 + je flag_not_found + mov edi, pszFlag + mov ecx, esi +compare_loop: + mov al, byte ptr [edi] + test al, al + jz flag_found + mov ah, byte ptr [ecx] + cmp al, 'A' + jl @F + cmp al, 'Z' + jg @F + add al, 32 +@@: + cmp ah, 'A' + jl @F + cmp ah, 'Z' + jg @F + add ah, 32 +@@: + cmp al, ah + jne mismatch + inc edi + inc ecx + jmp compare_loop +mismatch: + inc esi + jmp check_next_char +flag_found: + mov eax, 1 + ret +flag_not_found: + xor eax, eax + ret +CheckCmdFlag ENDP + +; ------------------------------------------------------------------------------ +; LoadProjectSettings: Reads project.eblanproj if present in working directory +; ------------------------------------------------------------------------------ +LoadProjectSettings PROC uses ebx ecx edx esi edi + LOCAL hFile:DWORD, bytesRead:DWORD + LOCAL projBuf[1024]:BYTE + + invoke CreateFileA, OFFSET szProjectFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0 + cmp eax, INVALID_HANDLE_VALUE + je proj_done + mov hFile, eax + + lea edx, projBuf + invoke ReadFile, hFile, edx, 1023, ADDR bytesRead, 0 + invoke CloseHandle, hFile + + cmp bytesRead, 0 + jle proj_done + + mov eax, bytesRead + mov byte ptr [projBuf + eax], 0 + + lea edx, projBuf + invoke CheckCmdFlag, edx, OFFSET szProjGameMode0 + test eax, eax + jnz set_adv_mode + + lea edx, projBuf + invoke CheckCmdFlag, edx, OFFSET szProjShowWeapons0 + test eax, eax + jnz set_adv_mode + + lea edx, projBuf + invoke CheckCmdFlag, edx, OFFSET szProjGameMode1 + test eax, eax + jnz set_act_mode + jmp proj_done + +set_adv_mode: + mov gameMode, GAME_MODE_ADVENTURE + mov enableWeapons, 0 + mov playerWeapon, WEAPON_NONE + mov showCrosshair, 0 + jmp proj_done + +set_act_mode: + mov gameMode, GAME_MODE_ACTION + mov enableWeapons, 1 + mov playerWeapon, WEAPON_PISTOL + mov showCrosshair, 1 + +proj_done: + ret +LoadProjectSettings ENDP + diff --git a/include/engine_input.inc b/include/engine_input.inc index 676320b..ca93aef 100644 --- a/include/engine_input.inc +++ b/include/engine_input.inc @@ -181,7 +181,34 @@ restart_released: mov keyRestartHeld, 0 restart_sampled: - ; Weapon Selection Keys: 1, 2, 3 + ; F1 / H: Toggle Clean HUD vs Debug HUD + invoke GetAsyncKeyState, VK_KEY_F1 + mov bx, ax + invoke GetAsyncKeyState, VK_KEY_H + or bx, ax + test bx, 8000h + jz hud_key_released + cmp keyHudStyleHeld, 0 + jne hud_key_sampled + xor hudStyle, 1 + mov keyHudStyleHeld, 1 + cmp hudStyle, HUD_STYLE_CLEAN + je toast_hud_clean + invoke ShowGameToast, OFFSET szHudDebugToast, 60, COLOR_CYAN + jmp hud_key_sampled +toast_hud_clean: + invoke ShowGameToast, OFFSET szHudCleanToast, 60, COLOR_GREEN + jmp hud_key_sampled +hud_key_released: + mov keyHudStyleHeld, 0 +hud_key_sampled: + + ; Weapon Selection Keys: 1, 2, 3 (only in Action mode with weapons enabled) + cmp gameMode, GAME_MODE_ACTION + jne skip_weapon_select + cmp enableWeapons, 0 + je skip_weapon_select + invoke GetAsyncKeyState, VK_KEY_1 test ax, 8000h jz @F @@ -197,6 +224,7 @@ restart_sampled: jz @F mov playerWeapon, WEAPON_SHOTGUN @@: +skip_weapon_select: ; Escape / Pause Key invoke GetAsyncKeyState, VK_KEY_ESCAPE @@ -224,31 +252,83 @@ pause_key_sampled: mov keyAttack, 0 mov keyAction, 0 + ; Check if any menu key is pressed + invoke GetAsyncKeyState, VK_KEY_1 + mov bx, ax + invoke GetAsyncKeyState, VK_KEY_2 + or bx, ax + invoke GetAsyncKeyState, VK_KEY_3 + or bx, ax + invoke GetAsyncKeyState, VK_KEY_4 + or bx, ax + invoke GetAsyncKeyState, VK_KEY_5 + or bx, ax + test bx, 8000h + jz menu_keys_released + + ; If a key is pressed, check if already held + cmp keyMenuHeld, 0 + jne pause_menu_done + mov keyMenuHeld, 1 + ; Option 1: Resume invoke GetAsyncKeyState, VK_KEY_1 test ax, 8000h jz @F mov isGamePaused, 0 + jmp pause_menu_done @@: - ; Option 2: Restart level + ; Option 2: Toggle Weapons / Peaceful Mode invoke GetAsyncKeyState, VK_KEY_2 test ax, 8000h jz @F + xor enableWeapons, 1 + cmp enableWeapons, 0 + je set_peaceful_mode + mov gameMode, GAME_MODE_ACTION + mov playerWeapon, WEAPON_PISTOL + mov showCrosshair, 1 + invoke ShowGameToast, OFFSET szWeaponsEnabledToast, 80, COLOR_YELLOW + jmp pause_menu_done +set_peaceful_mode: + mov gameMode, GAME_MODE_ADVENTURE + mov playerWeapon, WEAPON_NONE + mov showCrosshair, 0 + invoke ShowGameToast, OFFSET szWeaponsDisabledToast, 80, COLOR_GREEN + jmp pause_menu_done +@@: + ; Option 3: Toggle HUD (Clean vs Debug) + invoke GetAsyncKeyState, VK_KEY_3 + test ax, 8000h + jz @F + xor hudStyle, 1 + cmp hudStyle, HUD_STYLE_CLEAN + je toast_pause_clean + invoke ShowGameToast, OFFSET szHudDebugToast, 60, COLOR_CYAN + jmp pause_menu_done +toast_pause_clean: + invoke ShowGameToast, OFFSET szHudCleanToast, 60, COLOR_GREEN + jmp pause_menu_done +@@: + ; Option 4: Restart level + invoke GetAsyncKeyState, VK_KEY_4 + test ax, 8000h + jz @F mov isGamePaused, 0 mov keyRestart, 1 + jmp pause_menu_done @@: - ; Option 3: Toggle Map - invoke GetAsyncKeyState, VK_KEY_3 + ; Option 5: Quit to Windows + invoke GetAsyncKeyState, VK_KEY_5 test ax, 8000h - jz @F - xor keyShowMap, 1 -@@: - ; Option 4: Quit - invoke GetAsyncKeyState, VK_KEY_4 - test ax, 8000h - jz @F + jz pause_menu_done mov isRunning, 0 -@@: + jmp pause_menu_done + +menu_keys_released: + mov keyMenuHeld, 0 + +pause_menu_done: ret not_in_pause_menu: diff --git a/include/engine_preview.inc b/include/engine_preview.inc index 6a2d7fa..b5d6579 100644 --- a/include/engine_preview.inc +++ b/include/engine_preview.inc @@ -1,7 +1,6 @@ ; Windowless renderer used by EblanStudio. Same Render3DScene and overlays as game. ; Fixed wire layout is mirrored by editor/src/preview_protocol.h. .data -previewPrefix db "--preview:",0 previewHandle dd 0 previewMemory dd 0 previewParent dd 0 diff --git a/main.asm b/main.asm index dd0fb01..cd16f2d 100644 --- a/main.asm +++ b/main.asm @@ -220,19 +220,30 @@ Engine_RenderHUD PROC uses ebx ecx edx call RenderCutsceneOverlay ret normal_engine_hud: + cmp hudStyle, HUD_STYLE_DEBUG + jne clean_hud_mode + + ; --- DEBUG / DEVELOPER HUD MODE --- ; 1. Top HUD Header - invoke DrawString8x8, 10, 10, OFFSET strEngineName, COLOR_YELLOW + invoke DrawString8x8, 8, 8, OFFSET strEngineName, COLOR_YELLOW - ; 2. FPS Counter - invoke DrawString8x8, 10, 24, OFFSET szFpsLabel, COLOR_CYAN - invoke DrawNumber8x8, 42, 24, currentFps, COLOR_CYAN + ; 2. FPS Counter (drawn at top right X=250 so it never overlaps left elements) + invoke DrawString8x8, 250, 8, OFFSET szFpsLabel, COLOR_CYAN + invoke DrawNumber8x8, 282, 8, currentFps, COLOR_CYAN - ; 3. Controls Bar at Bottom + ; 3. Controls Bar at Bottom (Debug mode only) invoke DrawRect, 0, 215, SCREEN_W, 25, 00101010h invoke DrawString8x8, 10, 218, OFFSET strControls1, COLOR_WHITE invoke DrawString8x8, 10, 228, OFFSET strControls3, COLOR_GREEN + jmp check_hints - ; 4. Door / Portal Interaction Hint (center screen) +clean_hud_mode: + ; --- CLEAN / IMMERSIVE HUD MODE --- + ; In clean mode, no watermarks and no permanent bottom bar. + ; Full 320x240 view is dedicated to the game world. + +check_hints: + ; 4. Door / Portal Interaction Hint (Cleanly centered above crosshair) call CheckDoorHint cmp eax, 1 je show_door_hint @@ -241,11 +252,11 @@ normal_engine_hud: jmp check_minimap show_door_hint: - invoke DrawString8x8, 128, 150, OFFSET strDoorHint, COLOR_YELLOW + invoke DrawString8x8, 116, 140, OFFSET strDoorHint, COLOR_YELLOW jmp check_minimap show_portal_hint: - invoke DrawString8x8, 110, 150, OFFSET strPortalHint, COLOR_CYAN + invoke DrawString8x8, 110, 140, OFFSET strPortalHint, COLOR_CYAN check_minimap: cmp mapErrorFrames, 0 @@ -369,14 +380,14 @@ ParseStartupMap PROC uses ebx ecx edx esi edi skip_executable_quote: lodsb test al, al - jz startup_arg_done + jz check_cmdline_flags cmp al, 34 jne skip_executable_quote jmp skip_argument_spaces skip_executable_word: lodsb test al, al - jz startup_arg_done + jz check_cmdline_flags cmp al, 32 jne skip_executable_word skip_argument_spaces: @@ -413,10 +424,59 @@ write_startup_char: dec ecx jnz startup_argument_loop mov startupMapPath, 0 - ret + jmp check_cmdline_flags terminate_startup_arg: mov byte ptr [edi], 0 -startup_arg_done: + + ; If startupMapPath starts with '-' and not '--preview:', clear it + mov al, byte ptr [startupMapPath] + cmp al, '-' + jne check_cmdline_flags + mov esi, OFFSET startupMapPath + mov edi, OFFSET previewPrefix + mov ecx, 10 + repe cmpsb + je check_cmdline_flags + mov byte ptr [startupMapPath], 0 + +check_cmdline_flags: + invoke GetCommandLineA + mov ebx, eax + + invoke CheckCmdFlag, ebx, OFFSET szFlagNoWeapons + test eax, eax + jz @F + mov gameMode, GAME_MODE_ADVENTURE + mov enableWeapons, 0 + mov playerWeapon, WEAPON_NONE + mov showCrosshair, 0 +@@: + invoke CheckCmdFlag, ebx, OFFSET szFlagAdventure + test eax, eax + jz @F + mov gameMode, GAME_MODE_ADVENTURE + mov enableWeapons, 0 + mov playerWeapon, WEAPON_NONE + mov showCrosshair, 0 +@@: + invoke CheckCmdFlag, ebx, OFFSET szFlagAction + test eax, eax + jz @F + mov gameMode, GAME_MODE_ACTION + mov enableWeapons, 1 + mov playerWeapon, WEAPON_PISTOL + mov showCrosshair, 1 +@@: + invoke CheckCmdFlag, ebx, OFFSET szFlagDebugHud + test eax, eax + jz @F + mov hudStyle, HUD_STYLE_DEBUG +@@: + invoke CheckCmdFlag, ebx, OFFSET szFlagCleanHud + test eax, eax + jz @F + mov hudStyle, HUD_STYLE_CLEAN +@@: ret ParseStartupMap ENDP @@ -427,6 +487,7 @@ main PROC LOCAL msg:MSG LOCAL curTick:DWORD, elapsed:DWORD + call LoadProjectSettings call ParseStartupMap call TryEnginePreview call Engine_Init diff --git a/main.obj b/main.obj index edf5649..f31e5e4 100644 Binary files a/main.obj and b/main.obj differ