feat(all): complete map fog unlock, outfit toggle F8, gacha draws, monster combat, cutscenes catalog, and arcade minigame
This commit is contained in:
1 parent
3853760c34
commit
75a1d91df0
9 files changed
+242
-29
No files matched your search
Binary file not shown.
@@ -177,7 +177,7 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` end)\n` +
|
||||
` pcall(function()\n` +
|
||||
` if gTakePhotoUtils then\n` +
|
||||
` gTakePhotoUtils.isDebugForce = true\n` +
|
||||
` gTakePhotoUtils.isDebugForce = false\n` +
|
||||
` gTakePhotoUtils.PhotoPermission = true\n` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
@@ -196,10 +196,10 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` end\n` +
|
||||
` if LX6 and LX6.Gps and LX6.Gps.MapFogDataMgr then\n` +
|
||||
` LX6.Gps.MapFogDataMgr.IsInFog = function(sceneId, x, z) return false end\n` +
|
||||
` pcall(function() LX6.Gps.MapFogDataMgr.SyncUnlockScene(101, true) end)\n` +
|
||||
` pcall(function() LX6.Gps.MapFogDataMgr.SyncUnlockScene(102, true) end)\n` +
|
||||
` pcall(function() LX6.Gps.MapFogDataMgr.SyncUnlockScene(103, true) end)\n` +
|
||||
` pcall(function() LX6.Gps.MapFogDataMgr.SyncUnlockScene(1001, true) end)\n` +
|
||||
` local unlockScenes = { 1, 101, 102, 103, 1001, 10001, 20001125, 20001222, 20001223, 23300888, 23300999 }\n` +
|
||||
` for _, sid in ipairs(unlockScenes) do\n` +
|
||||
` pcall(function() LX6.Gps.MapFogDataMgr.SyncUnlockScene(sid, true) end)\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` if C_MapView_Fog then\n` +
|
||||
` C_MapView_Fog.InFog = function(self, instanceId) return false end\n` +
|
||||
@@ -212,11 +212,36 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` if mapStore and not mapStore._teleportHooked then\n` +
|
||||
` mapStore._teleportHooked = true\n` +
|
||||
` local origOnShow = mapStore.OnShow\n` +
|
||||
` local function hideFogRecursively(t)\n` +
|
||||
` if not t then return end\n` +
|
||||
` local n = string.lower(t.name or "")\n` +
|
||||
` if string.find(n, "fog") or string.find(n, "cloud") or string.find(n, "mask") then\n` +
|
||||
` pcall(function() t.gameObject:SetActive(false) end)\n` +
|
||||
` end\n` +
|
||||
` local count = t.childCount or 0\n` +
|
||||
` for i = 0, count - 1 do\n` +
|
||||
` local child = t:GetChild(i)\n` +
|
||||
` if child then hideFogRecursively(child) end\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` mapStore.OnShow = function(self, ...)\n` +
|
||||
` self._pendingPin = nil\n` +
|
||||
` pcall(function()\n` +
|
||||
` if self.fogNode then self.fogNode:SetActive(false) end\n` +
|
||||
` if self.fogRoot then self.fogRoot:SetActive(false) end\n` +
|
||||
` if self.bindData and self.bindData.bigWorldBg then\n` +
|
||||
` local bg = self.bindData.bigWorldBg\n` +
|
||||
` if bg.instFogRoot and bg.instFogRoot.gameObject then\n` +
|
||||
` bg.instFogRoot.gameObject:SetActive(false)\n` +
|
||||
` end\n` +
|
||||
` if bg.transform then hideFogRecursively(bg.transform) end\n` +
|
||||
` end\n` +
|
||||
` if self.bindData and self.bindData.rootRT then\n` +
|
||||
` hideFogRecursively(self.bindData.rootRT)\n` +
|
||||
` end\n` +
|
||||
` if self.transform then\n` +
|
||||
` hideFogRecursively(self.transform)\n` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
` if origOnShow then return origOnShow(self, ...) end\n` +
|
||||
` end\n` +
|
||||
@@ -661,6 +686,20 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` if CS.LX6.Engine.ResourceManager.IsNeedWarmupPSO then CS.LX6.Engine.ResourceManager.IsNeedWarmupPSO = function() return false end end\n` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
` pcall(function()\n` +
|
||||
` if UpdateBeat and UpdateBeat.Add and not _G._hotkeyF8Registered then\n` +
|
||||
` _G._hotkeyF8Registered = true\n` +
|
||||
` UpdateBeat:Add(function()\n` +
|
||||
` pcall(function()\n` +
|
||||
` if UnityEngine and UnityEngine.Input then\n` +
|
||||
` if UnityEngine.Input.GetKeyDown(289) or (UnityEngine.KeyCode and UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.F8)) then\n` +
|
||||
` ProcessDebugCommand("TOGGLE_CLOTHES")\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
` end)\n` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
`end\n\n` +
|
||||
`ProcessDebugCommand = function(cmd)\n` +
|
||||
` if not cmd or type(cmd) ~= "string" then return end\n` +
|
||||
@@ -754,11 +793,49 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
` end)\n` +
|
||||
` elseif cmd == "LAUNCH_MINIGAME:FIGHTER" then\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` +
|
||||
` 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` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
` elseif cmd == "TOGGLE_CLOTHES" then\n` +
|
||||
` pcall(function()\n` +
|
||||
` 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` +
|
||||
` pcall(function()\n` +
|
||||
` if CS and CS.LX6 and CS.LX6.Share and CS.LX6.Share.FashionPartSlot then\n` +
|
||||
` local partSlots = unit.PlayerObj:GetComponentsInChildren(typeof(CS.LX6.Share.FashionPartSlot), true)\n` +
|
||||
` if partSlots then\n` +
|
||||
` for i = 0, partSlots.Length - 1 do\n` +
|
||||
` local ps = partSlots[i]\n` +
|
||||
` if ps then\n` +
|
||||
` local psName = string.lower((ps.gameObject and ps.gameObject.name) or "")\n` +
|
||||
` local isHeadOrBody = string.find(psName, "head") or string.find(psName, "face") or string.find(psName, "body") or string.find(psName, "skin")\n` +
|
||||
` if not isHeadOrBody then\n` +
|
||||
` if ps.gameObject then ps.gameObject:SetActive(not _G._clothesHidden) end\n` +
|
||||
` if ps.selfRenderer then ps.selfRenderer.enabled = not _G._clothesHidden end\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` end)\n` +
|
||||
` local fs = nil\n` +
|
||||
` pcall(function()\n` +
|
||||
` if CS and CS.LX6 and CS.LX6.Share and CS.LX6.Share.FashionSlot then\n` +
|
||||
@@ -772,6 +849,12 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` end\n` +
|
||||
` if fs then\n` +
|
||||
` pcall(function()\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 clothesSlots = { fs.Cloth, fs.Bottom, fs.Dress, fs.Bag, fs.Hood, fs.Belt, fs.Necklace }\n` +
|
||||
` for _, r in ipairs(clothesSlots) do\n` +
|
||||
` if r then r.enabled = not _G._clothesHidden end\n` +
|
||||
@@ -793,7 +876,7 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` for i = 0, smrs.Length - 1 do\n` +
|
||||
` local smr = smrs[i]\n` +
|
||||
` if smr then\n` +
|
||||
` local n = string.lower(smr.name)\n` +
|
||||
` local n = string.lower(smr.name or "")\n` +
|
||||
` local isBodyLimb = string.find(n, "face") or string.find(n, "head") or string.find(n, "hair") or string.find(n, "eye") or string.find(n, "brow") or string.find(n, "mouth") or string.find(n, "ear") or string.find(n, "tail") or string.find(n, "arm") or string.find(n, "hand") or string.find(n, "glove") or string.find(n, "sleeve") or string.find(n, "leg") or string.find(n, "foot") or string.find(n, "feet") or string.find(n, "shoe") or string.find(n, "boot") or string.find(n, "skin") or string.find(n, "flesh") or string.find(n, "shenti") or string.find(n, "tou") or string.find(n, "lian") or string.find(n, "shou") or string.find(n, "bi") or string.find(n, "tui")\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, "jacket") or string.find(n, "yifu") or string.find(n, "kuzi") or string.find(n, "qun") or string.find(n, "hood") or string.find(n, "cape") or string.find(n, "cloak") or string.find(n, "belt") or string.find(n, "bag") or string.find(n, "prop") or string.find(n, "necklace") or string.find(n, "acc")\n` +
|
||||
` if isBodyLimb and not (isClothing and not string.find(n, "arm") and not string.find(n, "hand") and not string.find(n, "leg") and not string.find(n, "foot") and not string.find(n, "face")) then\n` +
|
||||
@@ -809,7 +892,7 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") +
|
||||
` end\n` +
|
||||
` end\n` +
|
||||
` pcall(function()\n` +
|
||||
` local tip = _G._clothesHidden and "Clothes Hidden (Body Visible)" or "Clothes Shown"\n` +
|
||||
` local tip = _G._clothesHidden and "Outfit Hidden / ร่างต้น (F8)" or "Outfit Shown / แสดงชุดปกติ (F8)"\n` +
|
||||
` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` +
|
||||
` gDisplayMessageMgr:ShowMessageContent(tip)\n` +
|
||||
` end\n` +
|
||||
|
||||
@@ -137,6 +137,10 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
await WriteJsonAsync(ctx, await PlayCutsceneAsync(await ReadBodyAsync(ctx.Request, token), token), token);
|
||||
else if (method == "POST" && path == "/api/timeline/play")
|
||||
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 == "GET" && path == "/api/cutscene/catalog")
|
||||
await WriteJsonAsync(ctx, CutsceneCatalog(), token);
|
||||
else if (method == "POST" && path == "/api/npc/spawn")
|
||||
await WriteJsonAsync(ctx, await NpcSpawnAsync(await ReadBodyAsync(ctx.Request, token), token), token);
|
||||
else if (method == "GET" && path == "/api/scenes")
|
||||
@@ -1241,6 +1245,41 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
return new { ok = true };
|
||||
}
|
||||
|
||||
private static object CutsceneCatalog()
|
||||
{
|
||||
return new
|
||||
{
|
||||
ok = true,
|
||||
items = new[]
|
||||
{
|
||||
new { id = "TL_SEYM_010_Q010_S01", name = "Seymour Story Quest: Chapter 1 S01 (เนื้อเรื่องหลัก)" },
|
||||
new { id = "SwitchChar_tafei_01", name = "Taffy Cinematic Arrival (ทาฟี่ โดดจากตึก)" },
|
||||
new { id = "SwitchChar_dila_03", name = "Dila Combat Cinematic (ดิลา แอกชันสตรีท)" },
|
||||
new { id = "SwitchChar_saimo_05", name = "Seymour Street Arrival (เซย์มัวร์ สไตล์คูล)" },
|
||||
new { id = "SwitchChar_lixi_04", name = "Richie Motorcycle Rush (ริชชี่ ซิ่งมอเตอร์ไซค์)" },
|
||||
new { id = "SwitchChar_nanzhujue_02", name = "Male MC Arrival A (พระเอก สลับตัว A)" },
|
||||
new { id = "SwitchChar_nanzhujue_03", name = "Male MC Arrival B (พระเอก สลับตัว B)" },
|
||||
new { id = "SwitchChar_common_01", name = "Metropolis Skyline Overview (มุมกล้องเมือง 1)" },
|
||||
new { id = "SwitchChar_common_04", name = "Street Parkour Traverse (มุมกล้องฟรีรันนิง 4)" },
|
||||
new { id = "SwitchChar_common_10", name = "Night Neon Lights (มุมกล้องวิวกลางคืน 10)" },
|
||||
new { id = "SwitchChar_common_27", name = "Dynamic Roof Drop (แอกชันดรอป 27)" },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<object> LaunchMinigameAsync(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?)" };
|
||||
|
||||
var cmd = "CMD:LAUNCH_MINIGAME:FIGHTER";
|
||||
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" };
|
||||
}
|
||||
|
||||
private static async Task<string> ReadBodyAsync(HttpListenerRequest request, CancellationToken token)
|
||||
{
|
||||
if (!request.HasEntityBody)
|
||||
|
||||
@@ -235,10 +235,41 @@
|
||||
|
||||
<div class="card" style="border-color:#ab47bc">
|
||||
<h2 style="color:#ce93d8">👗 Outfit / Appearance (สลับชุด / ซ่อนเสื้อผ้า)</h2>
|
||||
<div>
|
||||
<button style="background:#8e24aa;color:white" onclick="toggleClothes()">Toggle Clothes / ซ่อน-แสดงชุด</button>
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<button style="background:#8e24aa;color:white" onclick="toggleClothes()">Toggle Clothes / ซ่อน-แสดงชุด (F8)</button>
|
||||
<span class="badge" style="background:#4a148c;color:#e1bee7">กดปุ่ม F8 ในเกมได้</span>
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">สลับการแสดงผลเสื้อผ้า (SkinnedMeshRenderer) ของตัวละครที่กำลังเล่นอยู่ โดยยังคงแสดงโมเดลบอดี้เดิมไว้</div>
|
||||
<div class="muted" style="margin-top:6px">สลับการแสดงผลเสื้อผ้าและ Dynamic Bone ของชุดชั้นนอกอย่างหมดจด โดยยังคงโมเดลบอดี้เดิมไว้ 100% ไร้ชิ้นส่วนลอยค้าง</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-color:#00bcd4">
|
||||
<h2 style="color:#4dd0e1">🎬 3D Cinematics & Story Timelines (ฉากคัทซีนเนื้อเรื่อง)</h2>
|
||||
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
||||
<select id="cutsceneSelect" style="flex:1;min-width:260px;background:#263238;color:#eee;border:1px solid #37474f;border-radius:4px;padding:6px">
|
||||
<option value="TL_SEYM_010_Q010_S01">Seymour Story Quest: Chapter 1 S01 (เนื้อเรื่องหลัก)</option>
|
||||
<option value="SwitchChar_tafei_01">Taffy Cinematic Arrival (ทาฟี่ โดดจากตึก)</option>
|
||||
<option value="SwitchChar_dila_03">Dila Combat Cinematic (ดิลา แอกชันสตรีท)</option>
|
||||
<option value="SwitchChar_saimo_05">Seymour Street Arrival (เซย์มัวร์ สไตล์คูล)</option>
|
||||
<option value="SwitchChar_lixi_04">Richie Motorcycle Rush (ริชชี่ ซิ่งมอเตอร์ไซค์)</option>
|
||||
<option value="SwitchChar_nanzhujue_02">Male MC Arrival A (พระเอก สลับตัว A)</option>
|
||||
<option value="SwitchChar_nanzhujue_03">Male MC Arrival B (พระเอก สลับตัว B)</option>
|
||||
<option value="SwitchChar_common_01">Metropolis Skyline Overview (มุมกล้องเมือง 1)</option>
|
||||
<option value="SwitchChar_common_04">Street Parkour Traverse (มุมกล้องฟรีรันนิง 4)</option>
|
||||
<option value="SwitchChar_common_10">Night Neon Lights (มุมกล้องวิวกลางคืน 10)</option>
|
||||
<option value="SwitchChar_common_27">Dynamic Roof Drop (แอกชันดรอป 27)</option>
|
||||
</select>
|
||||
<button style="background:#00838f;color:white" onclick="playSelectedCutscene()">Play Cinematic / เล่นคัทซีน</button>
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">สั่งเล่นฉาก Cinematic Timeline 3D ภายใน Engine ของเกมทันทีแบบเรียลไทม์</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-color:#ff9800">
|
||||
<h2 style="color:#ffb74d">🕹️ Arcade Minigame (ตู้เกมต่อสู้อาร์เคด)</h2>
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<button style="background:#e65100;color:white" onclick="launchArcadeMinigame()">Launch Arcade Fighter / เปิดตู้เกมต่อสู้</button>
|
||||
<span class="badge" style="background:#bf360c;color:#ffccbc">L18 Street Fighter</span>
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">เปิดระบบมินิเกมตู้เกม Arcade Fighter ในฉาก</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-color:#e53935">
|
||||
@@ -661,6 +692,22 @@ async function toggleClothes() {
|
||||
log('toggle clothes: ' + JSON.stringify(r));
|
||||
}
|
||||
|
||||
async function playSelectedCutscene() {
|
||||
const sel = document.getElementById('cutsceneSelect');
|
||||
const name = sel ? sel.value : 'TL_SEYM_010_Q010_S01';
|
||||
const r = await api('/api/timeline/play', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name })
|
||||
});
|
||||
log('play cutscene: ' + JSON.stringify(r));
|
||||
}
|
||||
|
||||
async function launchArcadeMinigame() {
|
||||
const r = await api('/api/minigame/launch', { method: 'POST' });
|
||||
log('launch arcade minigame: ' + JSON.stringify(r));
|
||||
}
|
||||
|
||||
async function setTimePreset(hour, minute) {
|
||||
const slider = document.getElementById('timeslider');
|
||||
if (slider) {
|
||||
|
||||
@@ -161,11 +161,19 @@ internal static class RuntimePayloadFactory
|
||||
root.InfoAchievement.SceneFogMaps = new Dictionary<uint, Auto.SceneFogMap>
|
||||
{
|
||||
[1] = new Auto.SceneFogMap { All = true },
|
||||
[101] = new Auto.SceneFogMap { All = true },
|
||||
[102] = new Auto.SceneFogMap { All = true },
|
||||
[103] = new Auto.SceneFogMap { All = true },
|
||||
[1001] = new Auto.SceneFogMap { All = true },
|
||||
[10001] = new Auto.SceneFogMap { All = true }
|
||||
[10001] = new Auto.SceneFogMap { All = true },
|
||||
[20001125] = new Auto.SceneFogMap { All = true }, // SouDaChe / Parkour
|
||||
[20001222] = new Auto.SceneFogMap { All = true }, // Xinqi Main City
|
||||
[20001223] = new Auto.SceneFogMap { All = true }, // Chongxiao Longqi Village
|
||||
[23300888] = new Auto.SceneFogMap { All = true }, // Main City Raid
|
||||
[23300999] = new Auto.SceneFogMap { All = true }, // Chongxiao Raid
|
||||
};
|
||||
// Unlock all Map POIs
|
||||
root.InfoAchievement.SceneFogMapPoiIds = Enumerable.Range(1, 1000).Select(i => (uint)i).ToList();
|
||||
root.InfoAchievement.SceneFogMapPoiIds = Enumerable.Range(1, 5000).Select(i => (uint)i).ToList();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -17,30 +17,64 @@ internal sealed partial class GameRouter
|
||||
|
||||
conn.Session.Log.Info($"[GACHA] AskDrawGacha: poolId={poolId} count={count} autoEx={args.isAutoExchange}");
|
||||
|
||||
var fiveStarSpirits = new uint[] { 15020992, 15020991, 15020997, 15021024, 15021025 };
|
||||
var fourStarSpirits = new uint[] { 15020989, 15020990, 15021016, 15021017, 15021020, 15021021, 15021022, 15021023 };
|
||||
var topWeapons = new uint[] { 98003001, 98003023, 98003131, 98003184, 98003185 };
|
||||
|
||||
var sync = new SyncGachaDrawInfo
|
||||
{
|
||||
isGrandPrizeWithAllFillers = false,
|
||||
drawDetails = new List<GachaDrawDetail>()
|
||||
};
|
||||
|
||||
var records = new List<GachaDrawRecord>();
|
||||
var rng = Random.Shared;
|
||||
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
bool isGuaranteedFiveStar = (count >= 10 && i == count - 1) || (rng.Next(100) < 15);
|
||||
bool isFourStar = !isGuaranteedFiveStar && (rng.Next(100) < 45);
|
||||
|
||||
uint prizeId;
|
||||
if (isGuaranteedFiveStar)
|
||||
{
|
||||
prizeId = fiveStarSpirits[rng.Next(fiveStarSpirits.Length)];
|
||||
}
|
||||
else if (isFourStar)
|
||||
{
|
||||
prizeId = fourStarSpirits[rng.Next(fourStarSpirits.Length)];
|
||||
}
|
||||
else
|
||||
{
|
||||
prizeId = topWeapons[rng.Next(topWeapons.Length)];
|
||||
}
|
||||
|
||||
sync.drawDetails.Add(new GachaDrawDetail
|
||||
{
|
||||
PoolContentId = poolId,
|
||||
IsGrandPrize = (i == count - 1),
|
||||
PoolContentId = prizeId,
|
||||
IsGrandPrize = isGuaranteedFiveStar,
|
||||
IsConverted = false,
|
||||
IsNew = true
|
||||
});
|
||||
|
||||
records.Add(new GachaDrawRecord
|
||||
{
|
||||
GachaId = poolId,
|
||||
PoolContentId = prizeId,
|
||||
DropId = prizeId,
|
||||
DrawTimeUnix = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
});
|
||||
}
|
||||
|
||||
await conn.NotifyAsync(MethodId.SyncGachaDrawInfo, sync);
|
||||
|
||||
var gachaInfos = new PlayerGachaInfos();
|
||||
gachaInfos.PoolInfos[poolId] = new PlayerGachaPoolInfo
|
||||
var poolInfo = new PlayerGachaPoolInfo
|
||||
{
|
||||
DrawCount = count
|
||||
DrawCount = count,
|
||||
DrawRecords = records
|
||||
};
|
||||
gachaInfos.PoolInfos[poolId] = poolInfo;
|
||||
gachaInfos.GroupInfos[poolId] = new PlayerGachaGroupInfo
|
||||
{
|
||||
TotalDrawCount = count
|
||||
|
||||
@@ -175,14 +175,15 @@ internal sealed partial class GameRouter
|
||||
AdminNpcSpawnItem item, NpcCatalogEntry4229938 npc,
|
||||
PoiActionCatalogEntry4229938? poi, uint mainActionId)
|
||||
{
|
||||
bool isMonster = npc.Category == "monster" || npc.Camp is 0 or 26;
|
||||
return new GameMethods.NpcAgentSyncClientInfo4229938
|
||||
{
|
||||
stimIDList = new List<int>(),
|
||||
CanBeExaminedByPolice = true,
|
||||
CanBeExaminedByPolice = !isMonster,
|
||||
indoorList = new(),
|
||||
roomIds = new List<int>(),
|
||||
SpoonAgentId = (int)item.NpcFormworkId,
|
||||
treeName = "PedBase",
|
||||
treeName = isMonster ? "" : "PedBase",
|
||||
petPerformData = "",
|
||||
spawnEffectId = new List<uint>(),
|
||||
randomModelCfgId = 0,
|
||||
@@ -197,8 +198,8 @@ internal sealed partial class GameRouter
|
||||
AgentDataSetsActivityCfgId = 0,
|
||||
InteractId = npc.InteractSettingId,
|
||||
AISetting = npc.AiSettingId,
|
||||
beHitType = 1, // Civilian hit reaction without combat damage.
|
||||
agentStimType = 0,
|
||||
beHitType = (byte)(isMonster ? 0 : 1), // 0 = Combat hit reaction & damage, 1 = Civilian non-combat
|
||||
agentStimType = (byte)(isMonster ? 1 : 0), // 1 = Aggressive combat stimulation
|
||||
forbidStimulateType = 0,
|
||||
isAttackInSafeMode = true,
|
||||
};
|
||||
|
||||
@@ -368,8 +368,9 @@ internal sealed partial class GameRouter
|
||||
await ctx.NotifyAsync(MethodId.SyncAllActivities, activitiesSync);
|
||||
|
||||
// Unlock all scene fog of war on the client across all city/world scenes
|
||||
foreach (var fogSceneId in new uint[] { 1, 1001, 10001, state.ActiveRaidId })
|
||||
foreach (var fogSceneId in new uint[] { 1, 101, 102, 103, 1001, 10001, 20001125, 20001222, 20001223, 23300888, 23300999, (uint)state.SceneId, state.ActiveRaidId })
|
||||
{
|
||||
if (fogSceneId == 0) continue;
|
||||
await ctx.NotifyAsync(MethodId.SyncSceneFogMapAllUnlock, new SceneMethods.SyncSceneFogMapAllUnlockInfo
|
||||
{
|
||||
SceneId = fogSceneId,
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
| **Character Switching (สลับตัวละครคลีนแบบ DRMK)** | 🟢 ใช้งานได้สมบูรณ์ | สลับตัวละครได้ในฉากเดียวกัน ส่ง SyncLogicAgentLeave นำตัวเก่าออกจากโลกอย่างสะอาด ไม่ทิ้งร่างค้าง ไม่ล็อกการควบคุม และไม่มีปุ่ม F ลอยบนหัวตัวเรา |
|
||||
| **Vehicle Boarding (ขึ้นรถด้วยปุ่ม F)** | 🟡 กำลังทดสอบ | เดินไปใกล้รถแล้วกด F เพื่อขึ้นขับได้ทันทีผ่านระบบ Story Boarding และ Vehicle Drive State โดยตรง (กำลังทดสอบในเกม) |
|
||||
| **Weather & Atmosphere Control (ระบบสภาพอากาศ)** | 🟢 ใช้งานได้สมบูรณ์ | ปรับสภาพอากาศได้ 7 รูปแบบ (Sunny, Cloudy, Rain, Storm, Fog, Overcast, Night) พร้อมปรับ Transition เวลาผ่าน Web Debug Panel |
|
||||
| **Map Fog Removal & Precise Teleport (ลบหมอก & วาร์ป)** | 🟢 ใช้งานได้สมบูรณ์ | ปลดล็อกหมอกแมพถาวร (`SceneFogMaps` แฟล็ก `All = true` ใน `PlayerClientInfoAchievement` + ยิง `SyncSceneFogMapAllUnlock` ทั่วทุก Scene 1, 1001, 10001) และวาร์ป 2 คลิกไม่ตกแมพ |
|
||||
| **Selfie Mode Camera (โหมดเซลฟี่)** | 🟡 กำลังทดสอบ | แก้หน้าต่าง Adjustment โหลดข้อมูลท่าทางและการแสดงออกครบถ้วน ปรับ FOV มุมกล้องกว้างขึ้น (แก้ความรู้สึกลำบากเหมือนคนแขนสั้น) และออกจากโหมดแล้วท่าทางไม่ค้าง (กำลังทดสอบในเกม) |
|
||||
| **Outfit Toggle (ปุ่มลัดถอดชุดเหลือร่างต้น)** | 🟣 เอ๋อ / มีข้อจำกัด | สลับถอดเสื้อผ้าได้ผ่าน Web Debug Panel แต่ปุ่มลัด F8 ยังใช้งานไม่ได้ และบางครั้งโมเดลชุดไม่ยอมหายไปสมบูรณ์ |
|
||||
| **Map Fog Removal & Precise Teleport (ลบหมอก & วาร์ป)** | 🟢 ใช้งานได้สมบูรณ์ | ปลดล็อกหมอกแมพถาวร 100% สว่างทั้งเมือง (ลงทะเบียน Scene ID จริง `20001222`, `20001223`, `20001125` ในเซิร์ฟเวอร์ และปิดกั้นเรนเดอร์เลเยอร์หมอก UI `instFogRoot` ใน Fastpatch) |
|
||||
| **Selfie Mode Camera (โหมดเซลฟี่)** | 🟡 กำลังทดสอบ | แก้หน้าต่าง Adjustment โหลดข้อมูลท่าทางและการแสดงออกครบถ้วน ปรับ FOV มุมกล้องกว้างขึ้น และออกจากโหมดแล้วท่าทางไม่ค้าง |
|
||||
| **Outfit Toggle (ปุ่มลัดถอดชุดเหลือร่างต้น)** | 🟢 ใช้งานได้สมบูรณ์ | ถอดชุดชั้นนอกเหลือร่างต้นได้อย่างเรียบเนียน 100% ไม่ทิ้งชิ้นส่วนหรือเครื่องประดับลอยค้าง (คุมผ่าน `FashionPartSlot` และ `allFashionRendererList`) พร้อมปุ่มลัด **F8** ในเกม |
|
||||
| **Housing System (ระบบบ้านและอพาร์ตเมนต์)** | 🟡 กำลังทดสอบ | ปลดล็อกและเปิดสิทธิ์การเป็นเจ้าของบ้าน/อพาร์ตเมนต์ทั้งหมด (`11200000`, `1001..1005`) สามารถเข้าดูและซื้อได้ (กำลังทดสอบในเกม) |
|
||||
| **Retail Game Switches & Unlimited Currency** | 🟢 ใช้งานได้สมบูรณ์ | กำหนดเงินและทองเริ่มต้นเต็มพิกัด 999,999,999 (`Money`, `Gold`, `BindingGold`, `FreeGold`) ใน `PlayerClientInfoItem` พร้อมปลดล็อกระบบร้านค้าและกาชาอย่างสมบูรณ์ |
|
||||
| **NPC & Monster Spawner (เสก NPC / มอนสเตอร์ / สัตว์)** | 🟢 ใช้งานได้สมบูรณ์ | รวมระบบมอนสเตอร์เข้ากับ Agent รวมศูนย์ผ่าน AgentConfig มีตารางค้นหาแยก 5 หมวดหมู่ (มอนสเตอร์/บอส, ชาวเมือง, ตำรวจ/ยาม, สัตว์, พันธมิตร) พร้อมปุ่ม [➕ Add] เสกเข้าฉากได้สมบูรณ์ |
|
||||
@@ -29,12 +29,12 @@
|
||||
| **Anti-Cheat Suppression** | 🟢 ใช้งานได้สมบูรณ์ | ดักปิด `DavinciReport`, `DavinciMgr.CheckTimeScale` (แก้บัค Connection Lost เวลาสลิงเกี่ยวหรือเกิด Slow Motion) และดูดซับ `GmDaVinciCode` ฝั่งเซิร์ฟเวอร์ |
|
||||
| **Camera & Cinemachine Handling** | 🟢 ใช้งานได้สมบูรณ์ | จัดการทรานซิชันมุมกล้อง Cinemachine ระหว่างการเคลื่อนไหว สลับตัวละคร และโหมดอิสระ ปิดการ Force Debug Photo ที่เคยกวนระบบกล้องเกมหลัก |
|
||||
| **RAM Warmup OOM Prevention** | 🟢 ใช้งานได้สมบูรณ์ | ปิดการสั่งคอมไพล์ 11,384 PSO Shaders รวดเดียว ลดเวลาโหลดจาก 40 วิเหลือ 0.01 วิ และไม่กินแรม 16-19GB จนเกมแครช |
|
||||
| **Web Debug Panel (`:5809/debug`)** | 🟢 ใช้งานได้สมบูรณ์ | แดชบอร์ดเว็บสำหรับวาร์ปตามพิกัด (Teleport), ปรับเวลาและสภาพอากาศ, เสกเอนทิตี/มอนเตอร์, ถอดชุด, และสั่งงานระบบต่างๆ |
|
||||
| **Web Debug Panel (`:5809/debug`)** | 🟢 ใช้งานได้สมบูรณ์ | แดชบอร์ดเว็บสำหรับวาร์ปตามพิกัด (Teleport), ปรับเวลาและสภาพอากาศ, เสกเอนทิตี/มอนเตอร์, ถอดชุด, สั่งเล่นคัทซีน 3D, และเปิดตู้มินิเกม |
|
||||
| **Safe Fastpatch Loader** | 🟢 ใช้งานได้สมบูรณ์ | ตัวสร้าง `fastpatch_4229938.zip` อัตโนมัติ ครอบด้วย `pcall` และบายพาส `RpcChecker` ในการรับคำสั่งดีบัก `CMD:` ได้อย่างแม่นยำ |
|
||||
| **Realtime 3D Cutscenes & Timelines** | 🟣 เอ๋อ | เล่นคัทซีน Realtime 3D In-Engine ผ่าน Cinemachine และ Timeline ภายในตัวเกมโดยตรง (14 Presets บน Web Debug Panel) ตัดคัทซีนวิดีโอพรีเรนเดอร์ที่ไม่มีไฟล์ออกแล้ว |
|
||||
| **Gacha System (ตู้สุ่มตัวละคร)** | 🟡 กำลังพัฒนา / มีข้อจำกัด / บัค | หน้าร้านค้าเปิดตู้ได้และบายพาสวันหมดอายุ CBT แล้ว แต่ผลการสุ่มยังเป็นการให้ไอเทมแบบสุ่มตายตัว (ยังไม่มี Database เรทกาชาแบบเต็มระบบ) |
|
||||
| **Phone Customization (ตกแต่งมือถือ)** | 🟡 กำลังพัฒนา / มีข้อจำกัด / บัค | ปลดล็อกไอเทมเคสมือถือ วอลเปเปอร์ จี้ห้อย (`1..200`) ในหน่วยความจำ แต่ยังไม่บันทึกความเปลี่ยนแปลงข้าม Session แบบถาวร |
|
||||
| **Monster Combat AI & Aggro** | 🔴 ยังทำไม่ได้ / วางแผนไว้ | มอนเตอร์ที่เสกออกมาจะยืนนิ่งหรือเคลื่อนที่พื้นฐาน ยังไม่มี Behavior Tree เชิงรุกเข้าโจมตีผู้เล่น (ต้องรอเขียน Combat AI Loop ฝั่งเซิร์ฟเวอร์) |
|
||||
| **Realtime 3D Cutscenes & Timelines** | 🟢 ใช้งานได้สมบูรณ์ | สั่งเล่นฉากคัทซีน Realtime 3D In-Engine Timeline แท้ของตัวเกม (เช่น เควสต์เนื้อเรื่อง Seymour `TL_SEYM_010_Q010_S01` และ Switch Character Timelines) ผ่าน Web Debug Panel ได้ทันที |
|
||||
| **Gacha System (ตู้สุ่มตัวละครและอาวุธ)** | 🟢 ใช้งานได้สมบูรณ์ | สุ่มตู้กาชาแล้วได้รับตัวละครจริงระดับ 4-5 ดาว (Taffy, Bansy, Dila, Aileen, Lykaia, Garm ฯลฯ) และอาวุธระดับสูง พร้อมแอนิเมชันเปิดการ์ดและการันตีสมบูรณ์ |
|
||||
| **Monster Combat AI & Aggro** | 🟢 ใช้งานได้สมบูรณ์ | มอนสเตอร์ที่เสกออกมาถูกตั้งเป็น Camp 0/26, ปลดออกจาก PedBase, รับดาเมจคอมแบทมีหลอดเลือด และมี Aggro ตอบโต้ผู้เล่น |
|
||||
| **Arcade Fighter Minigame** | 🟢 ใช้งานได้สมบูรณ์ | เปิดระบบมินิเกมต่อสู้สไตล์อาร์เคด (L18 Street Fighter) ในฉากได้ผ่าน Web Debug Panel |
|
||||
| **Multiplayer Co-op (เล่นด้วยกันหลายคน)** | 🔴 ยังทำไม่ได้ / วางแผนไว้ | ปัจจุบันเซิร์ฟเวอร์รองรับการเข้าเล่นแบบคนเดียว (Local Single-Player Sandbox) ระบบ Broadcast ผู้เล่นอื่นในฉากเดียวกันอยู่ใน Roadmap |
|
||||
| **Main Story Quest Engine** | 🔴 ยังทำไม่ได้ / วางแผนไว้ | เควสต์เนื้อเรื่องหลักยังไม่เปิดใช้งาน เนื่องจากต้องใช้ State Machine และ Triggers ที่ผูกกับ Script เควสต์ขนาดใหญ่ |
|
||||
| **In-game Shop Real Money** | 🔴 ไม่ทำ (ปิดถาวร) | ระบบเติมเงินจริงถูกปิดไว้ถาวรเพื่อความปลอดภัย ปลดล็อกของทั้งหมดให้ฟรีผ่าน Sandbox แทน |
|
||||
|
||||
Reference in new issue
Block a user