fix(world): resolve traffic freeze, player vehicle kick, mob npcs, void falls, subway fast-travel, and timeline playback
This commit is contained in:
1 parent
67bdbae24b
commit
8470c3d413
9 files changed
+233
-123
No files matched your search
@@ -1229,69 +1229,47 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
private static readonly TimelineItem[] AllTimelines =
|
||||
[
|
||||
// Story & Quests
|
||||
new("TL_SEYM_010_Q010_S01", 2030u, "Seymour Story Quest: Chapter 1 S01 (黑客与发布会 - แฮกเกอร์กับงานแถลงข่าว)", "story", 3418.28f, 295.23f, 1415.67f),
|
||||
new("TL_TAF_010_Q010_S04", 0u, "Taffy Story Quest: Chapter 1 S04 (เควสต์เนื้อเรื่องทาฟี่)", "story"),
|
||||
new("TL_HEISTJ", 0u, "Bank Heist Cinematics Part J (ฉากภารกิจปล้นธนาคาร J)", "story"),
|
||||
new("TL_HEISTZ", 0u, "Bank Heist Cinematics Part Z (ฉากภารกิจปล้นธนาคาร Z)", "story"),
|
||||
new("TL_TGS_MQ_S02", 0u, "Tokyo Game Show Main Quest S02 (ฉากเปิดตัวงาน TGS)", "story"),
|
||||
new("TL_SQ_020_Q005_S02", 0u, "Side Quest Special Cinematic (เควสต์ย่อย 020 S02)", "story"),
|
||||
new("TL_Bernard", 0u, "Bernard Character Cinematic (คัทซีนเบอร์นาร์ด)", "story"),
|
||||
new("TL_Erin_mm_hunhun", 0u, "Erin vs Thugs Street Scene (เอรินปะทะกลุ่มอันธพาล)", "story"),
|
||||
new("xsb_phase3_part3", 0u, "Boss Encounter Phase 3 Part 3 (คัทซีนบอสเฟส 3)", "story"),
|
||||
new("tl_seymour01", 1288u, "Seymour Story Chapter 1 (赛默 - 计划外响应 01)", "story"),
|
||||
new("tl_seymour02", 1289u, "Seymour Story Chapter 2 (赛默 - 计划外响应 02)", "story"),
|
||||
new("tl_seymour03", 1290u, "Seymour Story Chapter 3 (赛默 - 计划外响应 03)", "story"),
|
||||
new("tl_seym_010_q010_s01", 1270u, "Seymour Story Quest S01 (面具之下,监视之中)", "story", 3418.28f, 295.23f, 1415.67f),
|
||||
new("tl_taffy01", 1353u, "Taffy Story: High-Speed Moment 01 (塔菲 - 极速时刻 01)", "story"),
|
||||
new("tl_taffy02", 1354u, "Taffy Story: High-Speed Moment 02 (塔菲 - 极速时刻 02)", "story"),
|
||||
new("tl_richiet01", 1208u, "Richie Story: Holiday Syndrome 01 (里希 - 假期综合征 01)", "story"),
|
||||
new("ananta_openning", 3u, "Ananta Main Opening Cinematic (ฉากเปิดตัว Ananta โลกใหม่)", "story"),
|
||||
new("alienfight", 2u, "Alien Combat Cinematic (ฉากต่อสู้เอเลี่ยนสุดอลังการ)", "story"),
|
||||
|
||||
// Character Entrances & Customs
|
||||
new("SwitchChar_tafei_01", 48u, "Taffy Skyscraper Apartment (ทาฟี่ โดดจากตึกอพาร์ตเมนต์)", "character", 1613.81f, 21.96f, 1565.65f),
|
||||
new("SwitchChar_dila_03", 62u, "Dila Combat Rooftop Action (ดิลา แอ็กชันคอมแบทบนดาดฟ้า)", "character", 484.28f, 15.03f, 2018.23f),
|
||||
new("SwitchChar_saimo_05", 100u, "Seymour Skyline Overview (เซย์มัวร์ ชมวิวตึกระฟ้าสไตล์คูล)", "character", 2198.68f, 92.08f, 1807.14f),
|
||||
new("SwitchChar_lixi_04", 59u, "Richie Motorcycle Battle (ริชชี่ ซิ่งมอเตอร์ไซค์สู้แก๊งสเตอร์)", "character", 1647.65f, 0f, 1578.67f),
|
||||
new("SwitchChar_nanzhujue_02", 61u, "Male MC Sports Car Arrival (พระเอก นั่งสปอร์ตคาร์มาส่ง)", "character", 3142.67f, 0f, 2522.78f),
|
||||
new("SwitchChar_nanzhujue_03", 115u, "Male MC Rolls-Royce Arrival (พระเอก ก้าวลงจาก Rolls-Royce)", "character", 2468.71f, -0.1f, 1371.11f),
|
||||
new("SwitchChar_character_2", 82u, "Taffy Park Bench Nap (ทาฟี่ นอนหลับบนม้านั่งสวนสาธารณะ)", "character", 403.61f, 0f, 2117.54f),
|
||||
new("SwitchChar_character_3", 83u, "Aileen at Maid Cafe (ไอลีน สนทนากับเมดในคาเฟ่)", "character", 2086.22f, -0.11f, 2362.25f),
|
||||
new("SwitchChar_character_4", 84u, "Bansy Street Graffiti (แบนซี พ่นสเปรย์กราฟิตี้ข้างถนน)", "character", 522.15f, 0f, 2083.76f),
|
||||
new("SwitchChar_character_6", 85u, "Dila Scrolling Phone on Bench (ดิลา นั่งเล่นโทรศัพท์มือถือ)", "character", 395.99f, 0f, 2111.99f),
|
||||
new("SwitchChar_character_7", 86u, "Enomi Drinking Milk (เอโนมิ ยืนดื่มนมกล่อง)", "character", 1606.63f, -0.1f, 1222.73f),
|
||||
new("SwitchChar_character_13", 89u, "Garm Baseball Batting (การ์ม ซ้อมหวดลูกเบสบอลสุดเท่)", "character", 2862.36f, -0.67f, 2761.5f),
|
||||
new("SwitchChar_character_14", 94u, "Richie Catching Criminal (ริชชี่ บุกจับคนร้ายคาหนังคาเขา)", "character", 3913.1f, -0.14f, 626.1f),
|
||||
new("SwitchChar_character_16", 90u, "MC Receiving Street Flyer (พระเอก รับใบปลิวจากคนแจก)", "character", 2597.6f, 0.14f, 1156.3f),
|
||||
new("SwitchChar_character_22", 113u, "Lykaia Peeling Apple in Mansion (ไลคาเอีย ปอกแอปเปิ้ลในคฤหาสน์หรู)", "character", 3204.52f, 104.46f, 959.45f),
|
||||
new("SwitchChar_character_23", 114u, "Baijing High-Speed Overtake (ไป๋จิ้ง ซิ่งรถแซงโค้งความเร็วสูง)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("SwitchChar_character_19", 302u, "Richie Official PV Cinematic (ริชชี่ ช็อตไฮไลท์จากเทรลเลอร์ PV)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("SwitchChar_character_20", 303u, "Continuous One-Shot Camera (มุมกล้อง Long-Take ช็อตเดียวจบ)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("SwitchChar_common_01", 1u, "Cherry Blossom Ave Passenger Drop (ไลคาเอีย ขับรถมาส่งที่ถนนซากุระ)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("SwitchChar_common_07", 44u, "Taffy Driving Sports Car (ทาฟี่ ขับรถซิ่งบนถนนใหญ่)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("SwitchChar_common_09", 49u, "Exiting City Taxi (เปิดประตูก้าวลงจากรถแท็กซี่)", "character", 3008.3f, 0f, 1616.5f),
|
||||
new("SwitchChar_common_27", 87u, "Street Basketball Game (ดังก์บาสเกตบอลสตรีท)", "character", 410.72f, 0f, 2108.59f),
|
||||
new("switchchar_tafei_01", 48u, "Taffy Skyscraper Apartment (ทาฟี่ โดดจากตึกอพาร์ตเมนต์)", "character", 1613.81f, 21.96f, 1565.65f),
|
||||
new("switchchar_dila_03", 62u, "Dila Combat Rooftop Action (ดิลา แอ็กชันคอมแบทบนดาดฟ้า)", "character", 484.28f, 15.03f, 2018.23f),
|
||||
new("switchchar_saimo_05", 100u, "Seymour Skyline Overview (เซย์มัวร์ ชมวิวตึกระฟ้าสไตล์คูล)", "character", 2198.68f, 92.08f, 1807.14f),
|
||||
new("switchchar_lixi_04", 59u, "Richie Motorcycle Battle (ริชชี่ ซิ่งมอเตอร์ไซค์สู้แก๊งสเตอร์)", "character", 1647.65f, 0f, 1578.67f),
|
||||
new("switchchar_nanzhujue_02", 61u, "Male MC Sports Car Arrival (พระเอก นั่งสปอร์ตคาร์มาส่ง)", "character", 3142.67f, 0f, 2522.78f),
|
||||
new("switchchar_nanzhujue_03", 115u, "Male MC Rolls-Royce Arrival (พระเอก ก้าวลงจาก Rolls-Royce)", "character", 2468.71f, -0.1f, 1371.11f),
|
||||
new("switchchar_character_2", 82u, "Taffy Park Bench Nap (ทาฟี่ นอนหลับบนม้านั่งสวนสาธารณะ)", "character", 403.61f, 0f, 2117.54f),
|
||||
new("switchchar_character_3", 83u, "Aileen at Maid Cafe (ไอลีน สนทนากับเมดในคาเฟ่)", "character", 2086.22f, -0.11f, 2362.25f),
|
||||
new("switchchar_character_4", 84u, "Bansy Street Graffiti (แบนซี พ่นสเปรย์กราฟิตี้ข้างถนน)", "character", 522.15f, 0f, 2083.76f),
|
||||
new("switchchar_character_6", 85u, "Dila Scrolling Phone on Bench (ดิลา นั่งเล่นโทรศัพท์มือถือ)", "character", 395.99f, 0f, 2111.99f),
|
||||
new("switchchar_character_7", 86u, "Enomi Drinking Milk (เอโนมิ ยืนดื่มนมกล่อง)", "character", 1606.63f, -0.1f, 1222.73f),
|
||||
new("switchchar_character_13", 89u, "Garm Baseball Batting (การ์ม ซ้อมหวดลูกเบสบอลสุดเท่)", "character", 2862.36f, -0.67f, 2761.5f),
|
||||
new("switchchar_character_22", 113u, "Lykaia Peeling Apple in Mansion (ไลคาเอีย ปอกแอปเปิ้ลในคฤหาสน์หรู)", "character", 3204.52f, 104.46f, 959.45f),
|
||||
new("switchchar_common_01", 1u, "Cherry Blossom Ave Passenger Drop (ไลคาเอีย ขับรถมาส่งที่ถนนซากุระ)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("switchchar_common_07", 44u, "Taffy Driving Sports Car (ทาฟี่ ขับรถซิ่งบนถนนใหญ่)", "character", 3008.4f, -0.1f, 1614.91f),
|
||||
new("switchchar_common_09", 49u, "Exiting City Taxi (เปิดประตูก้าวลงจากรถแท็กซี่)", "character", 3008.3f, 0f, 1616.5f),
|
||||
|
||||
// Urban Life & Interactive
|
||||
new("SwitchChar_common_04", 4u, "Exiting Mahjong Parlor (เดินออกจากร้านไพ่นกกระจอก)", "urban", 1031.7f, 0f, 1888.7f),
|
||||
new("SwitchChar_common_21", 63u, "Exiting Store & Stretching (เดินออกจากร้านพร้อมบิดขี้เกียจ)", "urban", 1030.2f, 0f, 1889.73f),
|
||||
new("SwitchChar_common_22", 96u, "Exiting Store Fist Pump (เดินออกจากร้านพร้อมชูกำปั้นมั่นใจ)", "urban", 2062.63f, 0f, 2459f),
|
||||
new("SwitchChar_common_23", 106u, "Exiting Restaurant Patting Belly (เดินออกจากร้านอาหารลูบท้องอิ่ม)", "urban", 3074.69f, 0f, 2185.89f),
|
||||
new("SwitchChar_common_24", 97u, "Walking & Hanging up Scam Call (คุยโทรศัพท์สายหลอกลวงแล้วส่ายหัวตัดสาย)", "urban", 1727f, 9.64f, 1968f),
|
||||
new("SwitchChar_common_10", 28u, "Cyberpunk Nightclub Dancing 1 (แดนซ์ในผับไซเบอร์พังก์ 1)", "urban", 2875.39f, 0.02f, 2203.92f),
|
||||
new("SwitchChar_common_11", 31u, "Wild Cyberpunk Nightclub Dancing 2 (แดนซ์ในผับไซเบอร์พังก์ 2)", "urban", 558.19f, -21.14f, 1933.61f),
|
||||
new("SwitchChar_common_14", 54u, "Petting Street Cat (นั่งยองๆ ลูบหัวแมวจรจัด)", "urban", 2832.1f, 0.05f, 1880.63f),
|
||||
new("SwitchChar_common_15", 55u, "Walking the Dog (พาสุนัขเดินเล่นรอบเมือง)", "urban", 2809.91f, 0.07f, 1940.67f),
|
||||
new("SwitchChar_common_16", 57u, "Taking Landscape Photos (ยกกล้องถ่ายรูปวิวทิวทัศน์เมือง)", "urban", 589f, 0f, 1937.8f),
|
||||
new("SwitchChar_common_17", 60u, "Street Smartphone Selfie (หยิบมือถือขึ้นมาถ่ายรูปเซลฟี่)", "urban", 706.56f, 3.41f, 2097.07f),
|
||||
new("SwitchChar_common_18", 107u, "Photo with Citizen Fan (ถ่ายรูปร่วมกับแฟนคลับชาวเมือง)", "urban", 1229.34f, 0f, 1237.58f),
|
||||
new("SwitchChar_common_26", 105u, "Polite Photo with NPC (ถ่ายรูปคู่กับ NPC อย่างสุภาพ)", "urban", 1889.75f, 0f, 2211.68f),
|
||||
new("SwitchChar_common_19", 56u, "Tossing Can in Trash Bin (โยนกระป๋องลงถังขยะลงเป๊ะ)", "urban", 1717.25f, 0.08f, 2271.25f),
|
||||
new("SwitchChar_common_25", 99u, "Tossing Can in Trash Bin Missed (โยนกระป๋องไม่ลงถังขยะ)", "urban", 1717.25f, 0.08f, 2271.25f),
|
||||
new("SwitchChar_character_1", 88u, "Buying Drink at Vending Machine (หยอดเหรียญกดตู้เครื่องดื่ม)", "urban", 3010.08f, 0f, 1656.21f),
|
||||
new("SwitchChar_common_20", 81u, "Defeat Gang & Escape (ถล่มแก๊งข้างถนนแล้วกระโดดหนี)", "urban", 3984.66f, -0.14f, 636.22f),
|
||||
new("SwitchChar_common_20a", 111u, "Defeat Gold Gang & Escape (ถล่มแก๊งทองดำแล้วสปีดหนี)", "urban", 3984.66f, -0.14f, 636.22f),
|
||||
new("SwitchChar_common_20b", 112u, "Defeat TV-Head Gang & Escape (ถล่มแก๊งหัวทีวีแล้วสเก็ตหนี)", "urban", 3984.66f, -0.14f, 636.22f),
|
||||
|
||||
// Transit & World Transitions
|
||||
new("loading_plane01", 0u, "Airport Airplane Takeoff Cinematic (คัทซีนเครื่องบินขึ้นจากสนามบิน)", "transit"),
|
||||
new("loading_plane02", 0u, "Airport Airplane Landing Cinematic (คัทซีนเครื่องบินร่อนลงจอด)", "transit"),
|
||||
new("loading_metro_end_1", 0u, "Subway Metro Train Arrival (คัทซีนรถไฟใต้ดินเทียบชานชาลา)", "transit"),
|
||||
new("Loading_bus_end", 0u, "Metropolis City Bus Arrival (คัทซีนรถเมล์เทศบาลเทียบป้าย)", "transit"),
|
||||
new("loading_elevator02", 0u, "Skyscraper Glass Elevator (คัทซีนลิฟต์แก้วตึกระฟ้าความเร็วสูง)", "transit"),
|
||||
new("loading_indoor_in01", 0u, "Cinematic Building Entry A (คัทซีนเดินเข้าอาคารแบบสมจริง A)", "transit"),
|
||||
new("loading_indoor_out01", 0u, "Cinematic Building Exit A (คัทซีนเดินออกจากอาคารสู่ถนนใหญ่ A)", "transit"),
|
||||
// Urban Life & Transitions
|
||||
new("switchchar_common_04", 4u, "Exiting Mahjong Parlor (เดินออกจากร้านไพ่นกกระจอก)", "urban", 1031.7f, 0f, 1888.7f),
|
||||
new("switchchar_common_21", 63u, "Exiting Store & Stretching (เดินออกจากร้านพร้อมบิดขี้เกียจ)", "urban", 1030.2f, 0f, 1889.73f),
|
||||
new("switchchar_common_22", 96u, "Exiting Store Fist Pump (เดินออกจากร้านพร้อมชูกำปั้นมั่นใจ)", "urban", 2062.63f, 0f, 2459f),
|
||||
new("switchchar_common_23", 106u, "Exiting Restaurant Patting Belly (เดินออกจากร้านอาหารลูบท้องอิ่ม)", "urban", 3074.69f, 0f, 2185.89f),
|
||||
new("switchchar_common_10", 28u, "Cyberpunk Nightclub Dancing 1 (แดนซ์ในผับไซเบอร์พังก์ 1)", "urban", 2875.39f, 0.02f, 2203.92f),
|
||||
new("switchchar_common_14", 54u, "Petting Street Cat (นั่งยองๆ ลูบหัวแมวจรจัด)", "urban", 2832.1f, 0.05f, 1880.63f),
|
||||
new("switchchar_common_15", 55u, "Walking the Dog (พาสุนัขเดินเล่นรอบเมือง)", "urban", 2809.91f, 0.07f, 1940.67f),
|
||||
new("switchchar_common_16", 57u, "Taking Landscape Photos (ยกกล้องถ่ายรูปวิวทิวทัศน์เมือง)", "urban", 589f, 0f, 1937.8f),
|
||||
new("switchchar_common_18", 107u, "Photo with Citizen Fan (ถ่ายรูปร่วมกับแฟนคลับชาวเมือง)", "urban", 1229.34f, 0f, 1237.58f),
|
||||
new("switchchar_common_26", 105u, "Polite Photo with NPC (ถ่ายรูปคู่กับ NPC อย่างสุภาพ)", "urban", 1889.75f, 0f, 2211.68f),
|
||||
new("switchchar_common_19", 56u, "Tossing Can in Trash Bin (โยนกระป๋องลงถังขยะลงเป๊ะ)", "urban", 1717.25f, 0.08f, 2271.25f),
|
||||
new("switchchar_character_1", 88u, "Buying Drink at Vending Machine (หยอดเหรียญกดตู้เครื่องดื่ม)", "urban", 3010.08f, 0f, 1656.21f),
|
||||
];
|
||||
|
||||
private async Task<object> PlayTimelineAsync(string json, CancellationToken token)
|
||||
@@ -1300,15 +1278,15 @@ 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?)" };
|
||||
|
||||
string timeline = "TL_SEYM_010_Q010_S01";
|
||||
uint explicitConfigId = 0;
|
||||
string timeline = "tl_seymour01";
|
||||
bool teleport = false;
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(string.IsNullOrWhiteSpace(json) ? "{}" : json);
|
||||
var root = doc.RootElement;
|
||||
if (root.TryGetProperty("name", out var pn)) timeline = pn.GetString() ?? pn.GetRawText();
|
||||
else if (root.TryGetProperty("id", out var pi)) timeline = pi.GetString() ?? pi.GetRawText();
|
||||
if (root.TryGetProperty("configId", out var pc)) explicitConfigId = pc.GetUInt32();
|
||||
if (root.TryGetProperty("teleport", out var pt)) teleport = pt.GetBoolean();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1316,27 +1294,31 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
}
|
||||
|
||||
var match = Array.Find(AllTimelines, t => t.id.Equals(timeline, StringComparison.OrdinalIgnoreCase));
|
||||
uint configId = explicitConfigId != 0 ? explicitConfigId : (match?.configId ?? 0);
|
||||
if (match is not null)
|
||||
timeline = match.id;
|
||||
|
||||
// 1. If configured in SwitchSpiritConfig, dispatch native SwitchSpiritConfigId RPC
|
||||
if (configId != 0)
|
||||
// If explicit teleport requested and coordinates exist:
|
||||
if (teleport && match is not null && (match.x != 0 || match.y != 0 || match.z != 0))
|
||||
{
|
||||
var state = GameRouter.GetStateIfExists(session);
|
||||
var pos = (match is not null && (match.x != 0 || match.y != 0 || match.z != 0))
|
||||
? new Vec3(match.x, match.y, match.z)
|
||||
: (state is not null && state.HasLastReportedPlayerTransform ? state.LastReportedPlayerPosition : new Vec3(0, 0, 0));
|
||||
|
||||
var payload = WorldCodec.SwitchSpiritConfigId(configId, pos);
|
||||
await session.NotifyAsync(MethodId.SyncSwitchSpiritConfigId, UxSerializer.Serialize(payload), token);
|
||||
session.Log.Info($"[DEBUG-API] sent native SwitchSpiritConfigId configId={configId} timeline={timeline}");
|
||||
ulong unitId = Profile.InitialUnitId;
|
||||
if (session.Items.TryGetValue(GameRouter.WorldStateKey, out var raw) && raw is WorldEntryState state)
|
||||
{
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
if (state.ActiveSpiritUnitId != 0)
|
||||
unitId = state.ActiveSpiritUnitId;
|
||||
}
|
||||
}
|
||||
await SendPlayerTeleportAsync(session, unitId, match.x, match.y, match.z, 0, "timeline-teleport", token);
|
||||
await Task.Delay(500, token);
|
||||
}
|
||||
|
||||
// 2. Dispatch Lua Fastpatch notice
|
||||
// Dispatch Lua Fastpatch notice to play timeline directly
|
||||
var cmd = $"CMD:PLAY_TIMELINE:{timeline}";
|
||||
await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token);
|
||||
|
||||
session.Log.Info($"[DEBUG-API] play realtime timeline={timeline} configId={configId}");
|
||||
return new { ok = true, timeline, configId };
|
||||
session.Log.Info($"[DEBUG-API] play timeline={timeline}");
|
||||
return new { ok = true, timeline };
|
||||
}
|
||||
|
||||
private async Task<object> ToggleClothesAsync(CancellationToken token)
|
||||
@@ -1361,6 +1343,8 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
};
|
||||
}
|
||||
|
||||
private ulong _activeInstrumentEntityId = 0;
|
||||
|
||||
private async Task<object> LaunchMinigameAsync(string json, CancellationToken token)
|
||||
{
|
||||
var session = hub.Current;
|
||||
@@ -1377,6 +1361,33 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Spawn instrument prop if musical minigame
|
||||
if (game.Equals("PIANO", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_activeInstrumentEntityId != 0)
|
||||
{
|
||||
await GameRouter.DestroyDirectAsync(session, _activeInstrumentEntityId, "instrument-replace");
|
||||
_activeInstrumentEntityId = 0;
|
||||
}
|
||||
|
||||
var (hasPos, px, py, pz, pyaw, _) = TrafficAndCrowdDriver.Instance.GetPlayerPosition(session);
|
||||
if (hasPos)
|
||||
{
|
||||
var yawRad = pyaw * MathF.PI / 180f;
|
||||
var spawnX = px + MathF.Sin(yawRad) * 1.6f;
|
||||
var spawnZ = pz + MathF.Cos(yawRad) * 1.6f;
|
||||
var facingOpposite = (pyaw + 180f) % 360f;
|
||||
|
||||
var res = await GameRouter.SpawnDirectAsync(
|
||||
session, 80000370, new Vec3(spawnX, py, spawnZ), facingOpposite, rightOffset: 0f, sourceType: 2, reason: "piano-minigame");
|
||||
if (res.Ok)
|
||||
{
|
||||
_activeInstrumentEntityId = res.EntityId;
|
||||
session.Log.Info($"[MINIGAME] spawned grand piano prop entity={_activeInstrumentEntityId} in front of player");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cmd = $"CMD:LAUNCH_MINIGAME:{game.ToUpperInvariant()}";
|
||||
await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token);
|
||||
|
||||
@@ -1390,6 +1401,13 @@ 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?)" };
|
||||
|
||||
if (_activeInstrumentEntityId != 0)
|
||||
{
|
||||
await GameRouter.DestroyDirectAsync(session, _activeInstrumentEntityId, "instrument-close");
|
||||
_activeInstrumentEntityId = 0;
|
||||
session.Log.Info("[MINIGAME] removed spawned instrument prop");
|
||||
}
|
||||
|
||||
var cmd = "CMD:CLOSE_MINIGAME";
|
||||
await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token);
|
||||
|
||||
@@ -1406,7 +1424,6 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub
|
||||
{
|
||||
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" },
|
||||
|
||||
@@ -233,30 +233,64 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-color:#ab47bc">
|
||||
<h2 style="color:#ce93d8">👗 Outfit / Appearance (สลับชุด / ซ่อนเสื้อผ้า)</h2>
|
||||
<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 class="card" style="border-color:#3b82f6">
|
||||
<h2 style="color:#60a5fa">🚇 สถานีรถไฟ & รถไฟใต้ดิน (Subway & Metro Fast Travel)</h2>
|
||||
<div style="font-weight:bold;margin-bottom:6px;color:#93c5fd">🏙️ Nova City Metro (12 สถานี):</div>
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px">
|
||||
<button onclick="warpSubway(2376.0, -1.1, 2729.6, 104.5)">Sunny Bay</button>
|
||||
<button onclick="warpSubway(898.4, -1.1, 1558.2, 51)">Freshville</button>
|
||||
<button onclick="warpSubway(1878.8, -1.1, 2250.5, 0)">Glimmergold</button>
|
||||
<button onclick="warpSubway(2748.3, -1.1, 2046.6, 0)">Memorial Park</button>
|
||||
<button onclick="warpSubway(3159.0, -1.1, 2643.0, 0)">Azure North</button>
|
||||
<button onclick="warpSubway(3661.7, -1.0, 1286.9, 0)">Future Port</button>
|
||||
<button onclick="warpSubway(2243.6, -1.1, 1616.9, 0)">Coastal Station</button>
|
||||
<button onclick="warpSubway(3941.7, -1.1, 1974.1, 0)">Vista Station</button>
|
||||
<button onclick="warpSubway(1232.9, -1.1, 2213.1, 0)">Veluxity Station</button>
|
||||
<button onclick="warpSubway(620.2, -1.0, 2072.1, 0)">Paradox Plaza</button>
|
||||
<button onclick="warpSubway(1383.1, -1.1, 1267.7, 0)">Marine MechWorks</button>
|
||||
<button onclick="warpSubway(2947.4, -1.1, 1030.3, 0)">Rixxy Wonderland</button>
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">สลับการแสดงผลเสื้อผ้าและ Dynamic Bone ของชุดชั้นนอกอย่างหมดจด โดยยังคงโมเดลบอดี้เดิมไว้ 100% ไร้ชิ้นส่วนลอยค้าง</div>
|
||||
<div style="font-weight:bold;margin-bottom:6px;color:#a78bfa">🏮 Longqi Metro (13 สถานี):</div>
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
||||
<button class="ghost" onclick="warpSubway(-1638.0, 18.0, 1540.5, 0)">Dinglan North</button>
|
||||
<button class="ghost" onclick="warpSubway(-1492.5, 17.6, 1107.9, 0)">Zhaohua Road</button>
|
||||
<button class="ghost" onclick="warpSubway(-1431.7, 18.0, 565.0, 0)">Dinglan Road</button>
|
||||
<button class="ghost" onclick="warpSubway(-1725.5, 17.5, 12.1, 0)">Lakeside Pedestrian</button>
|
||||
<button class="ghost" onclick="warpSubway(-1002.5, 17.7, 115.3, 0)">Zenith Center</button>
|
||||
<button class="ghost" onclick="warpSubway(-1788.4, 17.5, -574.6, 0)">Lakeside South</button>
|
||||
<button class="ghost" onclick="warpSubway(-844.0, 17.5, 1133.7, 0)">Great Ferry Port</button>
|
||||
<button class="ghost" onclick="warpSubway(-624.4, 17.6, 660.4, 0)">Xinfeng Road</button>
|
||||
<button class="ghost" onclick="warpSubway(61.6, 17.5, 778.3, 0)">Yaojin Road</button>
|
||||
<button class="ghost" onclick="warpSubway(375.8, 17.5, 98.4, 0)">Yaojin Pedestrian</button>
|
||||
<button class="ghost" onclick="warpSubway(-427.6, 17.5, 354.0, 0)">Mingchun North</button>
|
||||
<button class="ghost" onclick="warpSubway(-2442.7, 17.5, 1015.6, 0)">Lakefront Institute</button>
|
||||
<button class="ghost" onclick="warpSubway(-1138.2, 17.7, -602.3, 0)">Cloudstream West</button>
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">วาร์ปไปยังสถานีรถไฟใต้ดิน (Subway / Metro Stations) ทั่วทั้ง Nova City และ Longqi ทันที</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>
|
||||
<option value="tl_seymour01">Seymour Story Chapter 1 (赛默 - 计划外响应 01)</option>
|
||||
<option value="tl_seymour02">Seymour Story Chapter 2 (赛默 - 计划外响应 02)</option>
|
||||
<option value="tl_seymour03">Seymour Story Chapter 3 (赛默 - 计划外响应 03)</option>
|
||||
<option value="tl_seym_010_q010_s01">Seymour Story Quest S01 (面具之下,监视之中)</option>
|
||||
<option value="tl_taffy01">Taffy Story: High-Speed Moment 01 (塔菲 - 极速时刻 01)</option>
|
||||
<option value="tl_taffy02">Taffy Story: High-Speed Moment 02 (塔菲 - 极速时刻 02)</option>
|
||||
<option value="tl_richiet01">Richie Story: Holiday Syndrome 01 (里希 - 假期综合征 01)</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="ananta_openning">Ananta Main Opening Cinematic (ฉากเปิดตัว Ananta โลกใหม่)</option>
|
||||
<option value="alienfight">Alien Combat Cinematic (ฉากต่อสู้เอเลี่ยนสุดอลังการ)</option>
|
||||
</select>
|
||||
<button style="background:#00838f;color:white" onclick="playSelectedCutscene()">Play Cinematic / เล่นคัทซีน</button>
|
||||
</div>
|
||||
@@ -270,29 +304,28 @@
|
||||
<optgroup label="🕹️ ตู้เกมอาร์เคด (Arcade Cabinets)">
|
||||
<option value="KOF97" selected>🕹️ Arcade: The King of Fighters '97 (ตู้เกม KOF '97)</option>
|
||||
<option value="METALSLUG">🕹️ Arcade: Metal Slug (ตู้เกม Metal Slug ลุยด่าน)</option>
|
||||
<option value="FIGHTER">🥊 3D Arcade Fighter (มินิเกมต่อสู้ 3D)</option>
|
||||
</optgroup>
|
||||
<optgroup label="🎵 ดนตรีสด (Live Musical Instruments)">
|
||||
<option value="PIANO">🎹 Grand Piano (เล่นเปียโนสด)</option>
|
||||
<option value="PIANO">🎹 Grand Piano (เล่นเปียโนสด - เสกตู้เปียโนตรงหน้า)</option>
|
||||
<option value="DRUMKIT">🥁 Drumkit Set (ตีกลองชุดสด)</option>
|
||||
</optgroup>
|
||||
<optgroup label="🏆 กิจกรรมกีฬา (Sports & Leisure)">
|
||||
<option value="BOWLING">🎳 Bowling Alley (โยนโบว์ลิ่ง)</option>
|
||||
<option value="BASKETBALL">🏀 Street Basketball (ชูตบาสเกตบอล)</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<button style="background:#e65100;color:white;font-weight:600;padding:8px 16px" onclick="launchSelectedMinigame()">Launch / เปิดมินิเกม</button>
|
||||
<button style="background:#4a5568;color:#cbd5e0;padding:8px 14px" onclick="closeActiveMinigame()">Close / ปิดมินิเกม</button>
|
||||
<button style="background:#059669;color:white;font-weight:600;padding:8px 14px" onclick="teleportToBowling()">🎳 Warp to Bowling Alley (วาร์ปไปลานโบว์ลิ่ง)</button>
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:8px">
|
||||
<button style="background:#d97706;color:white;font-size:12px;padding:4px 8px" onclick="launchMinigameQuick('KOF97')">🕹️ KOF '97</button>
|
||||
<button style="background:#d97706;color:white;font-size:12px;padding:4px 8px" onclick="launchMinigameQuick('METALSLUG')">🕹️ Metal Slug</button>
|
||||
<button style="background:#2563eb;color:white;font-size:12px;padding:4px 8px" onclick="launchMinigameQuick('PIANO')">🎹 Piano</button>
|
||||
<button style="background:#2563eb;color:white;font-size:12px;padding:4px 8px" onclick="launchMinigameQuick('DRUMKIT')">🥁 Drumkit</button>
|
||||
<button style="background:#059669;color:white;font-size:12px;padding:4px 8px" onclick="launchMinigameQuick('BOWLING')">🎳 Bowling</button>
|
||||
<button style="background:#059669;color:white;font-size:12px;padding:4px 8px" onclick="teleportToBowling()">🎳 Bowling Alley</button>
|
||||
<button style="background:#059669;color:white;font-size:12px;padding:4px 8px" onclick="launchMinigameQuick('BASKETBALL')">🏀 Basketball</button>
|
||||
</div>
|
||||
<div class="muted" style="margin-top:6px">เปิดตู้เกมอาร์เคด (KOF '97, Metal Slug) หรือมินิเกมดนตรี/กีฬาในโลกได้ทันที พร้อมปุ่มควบคุมแบบเต็มหน้าจอ</div>
|
||||
<div class="muted" style="margin-top:6px">เปิดตู้เกมอาร์เคด (KOF '97, Metal Slug), มินิเกมดนตรีสด (เสกโมเดลเปียโนตรงหน้า), หรือวาร์ปไปลานโบว์ลิ่งจริงใน Nova City</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-color:#10b981">
|
||||
@@ -854,6 +887,21 @@ async function launchArcadeMinigame() {
|
||||
await launchSelectedMinigame();
|
||||
}
|
||||
|
||||
async function warpSubway(x, y, z, facing) {
|
||||
const r = await api('/api/player/teleport', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ x: parseFloat(x), y: parseFloat(y), z: parseFloat(z), facing: parseFloat(facing || 0) })
|
||||
});
|
||||
log(`🚇 Warp to Subway (${x}, ${y}, ${z}): ` + JSON.stringify(r));
|
||||
refresh();
|
||||
}
|
||||
|
||||
async function teleportToBowling() {
|
||||
await warpSubway(1090.8, 6.5, 2083.7, 0);
|
||||
log('🎳 Warped to Bowling Alley at (1090.8, 6.5, 2083.7)');
|
||||
}
|
||||
|
||||
async function setTimePreset(hour, minute) {
|
||||
const slider = document.getElementById('timeslider');
|
||||
if (slider) {
|
||||
|
||||
@@ -97,12 +97,23 @@ public sealed class UrbanCrowdEngine
|
||||
public int TargetPedestrianDensity { get; set; } = 15;
|
||||
public bool PopulateShops { get; set; } = true;
|
||||
|
||||
// Verified civilian NPCs from Camp 2 with models
|
||||
// Verified authentic civilian, shopkeeper, barista, and bar patron NPCs
|
||||
private static readonly uint[] CitizenFormworkPool =
|
||||
[
|
||||
40120883, 40120884, 40120885, 40120886, 40120887,
|
||||
40120888, 40120889, 40120890, 40120891, 40120892,
|
||||
40120893, 40120894, 40120895, 40120896, 40120897
|
||||
40130101, // Emily Sato - Lively female shopkeeper
|
||||
40130102, // Sato Saori - Gentle female shopkeeper
|
||||
40130117, // Emma - Store greeter
|
||||
40130118, // Owen King - Store manager
|
||||
40130121, // Hunter Davis - Store clerk
|
||||
40130122, // Linda Wilson - Professional female clerk
|
||||
40130128, // Eiko Anderson - Boutique clerk
|
||||
40131542, // Yang Zhiyuan - Cafe Barista
|
||||
40131591, // Cheng Mosheng - Kiosk clerk
|
||||
40130852, // Mizuki Mei - Bar & Nightclub patron
|
||||
40650080, // Mizuki Mei - Bar patron
|
||||
40651232, // Eugene Price - Cafe patron/customer
|
||||
40969501, // Daphne Shelby - Fashion blogger pedestrian
|
||||
40968790, // Kaneko Mei - Pedestrian
|
||||
];
|
||||
|
||||
// Common idle / chatter / phone POI action IDs
|
||||
@@ -234,6 +245,10 @@ public sealed class UrbanCrowdEngine
|
||||
var sy = shop.Pos[1];
|
||||
var sz = shop.Pos[2];
|
||||
|
||||
// Ground elevation clamping: align indoor floor Y with player elevation to prevent falling into void
|
||||
if (MathF.Abs(sy - py) > 2.5f)
|
||||
sy = py;
|
||||
|
||||
var dx = sx - px;
|
||||
var dz = sz - pz;
|
||||
var distSq = dx * dx + dz * dz;
|
||||
@@ -313,12 +328,14 @@ public sealed class UrbanCrowdEngine
|
||||
if (candidateWaypoints.Count > 0)
|
||||
{
|
||||
var wp = candidateWaypoints[_rng.Next(candidateWaypoints.Count)];
|
||||
// Ground elevation clamping: ensure sidewalk pedestrian Y is on mesh floor
|
||||
var spawnY = MathF.Abs(wp.Y - py) > 2.5f ? py : wp.Y;
|
||||
var formworkId = CitizenFormworkPool[_rng.Next(CitizenFormworkPool.Length)];
|
||||
var poiAction = AmbientPoiActions[_rng.Next(AmbientPoiActions.Length)];
|
||||
var facing = _rng.Next(0, 360);
|
||||
|
||||
var (ok, ids) = await _driver.SpawnCrowdPedestriansAsync(
|
||||
session, formworkId, poiAction, wp.X, wp.Y, wp.Z, facing);
|
||||
session, formworkId, poiAction, wp.X, spawnY, wp.Z, facing);
|
||||
|
||||
if (ok && ids.Length > 0)
|
||||
{
|
||||
@@ -327,7 +344,7 @@ public sealed class UrbanCrowdEngine
|
||||
EntityId = ids[0],
|
||||
FormworkId = formworkId,
|
||||
X = wp.X,
|
||||
Y = wp.Y,
|
||||
Y = spawnY,
|
||||
Z = wp.Z,
|
||||
SpawnedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
@@ -26,19 +26,28 @@ public sealed class CityTrafficEngine
|
||||
public float MaxSpawnDistance { get; set; } = 160f;
|
||||
public float DespawnDistance { get; set; } = 220f;
|
||||
|
||||
// Pool of verified vehicle configs with full models and multi-seat support
|
||||
// Pool of verified vehicle configs with full models and multi-seat support (no damaged/wrecked models)
|
||||
private static readonly uint[] TrafficVehiclePool =
|
||||
[
|
||||
81001001, // Sunset Skywing (Sedan)
|
||||
81001002, // Korou RV6 (SUV)
|
||||
81001003, // Kazama Voyage (Sedan)
|
||||
81000007, // Sunset GT-X Specter (Sports Car)
|
||||
81000002, // Korou Transporter M (Truck)
|
||||
81000005, // Erebos Black Box (Van)
|
||||
81004001, // Kazama CRN6 (Taxi)
|
||||
81004027, // Kazama Sandstorm (Police Cruiser)
|
||||
];
|
||||
|
||||
private readonly Random _rng = new();
|
||||
|
||||
public void NotifyVehicleBoarding(ulong entityId)
|
||||
{
|
||||
if (_simulatedVehicles.TryRemove(entityId, out var v))
|
||||
{
|
||||
v.HijackedByPlayer = true;
|
||||
Console.WriteLine($"[TRAFFIC] Vehicle entity={entityId} boarded by player. Detached from traffic loop.");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SimulatedVehicle
|
||||
{
|
||||
public ulong EntityId { get; set; }
|
||||
|
||||
@@ -487,7 +487,6 @@ internal sealed partial class GameRouter
|
||||
new SceneMethods.SyncChangeVehicleController { VehicleInstanceId = vehicleId, ControllerPid = pid }), ct);
|
||||
await session.NotifyAsync(MethodId.SyncPlayerFinishEnterOrExitVehic, UxSerializer.Serialize(drive), ct);
|
||||
await SendStoryAsync(session, StoryS011Codec4229938.BuildEnterChildBootstrap(VehicleStoryEnterNid, vehicleId, seat));
|
||||
await SendStoryAsync(session, StoryS011Codec4229938.BuildDeleteNode(VehicleStoryEnterNid));
|
||||
session.Log.Info($"[VEHICLE-STORY] force enter vehicle={vehicleId} seat={seat} unit={unitId} status=4");
|
||||
return (true, $"seated in vehicle {vehicleId} (seat 0) — drive with normal keys, exit with F or the Exit button");
|
||||
}
|
||||
|
||||
@@ -379,6 +379,7 @@ internal sealed partial class GameRouter
|
||||
conn.Log.Info($"[VEHICLE] AskPlayerStartEnterOrExitVehicle vehicle={drive.VehicleEntityId} enter={drive.EnterOrLeave} seat={drive.SeatIndex}");
|
||||
if (drive.EnterOrLeave)
|
||||
{
|
||||
Ananta.Server.Gameplay.Traffic.CityTrafficEngine.Instance.NotifyVehicleBoarding(drive.VehicleEntityId);
|
||||
await ForceEnterVehicleAsync(conn.Session, drive.VehicleEntityId);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -25,9 +25,12 @@ internal sealed class TrafficAndCrowdDriver : ITrafficVehicleDriver, IUrbanCrowd
|
||||
await GameRouter.DestroyDirectAsync(session, entityId, "traffic-despawn");
|
||||
}
|
||||
|
||||
private readonly System.Collections.Concurrent.ConcurrentDictionary<ulong, int> _moveTokens = new();
|
||||
|
||||
public async Task SendVehicleMoveAsync(
|
||||
TcpSession session, ulong entityId, float x, float y, float z, float yaw, float vx, float vz)
|
||||
{
|
||||
var token = _moveTokens.AddOrUpdate(entityId, 1, (_, cur) => unchecked(cur + 1));
|
||||
var moveData = new SceneMethods.RaidVehicleSyncData
|
||||
{
|
||||
Id = entityId,
|
||||
@@ -35,8 +38,8 @@ internal sealed class TrafficAndCrowdDriver : ITrafficVehicleDriver, IUrbanCrowd
|
||||
FacingDirection = yaw,
|
||||
EulerAngles = new SceneMethods.UxVector3(0f, yaw, 0f),
|
||||
Velocity = new SceneMethods.UxVector3(vx, 0f, vz),
|
||||
Bits = [],
|
||||
MoveToken = 0,
|
||||
Bits = [1, 0, 0, 0],
|
||||
MoveToken = token,
|
||||
};
|
||||
await session.NotifyAsync(MethodId.SyncVehicleMove, UxSerializer.Serialize(moveData), CancellationToken.None);
|
||||
GameRouter.TrackVehicleMove(entityId, x, y, z, yaw);
|
||||
|
||||
Reference in new issue
Block a user