From 81a0f65ea63b7552ceb151680a00c861bae0cdda Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Thu, 17 Sep 2026 00:59:06 +0700 Subject: [PATCH] fix(gameplay): resolve character switch, outfit hiding, weather, and locomotion issues --- .../proxy/public/fastpatch_4229938.zip | Bin 7890 -> 9563 bytes .../tools/generate_client_config_patch.js | 261 +++++++++++++++--- Ananta.Server/Ananta.App/DebugApiServer.cs | 28 ++ .../Ananta.App/DebugPanel/index.html | 48 +++- 4 files changed, 289 insertions(+), 48 deletions(-) diff --git a/Ananta.Proxy/proxy/public/fastpatch_4229938.zip b/Ananta.Proxy/proxy/public/fastpatch_4229938.zip index e5af67152c1d8fb73fd4681afbdb813342e94d8d..0bfce944289d2416ee248267f8b510ddae618a82 100644 GIT binary patch delta 7911 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 delta 6220 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 diff --git a/Ananta.Proxy/proxy/tools/generate_client_config_patch.js b/Ananta.Proxy/proxy/tools/generate_client_config_patch.js index 0dfd1e3..439d5a9 100644 --- a/Ananta.Proxy/proxy/tools/generate_client_config_patch.js +++ b/Ananta.Proxy/proxy/tools/generate_client_config_patch.js @@ -129,37 +129,39 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` local curFrame = UnityEngine.Time and UnityEngine.Time.frameCount or -1\n` + ` if curFrame == _lastHotkeyFrame then return end\n` + ` _lastHotkeyFrame = curFrame\n\n` + +` local UnityInput = (CS and CS.UnityEngine and CS.UnityEngine.Input) or (UnityEngine and UnityEngine.Input)\n` + +` local UnityKeyCode = (CS and CS.UnityEngine and CS.UnityEngine.KeyCode) or (UnityEngine and UnityEngine.KeyCode) or KeyCode\n` + ` local f8 = false\n` + ` local f7 = false\n` + ` local isShift = false\n` + -` pcall(function()\n` + -` f8 = UnityEngine.Input.GetKeyDown("f8")\n` + -` f7 = UnityEngine.Input.GetKeyDown("f7")\n` + -` isShift = UnityEngine.Input.GetKey("left shift") or UnityEngine.Input.GetKey("right shift")\n` + -` end)\n` + -` if not f8 and UnityEngine.KeyCode then\n` + -` pcall(function() f8 = UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.F8) end)\n` + -` end\n` + -` if not f7 and UnityEngine.KeyCode then\n` + -` pcall(function() f7 = UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.F7) end)\n` + -` end\n` + -` if not isShift and UnityEngine.KeyCode then\n` + -` pcall(function() isShift = UnityEngine.Input.GetKey(UnityEngine.KeyCode.LeftShift) or UnityEngine.Input.GetKey(UnityEngine.KeyCode.RightShift) end)\n` + -` end\n` + -` if not f8 and KeyCode then\n` + -` pcall(function() f8 = UnityEngine.Input.GetKeyDown(KeyCode.F8) end)\n` + -` end\n` + -` if not f7 and KeyCode then\n` + -` pcall(function() f7 = UnityEngine.Input.GetKeyDown(KeyCode.F7) end)\n` + -` end\n` + -` if not f8 then\n` + -` pcall(function() f8 = UnityEngine.Input.GetKeyDown(289) end)\n` + -` end\n` + -` if not f7 then\n` + -` pcall(function() f7 = UnityEngine.Input.GetKeyDown(288) end)\n` + -` end\n` + -` if not isShift then\n` + -` pcall(function() isShift = UnityEngine.Input.GetKey(304) or UnityEngine.Input.GetKey(303) end)\n` + +` if UnityInput then\n` + +` if UnityKeyCode and UnityKeyCode.F8 then\n` + +` pcall(function() f8 = UnityInput.GetKeyDown(UnityKeyCode.F8) end)\n` + +` end\n` + +` if UnityKeyCode and UnityKeyCode.F7 then\n` + +` pcall(function() f7 = UnityInput.GetKeyDown(UnityKeyCode.F7) end)\n` + +` end\n` + +` if not f8 then\n` + +` pcall(function() f8 = UnityInput.GetKeyDown(289) end)\n` + +` end\n` + +` if not f7 then\n` + +` pcall(function() f7 = UnityInput.GetKeyDown(288) end)\n` + +` end\n` + +` if not f8 then\n` + +` pcall(function() f8 = UnityInput.GetKeyDown("f8") end)\n` + +` end\n` + +` if not f7 then\n` + +` pcall(function() f7 = UnityInput.GetKeyDown("f7") end)\n` + +` end\n` + +` if UnityKeyCode and UnityKeyCode.LeftShift then\n` + +` pcall(function() isShift = UnityInput.GetKey(UnityKeyCode.LeftShift) or UnityInput.GetKey(UnityKeyCode.RightShift) end)\n` + +` end\n` + +` if not isShift then\n` + +` pcall(function() isShift = UnityInput.GetKey(304) or UnityInput.GetKey(303) end)\n` + +` end\n` + +` if not isShift then\n` + +` pcall(function() isShift = UnityInput.GetKey("left shift") or UnityInput.GetKey("right shift") end)\n` + +` end\n` + ` end\n\n` + ` if f8 then\n` + ` if ProcessDebugCommand then ProcessDebugCommand("TOGGLE_CLOTHES") end\n` + @@ -171,6 +173,30 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` if ProcessDebugCommand then ProcessDebugCommand("SPAWN_ENEMY") end\n` + ` end\n` + ` end\n` + +` pcall(function()\n` + +` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` if myUnit then\n` + +` local hasMove = UnityEngine.Input.GetKey(119) or UnityEngine.Input.GetKey(97) or UnityEngine.Input.GetKey(115) or UnityEngine.Input.GetKey(100)\n` + +` if hasMove then\n` + +` if CS and CS.LX6 and CS.LX6.Units and CS.LX6.Units.Module and CS.LX6.Units.Module.LockMoveDirModule then\n` + +` CS.LX6.Units.Module.LockMoveDirModule.ClearLockMoveDir(myUnit)\n` + +` end\n` + +` end\n` + +` if gCS and gCS.PaoKuManager and gCS.PaoKuManager.ParkourStateLua == 25 then\n` + +` gCS.PaoKuManager.enableAccSpeed = true\n` + +` if CS and CS.LX6 and CS.LX6.Units and CS.LX6.Units.Module and CS.LX6.Units.Module.MotoModule then\n` + +` local motoMod = myUnit:GetModule(typeof(CS.LX6.Units.Module.MotoModule))\n` + +` if motoMod then\n` + +` motoMod.acceleratorValue = 1.0\n` + +` local isShiftKey = UnityEngine.Input.GetKey(304) or UnityEngine.Input.GetKey(303)\n` + +` if isShiftKey then\n` + +` motoMod.isMotoRush = true\n` + +` end\n` + +` end\n` + +` end\n` + +` end\n` + +` end\n` + +` end)\n` + ` end)\n` + `end\n\n` + `-- Bypass CBT date expiration for Gacha pools and unlock all map/systems\n` + @@ -339,6 +365,85 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` if oldHudUpdate then return oldHudUpdate(self) end\n` + ` end\n` + ` end\n` + +` local hintStore = GroupName2Class and GroupName2Class.HintInfosHudStore or C_HintInfosHudStore\n` + +` if hintStore and not hintStore._filterPlayerFHooked then\n` + +` hintStore._filterPlayerFHooked = true\n` + +` local origRefreshPc = hintStore.RefreshPcBtnShow\n` + +` hintStore.RefreshPcBtnShow = function(self, force)\n` + +` pcall(function()\n` + +` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` local btnMgr = L50 and L50.L50App and L50.L50App.L50Game and L50.L50App.L50Game.InteractBtnMgr\n` + +` if myUnit and btnMgr and btnMgr.usefulList then\n` + +` local myPid = myUnit.Pid\n` + +` local i = 0\n` + +` while i < btnMgr.usefulList.Count do\n` + +` local item = btnMgr.usefulList[i]\n` + +` if item and (item.pid == myPid or item.targetPid == myPid or (item.target and item.target == myUnit.PlayerObj)) then\n` + +` btnMgr.usefulList:RemoveAt(i)\n` + +` else\n` + +` i = i + 1\n` + +` end\n` + +` end\n` + +` end\n` + +` end)\n` + +` if origRefreshPc then return origRefreshPc(self, force) end\n` + +` end\n` + +` end\n` + +` local switchMgr = gSwitchSpiritManager or (GroupName2Class and GroupName2Class.SwitchSpiritManager)\n` + +` if switchMgr and not switchMgr._fixSwapHooked then\n` + +` switchMgr._fixSwapHooked = true\n` + +` local origBeforeSet = switchMgr.BeforeSetChangeUnit\n` + +` switchMgr.BeforeSetChangeUnit = function(self, spiritUnitPid, noClearold)\n` + +` local oldUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` local res = origBeforeSet and origBeforeSet(self, spiritUnitPid, noClearold)\n` + +` pcall(function()\n` + +` local newUnit = gCS and gCS.SceneDataMgr and gCS.SceneDataMgr.GetUnit(spiritUnitPid)\n` + +` if newUnit then\n` + +` if CS and CS.LX6 and CS.LX6.Units and CS.LX6.Units.Module and CS.LX6.Units.Module.LockMoveDirModule then\n` + +` CS.LX6.Units.Module.LockMoveDirModule.ClearLockMoveDir(newUnit)\n` + +` end\n` + +` if newUnit.RootMotionLockMoveAndRotate then\n` + +` pcall(function() newUnit.RootMotionLockMoveAndRotate:ClearAll() end)\n` + +` pcall(function() newUnit.RootMotionLockMoveAndRotate:ClearLockExtraRotation() end)\n` + +` end\n` + +` if newUnit.State then\n` + +` newUnit.State.nowInteractiveAction = 0\n` + +` newUnit.State.isFree = true\n` + +` end\n` + +` if gCS and gCS.BattleManager and gCS.BattleManager.RefreshAllSkills then\n` + +` gCS.BattleManager.RefreshAllSkills(false, false)\n` + +` end\n` + +` local btnMgr = L50 and L50.L50App and L50.L50App.L50Game and L50.L50App.L50Game.InteractBtnMgr\n` + +` if btnMgr then\n` + +` pcall(function() btnMgr:RemoveBtnByType(newUnit.Pid, 1) end)\n` + +` pcall(function() btnMgr:RemoveBtnByType(newUnit.Pid, 2) end)\n` + +` end\n` + +` end\n` + +` if oldUnit and oldUnit.Pid ~= spiritUnitPid then\n` + +` local btnMgr = L50 and L50.L50App and L50.L50App.L50Game and L50.L50App.L50Game.InteractBtnMgr\n` + +` if btnMgr and CS and CS.LX6 and CS.LX6.Interact then\n` + +` pcall(function()\n` + +` btnMgr:RemoveBtnByType(oldUnit.Pid, 1)\n` + +` local btnInfo = CS.LX6.Interact.UnitBtnInfo and CS.LX6.Interact.UnitBtnInfo.New()\n` + +` if btnInfo then\n` + +` btnInfo.pid = oldUnit.Pid\n` + +` btnInfo.target = oldUnit.PlayerObj\n` + +` btnInfo.text = "Switch Character"\n` + +` btnInfo.isAvailable = true\n` + +` btnInfo.DoClick = function()\n` + +` if gSwitchSpiritManager then\n` + +` gSwitchSpiritManager:BeforeSetChangeUnit(oldUnit.Pid, false)\n` + +` end\n` + +` end\n` + +` btnMgr:AddBtn(btnInfo)\n` + +` end\n` + +` end)\n` + +` end\n` + +` end\n` + +` end)\n` + +` return res\n` + +` end\n` + +` end\n` + ` if gBuyHouseUtils then\n` + ` gBuyHouseUtils.CheckHasBuyTheHouse = function(houseId) return true end\n` + ` gBuyHouseUtils.CheckBuyHouseMoneyEnough = function(houseId) return true end\n` + @@ -364,7 +469,7 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` pcall(function()\n` + ` if gCS and gCS.TransitionMgr then gCS.TransitionMgr.showMainCube = true end\n` + ` if gCS and gCS.CameraDataMgr and gCS.CameraDataMgr.cinemachineManager then\n` + -` gCS.CameraDataMgr.cinemachineManager:SwitchSelfiePhotoMode(true, 0.2)\n` + +` gCS.CameraDataMgr.cinemachineManager:SwitchSelfiePhotoMode(false, 0.2)\n` + ` gCS.CameraDataMgr.cinemachineManager:SetFov(68, 0, 0, false)\n` + ` end\n` + ` end)\n` + @@ -641,24 +746,63 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` local unit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + ` if not unit or not unit.PlayerObj then return end\n` + ` _G._clothesHidden = not _G._clothesHidden\n` + +` local fs = nil\n` + +` pcall(function()\n` + +` if CS and CS.LX6 and CS.LX6.Share and CS.LX6.Share.FashionSlot then\n` + +` fs = unit.PlayerObj:GetComponentInChildren(typeof(CS.LX6.Share.FashionSlot))\n` + +` end\n` + +` end)\n` + +` if not fs then\n` + +` pcall(function()\n` + +` fs = unit.FashionSlot or (unit.ModelSlot and unit.ModelSlot.FashionSlot)\n` + +` end)\n` + +` end\n` + +` if fs then\n` + +` pcall(function()\n` + +` local clothes = { fs.Cloth, fs.Bottom, fs.Gloves, fs.Shoes, fs.Sleeve, fs.Dress, fs.Bag, fs.Hood, fs.Belt, fs.Necklace }\n` + +` for _, r in ipairs(clothes) do\n` + +` if r then r.enabled = not _G._clothesHidden end\n` + +` end\n` + +` if fs.SpProps then\n` + +` for i = 0, fs.SpProps.Count - 1 do\n` + +` local r = fs.SpProps[i]\n` + +` if r then r.enabled = not _G._clothesHidden end\n` + +` end\n` + +` end\n` + +` if fs.allFashionRendererList then\n` + +` for i = 0, fs.allFashionRendererList.Count - 1 do\n` + +` local r = fs.allFashionRendererList[i]\n` + +` if r then r.enabled = not _G._clothesHidden end\n` + +` end\n` + +` end\n` + +` local keep = { fs.Face, fs.Hair, fs.Tail, fs.Hair01, fs.Hair02, fs.Ear }\n` + +` for _, r in ipairs(keep) do\n` + +` if r then r.enabled = true end\n` + +` end\n` + +` end)\n` + +` end\n` + ` local smrs = unit.PlayerObj:GetComponentsInChildren(typeof(UnityEngine.SkinnedMeshRenderer), true)\n` + ` if smrs then\n` + ` for i = 0, smrs.Length - 1 do\n` + ` local smr = smrs[i]\n` + -` local n = string.lower(smr.name)\n` + -` local isBaseBody = string.find(n, "body") or string.find(n, "face") or string.find(n, "head") or string.find(n, "hair") or string.find(n, "eye") or string.find(n, "skin") or string.find(n, "shenti") or string.find(n, "tou") or string.find(n, "lian")\n` + -` local isClothing = string.find(n, "cloth") or string.find(n, "coat") or string.find(n, "skirt") or string.find(n, "pant") or string.find(n, "dress") or string.find(n, "top") or string.find(n, "bottom") or string.find(n, "jacket") or string.find(n, "yifu") or string.find(n, "kuzi") or string.find(n, "qun") or string.find(n, "shoe") or string.find(n, "sock") or string.find(n, "hat") or string.find(n, "wa") or string.find(n, "xie") or string.find(n, "under") or string.find(n, "suit") or string.find(n, "acc")\n` + -` if isClothing and not (string.find(n, "body") and not string.find(n, "cloth")) then\n` + -` smr.enabled = not _G._clothesHidden\n` + -` elseif isBaseBody then\n` + -` smr.enabled = true\n` + -` else\n` + -` smr.enabled = not _G._clothesHidden\n` + +` if smr then\n` + +` local n = string.lower(smr.name)\n` + +` local isBaseBody = string.find(n, "body") or string.find(n, "face") or string.find(n, "head") or string.find(n, "hair") or string.find(n, "eye") or string.find(n, "skin") or string.find(n, "shenti") or string.find(n, "tou") or string.find(n, "lian") or string.find(n, "arm") or string.find(n, "hand") or string.find(n, "leg") or string.find(n, "foot") or string.find(n, "feet") or string.find(n, "shou") or string.find(n, "bi") or string.find(n, "tui") or string.find(n, "limb") or string.find(n, "torso") or string.find(n, "base") or string.find(n, "flesh") or string.find(n, "mouth") or string.find(n, "brow") or string.find(n, "ear") or string.find(n, "nose") or string.find(n, "tooth") or string.find(n, "teeth") or string.find(n, "tongue")\n` + +` local isClothing = string.find(n, "cloth") or string.find(n, "coat") or string.find(n, "skirt") or string.find(n, "pant") or string.find(n, "dress") or string.find(n, "top") or string.find(n, "bottom") or string.find(n, "jacket") or string.find(n, "yifu") or string.find(n, "kuzi") or string.find(n, "qun") or string.find(n, "shoe") or string.find(n, "sock") or string.find(n, "hat") or string.find(n, "wa") or string.find(n, "xie") or string.find(n, "under") or string.find(n, "suit") or string.find(n, "acc") or string.find(n, "hood") or string.find(n, "belt") or string.find(n, "sleeve") or string.find(n, "glove") or string.find(n, "bag") or string.find(n, "prop") or string.find(n, "fashion")\n` + +` if isBaseBody and not (isClothing and not string.find(n, "body") and not string.find(n, "arm") and not string.find(n, "hand") and not string.find(n, "leg")) then\n` + +` smr.enabled = true\n` + +` elseif isClothing then\n` + +` smr.enabled = not _G._clothesHidden\n` + +` else\n` + +` if not _G._clothesHidden then\n` + +` smr.enabled = true\n` + +` end\n` + +` end\n` + ` end\n` + ` end\n` + ` end\n` + ` pcall(function()\n` + -` local tip = _G._clothesHidden and "Outfit Hidden (Base Body)" or "Outfit Shown"\n` + +` local tip = _G._clothesHidden and "Outfit Hidden (Base Body Only)" or "Outfit Shown"\n` + ` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + ` gDisplayMessageMgr:ShowMessageContent(tip)\n` + ` end\n` + @@ -667,6 +811,45 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` end\n` + ` end)\n` + ` end)\n` + +` elseif string.sub(cmd, 1, 8) == "SET_FOG:" then\n` + +` local arg = string.sub(cmd, 9)\n` + +` local density = tonumber(arg) or 0\n` + +` pcall(function()\n` + +` if CS and CS.CTT3 and CS.CTT3.Weather and CS.CTT3.Weather.WeatherBridge then\n` + +` if density > 0 then\n` + +` CS.CTT3.Weather.WeatherBridge.EnableExternalExpFogDensity(density)\n` + +` CS.CTT3.Weather.WeatherBridge.EnableExternalSkyFogDensity(density)\n` + +` else\n` + +` CS.CTT3.Weather.WeatherBridge.DisableExternalExpFogDensity()\n` + +` CS.CTT3.Weather.WeatherBridge.DisableExternalSkyFogDensity()\n` + +` end\n` + +` end\n` + +` local tip = density > 0 and ("Fog Set: " .. tostring(density)) or "Fog Cleared"\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` + +` elseif string.sub(cmd, 1, 12) == "SET_WEATHER:" then\n` + +` local arg = string.sub(cmd, 13)\n` + +` local wId = tonumber(arg) or 1\n` + +` pcall(function()\n` + +` if CS and CS.CTT3 and CS.CTT3.Weather and CS.CTT3.Weather.WeatherBridge then\n` + +` CS.CTT3.Weather.WeatherBridge.SetWeather(wId, 2.0)\n` + +` end\n` + +` if gCS and gCS.WeatherManager and gCS.WeatherManager.SetWeather then\n` + +` gCS.WeatherManager:SetWeather(wId)\n` + +` end\n` + +` local tip = "Weather Set: " .. tostring(wId)\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` + `end\n\n` + `local function HookNoticeTable(tbl)\n` + diff --git a/Ananta.Server/Ananta.App/DebugApiServer.cs b/Ananta.Server/Ananta.App/DebugApiServer.cs index 42feb05..27bd337 100644 --- a/Ananta.Server/Ananta.App/DebugApiServer.cs +++ b/Ananta.Server/Ananta.App/DebugApiServer.cs @@ -120,6 +120,8 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub await WriteJsonAsync(ctx, await TimeSetAsync(await ReadBodyAsync(ctx.Request, token), token), token); else if (method == "POST" && path == "/api/weather/set") await WriteJsonAsync(ctx, await WeatherSetAsync(await ReadBodyAsync(ctx.Request, token), token), token); + else if (method == "POST" && path == "/api/weather/fog") + await WriteJsonAsync(ctx, await WeatherFogAsync(await ReadBodyAsync(ctx.Request, token), token), token); else if (method == "POST" && path == "/api/world/switch-scene") await WriteJsonAsync(ctx, await SwitchSceneAsync(await ReadBodyAsync(ctx.Request, token), token), token); else if (method == "POST" && path == "/api/unstuck/blackscreen") @@ -902,6 +904,32 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub return new { ok = true, weatherId, transition }; } + private async Task WeatherFogAsync(string json, 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?)" }; + + float density = 0.08f; + try + { + using var doc = JsonDocument.Parse(string.IsNullOrWhiteSpace(json) ? "{}" : json); + var root = doc.RootElement; + if (root.TryGetProperty("density", out var pd)) + density = pd.GetSingle(); + } + catch (Exception ex) + { + return new { ok = false, error = $"bad request: {ex.Message}" }; + } + + var cmd = $"CMD:SET_FOG:{density.ToString(System.Globalization.CultureInfo.InvariantCulture)}"; + await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token); + + session.Log.Info($"[DEBUG-API] fog set density={density}"); + return new { ok = true, density }; + } + private async Task UnstuckBlackScreenAsync(CancellationToken token) { var session = hub.Current; diff --git a/Ananta.Server/Ananta.App/DebugPanel/index.html b/Ananta.Server/Ananta.App/DebugPanel/index.html index 171c601..2a0469c 100644 --- a/Ananta.Server/Ananta.App/DebugPanel/index.html +++ b/Ananta.Server/Ananta.App/DebugPanel/index.html @@ -37,25 +37,37 @@
12:00
00:00 — night  ·  06:00 — morning  ·  12:00 — noon  ·  18:00 — evening  ·  23:59 — night
+
+ + + + + +

Weather / Atmosphere (สภาพอากาศ)

- - - - - - - + + + + + +
+
+
🌫️ Volumetric Exponential Fog (หมอกหนา / ทัศนวิสัย):
+ + + +
ปรับความหนาแน่นหมอกแบบเรียลไทม์ผ่าน WeatherBridge.EnableExternalExpFogDensity
- Custom Weather ID: + Custom Weather ID: Transition (sec):
-
ซิงค์สภาพอากาศของโลกและบรรยากาศ client ทันที
+
ซิงค์สภาพอากาศของโลก (WeatherConfig 1..5) และบรรยากาศ client ทันที
@@ -444,6 +456,18 @@ async function toggleClothes() { log('toggle clothes: ' + JSON.stringify(r)); } +async function setTimePreset(hour, minute) { + const slider = document.getElementById('timeslider'); + if (slider) { + slider.value = hour * 60 + minute; + const clock = document.getElementById('clock'); + if (clock) clock.textContent = fmtClock(hour * 60 + minute); + } + const body = { hour, minute, fix: true, transition: 5 }; + const r = await api('/api/time/set', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); + log('time preset: ' + JSON.stringify(r)); +} + async function setWeather(weatherId) { const transition = parseInt(document.getElementById('weatherTransition')?.value || '5', 10); const body = { weatherId: parseInt(weatherId, 10), transition }; @@ -451,6 +475,12 @@ async function setWeather(weatherId) { log('set weather: ' + JSON.stringify(r)); } +async function setFog(density) { + const body = { density: parseFloat(density) }; + const r = await api('/api/weather/fog', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); + log('set fog: ' + JSON.stringify(r)); +} + async function setCustomWeather() { const id = parseInt(document.getElementById('customWeatherId').value || '1', 10); await setWeather(id);