diff --git a/Ananta.Proxy/proxy/public/fastpatch_4229938.zip b/Ananta.Proxy/proxy/public/fastpatch_4229938.zip index e79d75d..61ef222 100644 Binary files a/Ananta.Proxy/proxy/public/fastpatch_4229938.zip and b/Ananta.Proxy/proxy/public/fastpatch_4229938.zip differ diff --git a/Ananta.Proxy/proxy/tools/generate_client_config_patch.js b/Ananta.Proxy/proxy/tools/generate_client_config_patch.js index 9858e6e..1f1f5b0 100644 --- a/Ananta.Proxy/proxy/tools/generate_client_config_patch.js +++ b/Ananta.Proxy/proxy/tools/generate_client_config_patch.js @@ -825,25 +825,115 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` end\n` + ` end)\n` + ` end)\n` + -` elseif cmd == "LAUNCH_MINIGAME:FIGHTER" then\n` + +` elseif string.sub(cmd, 1, 16) == "LAUNCH_MINIGAME:" or string.sub(cmd, 1, 20) == "CMD:LAUNCH_MINIGAME:" or string.sub(cmd, 1, 9) == "MINIGAME:" or string.sub(cmd, 1, 13) == "CMD:MINIGAME:" then\n` + +` local rawGame = string.match(cmd, "MINIGAME:([^:]+)") or "KOF97"\n` + +` local gameType = string.upper(rawGame)\n` + ` pcall(function()\n` + -` if gPanelManager and gPanelId and gPanelId.S_FIGHTER_MINIGAME_PANEL then\n` + -` gPanelManager:OpenWindow(gPanelId.S_FIGHTER_MINIGAME_PANEL)\n` + -` elseif CS and CS.L18 and CS.L18.MiniGame and CS.L18.MiniGame.Fighter and CS.L18.MiniGame.Fighter.FighterMinigame then\n` + -` local mg = CS.L18.MiniGame.Fighter.FighterMinigame.Instance\n` + -` if mg then\n` + -` mg.GameStarted = true\n` + -` pcall(function() mg:CreatePlayer() end)\n` + -` pcall(function() mg:CreateEnemy() end)\n` + +` local function forceShow(panelId, data)\n` + +` if not panelId or not gPanelManager then return false end\n` + +` local ok = false\n` + +` pcall(function()\n` + +` if gPanelManager.panelData then\n` + +` gPanelManager.panelData[panelId] = data\n` + +` end\n` + +` if LX6 and LX6.Manager and LX6.Manager.PanelManager and LX6.Manager.PanelManager.Instance then\n` + +` LX6.Manager.PanelManager.Instance:CheckShowFromLua(panelId, nil, nil, -1, -1)\n` + +` ok = true\n` + +` elseif gPanelManager.CheckShow then\n` + +` ok = gPanelManager:CheckShow(panelId, data)\n` + +` end\n` + +` end)\n` + +` return ok\n` + +` end\n` + +` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` local pObj = myUnit and myUnit.PlayerObj\n` + +` local pPos = (pObj and pObj.transform and pObj.transform.position) or (UnityEngine and UnityEngine.Vector3 and UnityEngine.Vector3.zero) or Vector3.zero\n` + +` local pRot = (pObj and pObj.transform and pObj.transform.rotation) or (UnityEngine and UnityEngine.Quaternion and UnityEngine.Quaternion.identity) or Quaternion.identity\n` + +` local pScale = (UnityEngine and UnityEngine.Vector3 and UnityEngine.Vector3.one) or Vector3.one\n` + +` local fArgs = {\n` + +` position = pPos,\n` + +` rotation = pRot,\n` + +` localScale = pScale,\n` + +` forbidClickExit = false,\n` + +` autoCloseAfterWin = -1,\n` + +` ignoreCountdown = false\n` + +` }\n` + +` local tip = "Launched Minigame: " .. tostring(gameType)\n` + +` if gameType == "KOF97" then\n` + +` if gPanelId and gPanelId.LIBRETRO_PANEL then\n` + +` forceShow(gPanelId.LIBRETRO_PANEL, { gameType = 1 })\n` + +` tip = "Opened Arcade: The King of Fighters 97 (ตู้เกม KOF '97)"\n` + +` end\n` + +` elseif gameType == "METALSLUG" then\n` + +` if gPanelId and gPanelId.LIBRETRO_PANEL then\n` + +` forceShow(gPanelId.LIBRETRO_PANEL, { gameType = 0 })\n` + +` tip = "Opened Arcade: Metal Slug (ตู้เกม Metal Slug)"\n` + +` end\n` + +` elseif gameType == "FIGHTER" then\n` + +` local shown = false\n` + +` if gPanelId and gPanelId.FIGHTER_MAIN_PANEL then\n` + +` shown = forceShow(gPanelId.FIGHTER_MAIN_PANEL, fArgs)\n` + +` end\n` + +` if gPanelId and gPanelId.FIGHTER_HUD_PANEL then\n` + +` forceShow(gPanelId.FIGHTER_HUD_PANEL, fArgs)\n` + +` end\n` + +` if not shown and gPanelId and gPanelId.LIBRETRO_PANEL then\n` + +` forceShow(gPanelId.LIBRETRO_PANEL, { gameType = 1 })\n` + +` tip = "Opened Arcade: The King of Fighters 97 (KOF)"\n` + +` else\n` + +` tip = "Opened 3D Arcade Fighter Minigame"\n` + +` end\n` + +` elseif gameType == "PIANO" then\n` + +` if gPanelId and gPanelId.UI_PANEL__INSTRUMENT__PIANO then\n` + +` forceShow(gPanelId.UI_PANEL__INSTRUMENT__PIANO)\n` + +` tip = "Opened Grand Piano Minigame"\n` + +` end\n` + +` elseif gameType == "DRUMKIT" then\n` + +` if gPanelId and gPanelId.UI_PANEL__INSTRUMENT__DRUMKIT then\n` + +` forceShow(gPanelId.UI_PANEL__INSTRUMENT__DRUMKIT)\n` + +` tip = "Opened Drumkit Minigame"\n` + +` end\n` + +` elseif gameType == "BOWLING" then\n` + +` if gPanelId and gPanelId.MINI_GAMES_BOWLING_MAIN_PANEL then\n` + +` forceShow(gPanelId.MINI_GAMES_BOWLING_MAIN_PANEL)\n` + +` tip = "Opened Bowling Minigame"\n` + +` end\n` + +` elseif gameType == "BASKETBALL" then\n` + +` if gPanelId and gPanelId.BASKETBALL_SHOOT_PANEL then\n` + +` forceShow(gPanelId.BASKETBALL_SHOOT_PANEL)\n` + +` tip = "Opened Basketball Shoot Minigame"\n` + ` end\n` + ` end\n` + -` local tip = "Launched Arcade Fighter Minigame"\n` + -` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + -` gDisplayMessageMgr:ShowMessageContent(tip)\n` + -` end\n` + -` if gCS and gCS.MessageTipsMgr and gCS.MessageTipsMgr.ShowMessageTips then\n` + -` gCS.MessageTipsMgr:ShowMessageTips(tip)\n` + +` pcall(function()\n` + +` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + +` gDisplayMessageMgr:ShowMessageContent(tip)\n` + +` end\n` + +` if gCS and gCS.MessageTipsMgr and gCS.MessageTipsMgr.ShowMessageTips then\n` + +` gCS.MessageTipsMgr:ShowMessageTips(tip)\n` + +` end\n` + +` end)\n` + +` end)\n` + +` elseif cmd == "CLOSE_MINIGAME" or cmd == "CMD:CLOSE_MINIGAME" then\n` + +` pcall(function()\n` + +` local toClose = {\n` + +` gPanelId and gPanelId.LIBRETRO_PANEL,\n` + +` gPanelId and gPanelId.FIGHTER_MAIN_PANEL,\n` + +` gPanelId and gPanelId.FIGHTER_HUD_PANEL,\n` + +` gPanelId and gPanelId.UI_PANEL__INSTRUMENT__PIANO,\n` + +` gPanelId and gPanelId.UI_PANEL__INSTRUMENT__DRUMKIT,\n` + +` gPanelId and gPanelId.MINI_GAMES_BOWLING_MAIN_PANEL,\n` + +` gPanelId and gPanelId.BASKETBALL_SHOOT_PANEL\n` + +` }\n` + +` for _, pid in ipairs(toClose) do\n` + +` if pid and gPanelManager then\n` + +` pcall(function() gPanelManager:Close(pid) end)\n` + +` end\n` + ` end\n` + +` pcall(function()\n` + +` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + +` gDisplayMessageMgr:ShowMessageContent("Closed Minigame")\n` + +` end\n` + +` end)\n` + ` end)\n` + ` elseif cmd == "TOGGLE_CLOTHES" then\n` + ` pcall(function()\n` + diff --git a/Ananta.Server/Ananta.App/DebugApiServer.cs b/Ananta.Server/Ananta.App/DebugApiServer.cs index c6cbead..36204f6 100644 --- a/Ananta.Server/Ananta.App/DebugApiServer.cs +++ b/Ananta.Server/Ananta.App/DebugApiServer.cs @@ -139,6 +139,10 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub await WriteJsonAsync(ctx, await PlayTimelineAsync(await ReadBodyAsync(ctx.Request, token), token), token); else if (method == "POST" && path == "/api/minigame/launch") await WriteJsonAsync(ctx, await LaunchMinigameAsync(await ReadBodyAsync(ctx.Request, token), token), token); + else if (method == "POST" && path == "/api/minigame/close") + await WriteJsonAsync(ctx, await CloseMinigameAsync(token), token); + else if (method == "GET" && path == "/api/minigame/catalog") + await WriteJsonAsync(ctx, MinigameCatalog(), token); else if (method == "GET" && path == "/api/cutscene/catalog") await WriteJsonAsync(ctx, CutsceneCatalog(), token); else if (method == "POST" && path == "/api/npc/spawn") @@ -1349,11 +1353,52 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub if (session is null) return new { ok = false, error = "no live game session (is the client in the world?)" }; - var cmd = "CMD:LAUNCH_MINIGAME:FIGHTER"; + string game = "KOF97"; + try + { + using var doc = JsonDocument.Parse(string.IsNullOrWhiteSpace(json) ? "{}" : json); + var root = doc.RootElement; + if (root.TryGetProperty("game", out var pg)) game = pg.GetString() ?? "KOF97"; + else if (root.TryGetProperty("id", out var pi)) game = pi.GetString() ?? "KOF97"; + } + catch { } + + var cmd = $"CMD:LAUNCH_MINIGAME:{game.ToUpperInvariant()}"; await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token); - session.Log.Info("[DEBUG-API] launch arcade minigame fighter dispatched"); - return new { ok = true, game = "FIGHTER" }; + session.Log.Info($"[DEBUG-API] launch minigame game={game}"); + return new { ok = true, game }; + } + + private async Task CloseMinigameAsync(CancellationToken token) + { + var session = hub.Current; + if (session is null) + return new { ok = false, error = "no live game session (is the client in the world?)" }; + + var cmd = "CMD:CLOSE_MINIGAME"; + await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token); + + session.Log.Info("[DEBUG-API] close minigame dispatched"); + return new { ok = true }; + } + + private static object MinigameCatalog() + { + return new + { + ok = true, + items = new[] + { + new { id = "KOF97", name = "🕹️ Arcade: The King of Fighters '97 (ตู้เกม KOF '97)", category = "arcade" }, + new { id = "METALSLUG", name = "🕹️ Arcade: Metal Slug (ตู้เกม Metal Slug ลุยด่าน)", category = "arcade" }, + new { id = "FIGHTER", name = "🥊 3D Arcade Fighter (มินิเกมต่อสู้ 3D)", category = "arcade" }, + new { id = "PIANO", name = "🎹 Grand Piano Interactive (เล่นเปียโนสด)", category = "music" }, + new { id = "DRUMKIT", name = "🥁 Drumkit Interactive (ตีกลองชุดสด)", category = "music" }, + new { id = "BOWLING", name = "🎳 Bowling Alley (โยนโบว์ลิ่ง)", category = "sports" }, + new { id = "BASKETBALL", name = "🏀 Street Basketball (ชูตบาสเกตบอล)", category = "sports" }, + } + }; } private static async Task ReadBodyAsync(HttpListenerRequest request, CancellationToken token) diff --git a/Ananta.Server/Ananta.App/DebugPanel/index.html b/Ananta.Server/Ananta.App/DebugPanel/index.html index 385fabc..9c22f13 100644 --- a/Ananta.Server/Ananta.App/DebugPanel/index.html +++ b/Ananta.Server/Ananta.App/DebugPanel/index.html @@ -264,12 +264,35 @@
-

🕹️ Arcade Minigame (ตู้เกมต่อสู้อาร์เคด)

-
- - L18 Street Fighter +

🕹️ Arcade & Interactive Minigames (ตู้เกมอาร์เคด & มินิเกมในโลก)

+
+ + +
-
เปิดระบบมินิเกมตู้เกม Arcade Fighter ในฉาก
+
+ + + + + + +
+
เปิดตู้เกมอาร์เคด (KOF '97, Metal Slug) หรือมินิเกมดนตรี/กีฬาในโลกได้ทันที พร้อมปุ่มควบคุมแบบเต็มหน้าจอ
@@ -739,9 +762,33 @@ async function playSelectedCutscene() { log('play cutscene: ' + JSON.stringify(r)); } +async function launchSelectedMinigame() { + const sel = document.getElementById('minigameSelect'); + const game = sel ? sel.value : 'KOF97'; + const r = await api('/api/minigame/launch', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ game }) + }); + log('launch minigame (' + game + '): ' + JSON.stringify(r)); +} + +async function launchMinigameQuick(game) { + const r = await api('/api/minigame/launch', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ game }) + }); + log('quick launch minigame (' + game + '): ' + JSON.stringify(r)); +} + +async function closeActiveMinigame() { + const r = await api('/api/minigame/close', { method: 'POST' }); + log('close minigame: ' + JSON.stringify(r)); +} + async function launchArcadeMinigame() { - const r = await api('/api/minigame/launch', { method: 'POST' }); - log('launch arcade minigame: ' + JSON.stringify(r)); + await launchSelectedMinigame(); } async function setTimePreset(hour, minute) {