From 19cd76c50229d16a67c92e1c034466c7085a8af1 Mon Sep 17 00:00:00 2001 From: Somebody Date: Thu, 31 Jul 2025 17:46:54 +0800 Subject: [PATCH] style: move scene actions in `PlayerInstance` to another file --- .../Player/PlayerInstance.SceneActions.cs | 505 ++++++++++++++++++ GameServer/Game/Player/PlayerInstance.cs | 495 +---------------- 2 files changed, 506 insertions(+), 494 deletions(-) create mode 100644 GameServer/Game/Player/PlayerInstance.SceneActions.cs diff --git a/GameServer/Game/Player/PlayerInstance.SceneActions.cs b/GameServer/Game/Player/PlayerInstance.SceneActions.cs new file mode 100644 index 00000000..33c40f61 --- /dev/null +++ b/GameServer/Game/Player/PlayerInstance.SceneActions.cs @@ -0,0 +1,505 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Database.Scene; +using EggLink.DanhengServer.Enums.Mission; +using EggLink.DanhengServer.Enums.Scene; +using EggLink.DanhengServer.GameServer.Game.Drop; +using EggLink.DanhengServer.GameServer.Game.Scene; +using EggLink.DanhengServer.GameServer.Game.Scene.Entity; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.MarkChest; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using static EggLink.DanhengServer.GameServer.Plugin.Event.PluginEvent; + +namespace EggLink.DanhengServer.GameServer.Game.Player; + +public partial class PlayerInstance +{ + #region Scene Actions + + public async ValueTask OnMove() + { + if (SceneInstance != null) + { + var prop = SceneInstance.GetNearestSpring(25_000_000); + + var isInRange = prop != null; + + if (isInRange) + if (LineupManager?.GetCurLineup()?.Heal(10000, true) == true) + await SendPacket(new PacketSyncLineupNotify(LineupManager.GetCurLineup()!)); + } + } + + public async ValueTask InteractProp(int propEntityId, int interactId) + { + if (SceneInstance == null) return null; + SceneInstance.Entities.TryGetValue(propEntityId, out var entity); + if (entity is not EntityProp prop) return null; + GameData.InteractConfigData.TryGetValue(interactId, out var config); + if (config == null || config.SrcState != prop.State) return prop; + var oldState = prop.State; + await prop.SetState(config.TargetState); + var newState = prop.State; + await SendPacket(new PacketGroupStateChangeScNotify(Data.EntryId, prop.GroupId, prop.State)); + + switch (prop.Excel.PropType) + { + case PropTypeEnum.PROP_TREASURE_CHEST: + if (oldState == PropStateEnum.ChestClosed && newState == PropStateEnum.ChestUsed) + { + // TODO: Filter treasure chest + var items = DropService.CalculateDropsFromProp(prop.PropInfo.ChestID); + await InventoryManager!.AddItems(items); + await SendPacket(new PacketOpenChestScNotify(prop.PropInfo.ChestID)); + + var notifyMark = false; + foreach (var markedChest in SceneData!.MarkedChestData.Values) + { + var chest = markedChest.Find(x => + x.FloorId == SceneInstance.FloorId && x.GroupId == prop.GroupId && + x.ConfigId == prop.PropInfo.ID); + + if (chest == null) continue; + markedChest.Remove(chest); + notifyMark = true; + } + + if (notifyMark) + await SendPacket(new PacketMarkChestChangedScNotify(this)); + } + + break; + case PropTypeEnum.PROP_DESTRUCT: + if (newState == PropStateEnum.Closed) await prop.SetState(PropStateEnum.Open); + break; + case PropTypeEnum.PROP_MAZE_JIGSAW: + switch (newState) + { + case PropStateEnum.Closed: + { + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) + if (p.Excel.PropType == PropTypeEnum.PROP_TREASURE_CHEST) + { + await p.SetState(PropStateEnum.ChestClosed); + } + else if (p.Excel.PropType == prop.Excel.PropType) + { + // Skip + } + else + { + await p.SetState(PropStateEnum.Open); + } + + break; + } + case PropStateEnum.Open: + { + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId).Where(p => + p.Excel.PropType is not PropTypeEnum.PROP_TREASURE_CHEST && + p.Excel.PropType != prop.Excel.PropType)) + await p.SetState(PropStateEnum.Open); + + break; + } + } + + break; + case PropTypeEnum.PROP_MAZE_PUZZLE: + if (newState is PropStateEnum.Closed or PropStateEnum.Open) + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) + { + if (p.Excel.PropType == PropTypeEnum.PROP_TREASURE_CHEST) + { + await p.SetState(PropStateEnum.ChestClosed); + } + else if (p.Excel.PropType == prop.Excel.PropType) + { + // Skip + } + else + { + await p.SetState(PropStateEnum.Open); + } + + await MissionManager!.OnPlayerInteractWithProp(); + } + + break; + case PropTypeEnum.PROP_ORDINARY: + if (prop.PropInfo.CommonConsole) + // set group + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) + { + await p.SetState(newState); + + await MissionManager!.OnPlayerInteractWithProp(); + } + + if (prop.Excel.ID == 104039) + { + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) + { + await p.SetState(newState); + } + + await MissionManager!.OnPlayerInteractWithProp(); + } + + if (prop.PropInfo.Name.Contains("Piece")) + { + var pieceDone = SceneInstance.GetEntitiesInGroup(prop.GroupId) + .Where(p => p.PropInfo.Name.Contains("Piece")).All(p => p.State == PropStateEnum.Closed); + + if (pieceDone) + // set JigsawSir to open + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId) + .Where(p => p.PropInfo.Name.Contains("JigsawSir") && + p.State != PropStateEnum.Closed)) + await p.SetState(PropStateEnum.TriggerEnable); + } + + break; + } + + // for door unlock + if (prop.PropInfo.UnlockDoorID.Count > 0) + foreach (var p in prop.PropInfo.UnlockDoorID.SelectMany(id => + SceneInstance.GetEntitiesInGroup(id.Key) + .Where(p => id.Value.Contains(p.PropInfo.ID)))) + { + await p.SetState(PropStateEnum.Open); + await MissionManager!.OnPlayerInteractWithProp(); + } + + // for mission + await MissionManager!.OnPlayerInteractWithProp(); + + // plane event + InventoryManager!.HandlePlaneEvent(prop.PropInfo.EventID); + + // handle plugin event + InvokeOnPlayerInteract(this, prop); + + var floorSavedKey = prop.PropInfo.Name.Replace("Controller_", ""); + var key = $"FSV_ML{floorSavedKey}{(config.TargetState == PropStateEnum.Open ? "Started" : "Complete")}"; + + if (prop.Group.GroupName.Contains("JigsawPuzzle") && prop.Group.GroupName.Contains("MainLine")) + { + var splits = prop.Group.GroupName.Split('_'); + key = + $"JG_ML_{splits[3]}_Puzzle{(config.TargetState == PropStateEnum.Open ? "Started" : "Complete")}"; + } + + if (SceneInstance?.FloorInfo?.FloorSavedValue.Find(x => x.Name == key) != null) + { + // should save + var plane = SceneInstance.PlaneId; + var floor = SceneInstance.FloorId; + SceneData!.FloorSavedData.TryGetValue(floor, out var value); + if (value == null) + { + value = []; + SceneData.FloorSavedData[floor] = value; + } + + value[key] = 1; // ParamString[2] is the key + await SendPacket(new PacketUpdateFloorSavedValueNotify(key, 1, this)); + + TaskManager?.SceneTaskTrigger.TriggerFloor(plane, floor); + MissionManager?.HandleFinishType(MissionFinishTypeEnum.FloorSavedValue); + } + + if (prop.PropInfo.IsLevelBtn) await prop.SetState(PropStateEnum.Closed); + + return prop; + } + + public async ValueTask SetPropTimeline(int propEntityId, PropTimelineInfo info) + { + if (SceneInstance == null) return; + SceneInstance.Entities.TryGetValue(propEntityId, out var entity); + if (entity is not EntityProp prop) return; + + var data = new ScenePropTimelineData + { + BoolValue = info.TimelineBoolValue, + ByteValue = info.TimelineByteValue.ToBase64() + }; + + // save to db + SceneData!.PropTimelineData.TryGetValue(Data.FloorId, out var floorData); + if (floorData == null) + { + floorData = new Dictionary>(); + SceneData.PropTimelineData[Data.FloorId] = floorData; + } + + if (!floorData.ContainsKey(prop.GroupId)) + floorData[prop.GroupId] = new Dictionary(); + + floorData[prop.GroupId][prop.PropInfo.ID] = data; + + prop.PropTimelineData = data; + + // handle mission / quest + await MissionManager!.HandleFinishType(MissionFinishTypeEnum.TimeLineSetState); + await MissionManager!.HandleFinishType(MissionFinishTypeEnum.TimeLineSetStateCnt); + } + + public ScenePropTimelineData? GetScenePropTimelineData(int floorId, int groupId, int propId) + { + SceneData!.PropTimelineData.TryGetValue(floorId, out var floorData); + if (floorData == null) return null; + floorData.TryGetValue(groupId, out var groupData); + if (groupData == null) return null; + groupData.TryGetValue(propId, out var data); + return data; + } + + public async ValueTask EnterScene(int entryId, int teleportId, bool sendPacket, int storyLineId = 0, + bool mapTp = false) + { + var beforeStoryLineId = StoryLineManager?.StoryLineData.CurStoryLineId; + if (storyLineId != StoryLineManager?.StoryLineData.CurStoryLineId) + { + if (StoryLineManager != null) + await StoryLineManager.EnterStoryLine(storyLineId, entryId == 0); // entryId == 0 -> teleport + mapTp = false; // do not use mapTp when enter story line + } + + GameData.MapEntranceData.TryGetValue(entryId, out var entrance); + if (entrance == null) return false; + + GameData.GetFloorInfo(entrance.PlaneID, entrance.FloorID, out var floorInfo); + + var startGroup = entrance.StartGroupID; + var startAnchor = entrance.StartAnchorID; + + if (teleportId != 0) + { + floorInfo.CachedTeleports.TryGetValue(teleportId, out var teleport); + if (teleport != null) + { + startGroup = teleport.AnchorGroupID; + startAnchor = teleport.AnchorID; + } + } + else if (startAnchor == 0) + { + startGroup = floorInfo.StartGroupID; + startAnchor = floorInfo.StartAnchorID; + } + + var anchor = floorInfo.GetAnchorInfo(startGroup, startAnchor); + + await MissionManager!.HandleFinishType(MissionFinishTypeEnum.EnterMapByEntrance, entrance); + + var beforeEntryId = Data.EntryId; + + await LoadScene(entrance.PlaneID, entrance.FloorID, entryId, anchor!.ToPositionProto(), + anchor.ToRotationProto(), sendPacket, mapTp); + + var afterEntryId = Data.EntryId; + + return beforeEntryId != afterEntryId || + beforeStoryLineId != storyLineId; // return true if entryId changed or story line changed + } + + public async ValueTask EnterSceneByEntranceId(int entranceId, int anchorGroupId, int anchorId, bool sendPacket) + { + GameData.MapEntranceData.TryGetValue(entranceId, out var entrance); + if (entrance == null) return; + + GameData.GetFloorInfo(entrance.PlaneID, entrance.FloorID, out var floorInfo); + + var startGroup = anchorGroupId == 0 ? entrance.StartGroupID : anchorGroupId; + var startAnchor = anchorId == 0 ? entrance.StartAnchorID : anchorId; + + if (startAnchor == 0) + { + startGroup = floorInfo.StartGroupID; + startAnchor = floorInfo.StartAnchorID; + } + + var anchor = floorInfo.GetAnchorInfo(startGroup, startAnchor); + + await LoadScene(entrance.PlaneID, entrance.FloorID, entranceId, anchor!.ToPositionProto(), + anchor.ToRotationProto(), sendPacket); + } + + public async ValueTask MoveTo(Position position) + { + Data.Pos = position; + await SendPacket(new PacketSceneEntityMoveScNotify(this)); + } + + public void MoveTo(EntityMotion motion) + { + Data.Pos = motion.Motion.Pos.ToPosition(); + Data.Rot = motion.Motion.Rot.ToPosition(); + } + + public async ValueTask MoveTo(Position pos, Position rot) + { + Data.Pos = pos; + Data.Rot = rot; + await SendPacket(new PacketSceneEntityMoveScNotify(this)); + } + + public async ValueTask LoadScene(int planeId, int floorId, int entryId, Position pos, Position rot, bool sendPacket, + bool mapTp = false) + { + GameData.MazePlaneData.TryGetValue(planeId, out var plane); + if (plane == null) return; + + if (plane.PlaneType == PlaneTypeEnum.Rogue && RogueManager!.GetRogueInstance() == null) + { + await EnterScene(801120102, 0, sendPacket); + return; + } + + if (plane.PlaneType == PlaneTypeEnum.Raid && RaidManager!.RaidData.CurRaidId == 0) + { + await EnterScene(2000101, 0, sendPacket); + return; + } + + if (plane.PlaneType == PlaneTypeEnum.Challenge && ChallengeManager!.ChallengeInstance == null) + { + await EnterScene(100000103, 0, sendPacket); + return; + } + + // TODO: Sanify check + Data.Pos = pos; + Data.Rot = rot; + var notSendMove = true; + if (planeId != Data.PlaneId || floorId != Data.FloorId || entryId != Data.EntryId || SceneInstance == null || !mapTp) + { + if (SceneInstance != null) + await SceneInstance.OnDestroy(); + SceneInstance instance = new(this, plane, floorId, entryId); + InvokeOnPlayerLoadScene(this, instance); + SceneInstance = instance; + + await instance.SyncLineup(true); + Data.PlaneId = planeId; + Data.FloorId = floorId; + Data.EntryId = entryId; + } + else if (StoryLineManager?.StoryLineData.CurStoryLineId == 0 && + mapTp) // only send move packet when not in story line and mapTp + { + notSendMove = false; + } + + if (MissionManager != null) + await MissionManager.OnPlayerChangeScene(); + + Connection?.SendPacket(CmdIds.SyncServerSceneChangeNotify); + if (sendPacket && notSendMove) + await SendPacket(new PacketEnterSceneByServerScNotify(SceneInstance!)); + else if (sendPacket && !notSendMove) // send move packet + await SendPacket(new PacketSceneEntityMoveScNotify(this)); + + if (MissionManager != null) + { + await MissionManager.HandleFinishType(MissionFinishTypeEnum.EnterFloor); + await MissionManager.HandleFinishType(MissionFinishTypeEnum.EnterPlane); + await MissionManager.HandleFinishType(MissionFinishTypeEnum.NotInFloor); + await MissionManager.HandleFinishType(MissionFinishTypeEnum.NotInPlane); + } + } + + public ScenePropData? GetScenePropData(int floorId, int groupId, int propId) + { + if (SceneData != null) + if (SceneData.ScenePropData.TryGetValue(floorId, out var floorData)) + if (floorData.TryGetValue(groupId, out var groupData)) + { + var propData = groupData.Find(x => x.PropId == propId); + return propData; + } + + return null; + } + + public void SetScenePropData(int floorId, int groupId, int propId, PropStateEnum state) + { + if (SceneData != null) + { + if (!SceneData.ScenePropData.TryGetValue(floorId, out var floorData)) + { + floorData = []; + SceneData.ScenePropData.Add(floorId, floorData); + } + + if (!floorData.TryGetValue(groupId, out var groupData)) + { + groupData = []; + floorData.Add(groupId, groupData); + } + + var propData = groupData.Find(x => x.PropId == propId); // find prop data + if (propData == null) + { + propData = new ScenePropData + { + PropId = propId, + State = state + }; + groupData.Add(propData); + } + else + { + propData.State = state; + } + } + } + + public void EnterSection(int sectionId) + { + if (SceneInstance != null) + { + SceneData!.UnlockSectionIdList.TryGetValue(SceneInstance.FloorId, out var unlockList); + if (unlockList == null) + { + unlockList = [sectionId]; + SceneData.UnlockSectionIdList.Add(SceneInstance.FloorId, unlockList); + } + else + { + SceneData.UnlockSectionIdList[SceneInstance.FloorId].Add(sectionId); + } + } + } + + public void SetCustomSaveData(int entryId, int groupId, string data) + { + if (SceneData != null) + { + if (!SceneData.CustomSaveData.TryGetValue(entryId, out var entryData)) + { + entryData = []; + SceneData.CustomSaveData.Add(entryId, entryData); + } + + entryData[groupId] = data; + } + } + + public async ValueTask ForceQuitBattle() + { + if (BattleInstance != null) + { + BattleInstance = null; + await Connection!.SendPacket(CmdIds.QuitBattleScNotify); + } + } + + #endregion +} \ No newline at end of file diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs index 4e78ff34..4b7420b7 100644 --- a/GameServer/Game/Player/PlayerInstance.cs +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -5,14 +5,11 @@ using EggLink.DanhengServer.Database.Player; using EggLink.DanhengServer.Database.Scene; using EggLink.DanhengServer.Database.Tutorial; using EggLink.DanhengServer.Enums.Avatar; -using EggLink.DanhengServer.Enums.Mission; -using EggLink.DanhengServer.Enums.Scene; using EggLink.DanhengServer.GameServer.Game.Activity; using EggLink.DanhengServer.GameServer.Game.Avatar; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.Challenge; using EggLink.DanhengServer.GameServer.Game.ChessRogue; -using EggLink.DanhengServer.GameServer.Game.Drop; using EggLink.DanhengServer.GameServer.Game.Friend; using EggLink.DanhengServer.GameServer.Game.Gacha; using EggLink.DanhengServer.GameServer.Game.Inventory; @@ -27,16 +24,12 @@ using EggLink.DanhengServer.GameServer.Game.Rogue; using EggLink.DanhengServer.GameServer.Game.RogueMagic; using EggLink.DanhengServer.GameServer.Game.RogueTourn; using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; using EggLink.DanhengServer.GameServer.Game.Shop; using EggLink.DanhengServer.GameServer.Game.Task; using EggLink.DanhengServer.GameServer.Game.TrainParty; using EggLink.DanhengServer.GameServer.Server; -using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup; -using EggLink.DanhengServer.GameServer.Server.Packet.Send.MarkChest; using EggLink.DanhengServer.GameServer.Server.Packet.Send.Player; using EggLink.DanhengServer.GameServer.Server.Packet.Send.PlayerSync; -using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene; using EggLink.DanhengServer.Kcp; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Util; @@ -45,7 +38,7 @@ using OfferingManager = EggLink.DanhengServer.GameServer.Game.Inventory.Offering namespace EggLink.DanhengServer.GameServer.Game.Player; -public class PlayerInstance(PlayerData data) +public partial class PlayerInstance(PlayerData data) { #region Managers @@ -482,492 +475,6 @@ public class PlayerInstance(PlayerData data) #endregion - #region Scene Actions - - public async ValueTask OnMove() - { - if (SceneInstance != null) - { - var prop = SceneInstance.GetNearestSpring(25_000_000); - - var isInRange = prop != null; - - if (isInRange) - if (LineupManager?.GetCurLineup()?.Heal(10000, true) == true) - await SendPacket(new PacketSyncLineupNotify(LineupManager.GetCurLineup()!)); - } - } - - public async ValueTask InteractProp(int propEntityId, int interactId) - { - if (SceneInstance == null) return null; - SceneInstance.Entities.TryGetValue(propEntityId, out var entity); - if (entity is not EntityProp prop) return null; - GameData.InteractConfigData.TryGetValue(interactId, out var config); - if (config == null || config.SrcState != prop.State) return prop; - var oldState = prop.State; - await prop.SetState(config.TargetState); - var newState = prop.State; - await SendPacket(new PacketGroupStateChangeScNotify(Data.EntryId, prop.GroupId, prop.State)); - - switch (prop.Excel.PropType) - { - case PropTypeEnum.PROP_TREASURE_CHEST: - if (oldState == PropStateEnum.ChestClosed && newState == PropStateEnum.ChestUsed) - { - // TODO: Filter treasure chest - var items = DropService.CalculateDropsFromProp(prop.PropInfo.ChestID); - await InventoryManager!.AddItems(items); - await SendPacket(new PacketOpenChestScNotify(prop.PropInfo.ChestID)); - - var notifyMark = false; - foreach (var markedChest in SceneData!.MarkedChestData.Values) - { - var chest = markedChest.Find(x => - x.FloorId == SceneInstance.FloorId && x.GroupId == prop.GroupId && - x.ConfigId == prop.PropInfo.ID); - - if (chest == null) continue; - markedChest.Remove(chest); - notifyMark = true; - } - - if (notifyMark) - await SendPacket(new PacketMarkChestChangedScNotify(this)); - } - - break; - case PropTypeEnum.PROP_DESTRUCT: - if (newState == PropStateEnum.Closed) await prop.SetState(PropStateEnum.Open); - break; - case PropTypeEnum.PROP_MAZE_JIGSAW: - switch (newState) - { - case PropStateEnum.Closed: - { - foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) - if (p.Excel.PropType == PropTypeEnum.PROP_TREASURE_CHEST) - { - await p.SetState(PropStateEnum.ChestClosed); - } - else if (p.Excel.PropType == prop.Excel.PropType) - { - // Skip - } - else - { - await p.SetState(PropStateEnum.Open); - } - - break; - } - case PropStateEnum.Open: - { - foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId).Where(p => - p.Excel.PropType is not PropTypeEnum.PROP_TREASURE_CHEST && - p.Excel.PropType != prop.Excel.PropType)) - await p.SetState(PropStateEnum.Open); - - break; - } - } - - break; - case PropTypeEnum.PROP_MAZE_PUZZLE: - if (newState is PropStateEnum.Closed or PropStateEnum.Open) - foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) - { - if (p.Excel.PropType == PropTypeEnum.PROP_TREASURE_CHEST) - { - await p.SetState(PropStateEnum.ChestClosed); - } - else if (p.Excel.PropType == prop.Excel.PropType) - { - // Skip - } - else - { - await p.SetState(PropStateEnum.Open); - } - - await MissionManager!.OnPlayerInteractWithProp(); - } - - break; - case PropTypeEnum.PROP_ORDINARY: - if (prop.PropInfo.CommonConsole) - // set group - foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) - { - await p.SetState(newState); - - await MissionManager!.OnPlayerInteractWithProp(); - } - - if (prop.Excel.ID == 104039) - { - foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId)) - { - await p.SetState(newState); - } - - await MissionManager!.OnPlayerInteractWithProp(); - } - - if (prop.PropInfo.Name.Contains("Piece")) - { - var pieceDone = SceneInstance.GetEntitiesInGroup(prop.GroupId) - .Where(p => p.PropInfo.Name.Contains("Piece")).All(p => p.State == PropStateEnum.Closed); - - if (pieceDone) - // set JigsawSir to open - foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupId) - .Where(p => p.PropInfo.Name.Contains("JigsawSir") && - p.State != PropStateEnum.Closed)) - await p.SetState(PropStateEnum.TriggerEnable); - } - - break; - } - - // for door unlock - if (prop.PropInfo.UnlockDoorID.Count > 0) - foreach (var p in prop.PropInfo.UnlockDoorID.SelectMany(id => - SceneInstance.GetEntitiesInGroup(id.Key) - .Where(p => id.Value.Contains(p.PropInfo.ID)))) - { - await p.SetState(PropStateEnum.Open); - await MissionManager!.OnPlayerInteractWithProp(); - } - - // for mission - await MissionManager!.OnPlayerInteractWithProp(); - - // plane event - InventoryManager!.HandlePlaneEvent(prop.PropInfo.EventID); - - // handle plugin event - InvokeOnPlayerInteract(this, prop); - - var floorSavedKey = prop.PropInfo.Name.Replace("Controller_", ""); - var key = $"FSV_ML{floorSavedKey}{(config.TargetState == PropStateEnum.Open ? "Started" : "Complete")}"; - - if (prop.Group.GroupName.Contains("JigsawPuzzle") && prop.Group.GroupName.Contains("MainLine")) - { - var splits = prop.Group.GroupName.Split('_'); - key = - $"JG_ML_{splits[3]}_Puzzle{(config.TargetState == PropStateEnum.Open ? "Started" : "Complete")}"; - } - - if (SceneInstance?.FloorInfo?.FloorSavedValue.Find(x => x.Name == key) != null) - { - // should save - var plane = SceneInstance.PlaneId; - var floor = SceneInstance.FloorId; - SceneData!.FloorSavedData.TryGetValue(floor, out var value); - if (value == null) - { - value = []; - SceneData.FloorSavedData[floor] = value; - } - - value[key] = 1; // ParamString[2] is the key - await SendPacket(new PacketUpdateFloorSavedValueNotify(key, 1, this)); - - TaskManager?.SceneTaskTrigger.TriggerFloor(plane, floor); - MissionManager?.HandleFinishType(MissionFinishTypeEnum.FloorSavedValue); - } - - if (prop.PropInfo.IsLevelBtn) await prop.SetState(PropStateEnum.Closed); - - return prop; - } - - public async ValueTask SetPropTimeline(int propEntityId, PropTimelineInfo info) - { - if (SceneInstance == null) return; - SceneInstance.Entities.TryGetValue(propEntityId, out var entity); - if (entity is not EntityProp prop) return; - - var data = new ScenePropTimelineData - { - BoolValue = info.TimelineBoolValue, - ByteValue = info.TimelineByteValue.ToBase64() - }; - - // save to db - SceneData!.PropTimelineData.TryGetValue(Data.FloorId, out var floorData); - if (floorData == null) - { - floorData = new Dictionary>(); - SceneData.PropTimelineData[Data.FloorId] = floorData; - } - - if (!floorData.ContainsKey(prop.GroupId)) - floorData[prop.GroupId] = new Dictionary(); - - floorData[prop.GroupId][prop.PropInfo.ID] = data; - - prop.PropTimelineData = data; - - // handle mission / quest - await MissionManager!.HandleFinishType(MissionFinishTypeEnum.TimeLineSetState); - await MissionManager!.HandleFinishType(MissionFinishTypeEnum.TimeLineSetStateCnt); - } - - public ScenePropTimelineData? GetScenePropTimelineData(int floorId, int groupId, int propId) - { - SceneData!.PropTimelineData.TryGetValue(floorId, out var floorData); - if (floorData == null) return null; - floorData.TryGetValue(groupId, out var groupData); - if (groupData == null) return null; - groupData.TryGetValue(propId, out var data); - return data; - } - - public async ValueTask EnterScene(int entryId, int teleportId, bool sendPacket, int storyLineId = 0, - bool mapTp = false) - { - var beforeStoryLineId = StoryLineManager?.StoryLineData.CurStoryLineId; - if (storyLineId != StoryLineManager?.StoryLineData.CurStoryLineId) - { - if (StoryLineManager != null) - await StoryLineManager.EnterStoryLine(storyLineId, entryId == 0); // entryId == 0 -> teleport - mapTp = false; // do not use mapTp when enter story line - } - - GameData.MapEntranceData.TryGetValue(entryId, out var entrance); - if (entrance == null) return false; - - GameData.GetFloorInfo(entrance.PlaneID, entrance.FloorID, out var floorInfo); - - var startGroup = entrance.StartGroupID; - var startAnchor = entrance.StartAnchorID; - - if (teleportId != 0) - { - floorInfo.CachedTeleports.TryGetValue(teleportId, out var teleport); - if (teleport != null) - { - startGroup = teleport.AnchorGroupID; - startAnchor = teleport.AnchorID; - } - } - else if (startAnchor == 0) - { - startGroup = floorInfo.StartGroupID; - startAnchor = floorInfo.StartAnchorID; - } - - var anchor = floorInfo.GetAnchorInfo(startGroup, startAnchor); - - await MissionManager!.HandleFinishType(MissionFinishTypeEnum.EnterMapByEntrance, entrance); - - var beforeEntryId = Data.EntryId; - - await LoadScene(entrance.PlaneID, entrance.FloorID, entryId, anchor!.ToPositionProto(), - anchor.ToRotationProto(), sendPacket, mapTp); - - var afterEntryId = Data.EntryId; - - return beforeEntryId != afterEntryId || - beforeStoryLineId != storyLineId; // return true if entryId changed or story line changed - } - - public async ValueTask EnterSceneByEntranceId(int entranceId, int anchorGroupId, int anchorId, bool sendPacket) - { - GameData.MapEntranceData.TryGetValue(entranceId, out var entrance); - if (entrance == null) return; - - GameData.GetFloorInfo(entrance.PlaneID, entrance.FloorID, out var floorInfo); - - var startGroup = anchorGroupId == 0 ? entrance.StartGroupID : anchorGroupId; - var startAnchor = anchorId == 0 ? entrance.StartAnchorID : anchorId; - - if (startAnchor == 0) - { - startGroup = floorInfo.StartGroupID; - startAnchor = floorInfo.StartAnchorID; - } - - var anchor = floorInfo.GetAnchorInfo(startGroup, startAnchor); - - await LoadScene(entrance.PlaneID, entrance.FloorID, entranceId, anchor!.ToPositionProto(), - anchor.ToRotationProto(), sendPacket); - } - - public async ValueTask MoveTo(Position position) - { - Data.Pos = position; - await SendPacket(new PacketSceneEntityMoveScNotify(this)); - } - - public void MoveTo(EntityMotion motion) - { - Data.Pos = motion.Motion.Pos.ToPosition(); - Data.Rot = motion.Motion.Rot.ToPosition(); - } - - public async ValueTask MoveTo(Position pos, Position rot) - { - Data.Pos = pos; - Data.Rot = rot; - await SendPacket(new PacketSceneEntityMoveScNotify(this)); - } - - public async ValueTask LoadScene(int planeId, int floorId, int entryId, Position pos, Position rot, bool sendPacket, - bool mapTp = false) - { - GameData.MazePlaneData.TryGetValue(planeId, out var plane); - if (plane == null) return; - - if (plane.PlaneType == PlaneTypeEnum.Rogue && RogueManager!.GetRogueInstance() == null) - { - await EnterScene(801120102, 0, sendPacket); - return; - } - - if (plane.PlaneType == PlaneTypeEnum.Raid && RaidManager!.RaidData.CurRaidId == 0) - { - await EnterScene(2000101, 0, sendPacket); - return; - } - - if (plane.PlaneType == PlaneTypeEnum.Challenge && ChallengeManager!.ChallengeInstance == null) - { - await EnterScene(100000103, 0, sendPacket); - return; - } - - // TODO: Sanify check - Data.Pos = pos; - Data.Rot = rot; - var notSendMove = true; - if (planeId != Data.PlaneId || floorId != Data.FloorId || entryId != Data.EntryId || SceneInstance == null || !mapTp) - { - if (SceneInstance != null) - await SceneInstance.OnDestroy(); - SceneInstance instance = new(this, plane, floorId, entryId); - InvokeOnPlayerLoadScene(this, instance); - SceneInstance = instance; - - await instance.SyncLineup(true); - Data.PlaneId = planeId; - Data.FloorId = floorId; - Data.EntryId = entryId; - } - else if (StoryLineManager?.StoryLineData.CurStoryLineId == 0 && - mapTp) // only send move packet when not in story line and mapTp - { - notSendMove = false; - } - - if (MissionManager != null) - await MissionManager.OnPlayerChangeScene(); - - Connection?.SendPacket(CmdIds.SyncServerSceneChangeNotify); - if (sendPacket && notSendMove) - await SendPacket(new PacketEnterSceneByServerScNotify(SceneInstance!)); - else if (sendPacket && !notSendMove) // send move packet - await SendPacket(new PacketSceneEntityMoveScNotify(this)); - - if (MissionManager != null) - { - await MissionManager.HandleFinishType(MissionFinishTypeEnum.EnterFloor); - await MissionManager.HandleFinishType(MissionFinishTypeEnum.EnterPlane); - await MissionManager.HandleFinishType(MissionFinishTypeEnum.NotInFloor); - await MissionManager.HandleFinishType(MissionFinishTypeEnum.NotInPlane); - } - } - - public ScenePropData? GetScenePropData(int floorId, int groupId, int propId) - { - if (SceneData != null) - if (SceneData.ScenePropData.TryGetValue(floorId, out var floorData)) - if (floorData.TryGetValue(groupId, out var groupData)) - { - var propData = groupData.Find(x => x.PropId == propId); - return propData; - } - - return null; - } - - public void SetScenePropData(int floorId, int groupId, int propId, PropStateEnum state) - { - if (SceneData != null) - { - if (!SceneData.ScenePropData.TryGetValue(floorId, out var floorData)) - { - floorData = []; - SceneData.ScenePropData.Add(floorId, floorData); - } - - if (!floorData.TryGetValue(groupId, out var groupData)) - { - groupData = []; - floorData.Add(groupId, groupData); - } - - var propData = groupData.Find(x => x.PropId == propId); // find prop data - if (propData == null) - { - propData = new ScenePropData - { - PropId = propId, - State = state - }; - groupData.Add(propData); - } - else - { - propData.State = state; - } - } - } - - public void EnterSection(int sectionId) - { - if (SceneInstance != null) - { - SceneData!.UnlockSectionIdList.TryGetValue(SceneInstance.FloorId, out var unlockList); - if (unlockList == null) - { - unlockList = [sectionId]; - SceneData.UnlockSectionIdList.Add(SceneInstance.FloorId, unlockList); - } - else - { - SceneData.UnlockSectionIdList[SceneInstance.FloorId].Add(sectionId); - } - } - } - - public void SetCustomSaveData(int entryId, int groupId, string data) - { - if (SceneData != null) - { - if (!SceneData.CustomSaveData.TryGetValue(entryId, out var entryData)) - { - entryData = []; - SceneData.CustomSaveData.Add(entryId, entryData); - } - - entryData[groupId] = data; - } - } - - public async ValueTask ForceQuitBattle() - { - if (BattleInstance != null) - { - BattleInstance = null; - await Connection!.SendPacket(CmdIds.QuitBattleScNotify); - } - } - - #endregion - #region Serialization public PlayerBasicInfo ToProto()