using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Database.Avatar; using EggLink.DanhengServer.Enums.Scene; using EggLink.DanhengServer.Game.Battle; using EggLink.DanhengServer.Game.Challenge; using EggLink.DanhengServer.Game.ChessRogue.Cell; using EggLink.DanhengServer.Game.Player; using EggLink.DanhengServer.Game.Rogue.Scene; using EggLink.DanhengServer.Game.Scene.Entity; using EggLink.DanhengServer.GameServer.Game.Mission; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Server.Packet.Send.Scene; using EggLink.DanhengServer.Util; namespace EggLink.DanhengServer.Game.Scene; public class SceneInstance { #region Scene Details public EntityProp? GetNearestSpring(long minDistSq) { EntityProp? spring = null; long springDist = 0; foreach (var prop in HealingSprings) { var dist = Player.Data?.Pos?.GetFast2dDist(prop.Position) ?? 1000000; if (dist > minDistSq) continue; if (spring == null || dist < springDist) { spring = prop; springDist = dist; } } return spring; } #endregion #region Serialization public SceneInfo ToProto() { SceneInfo sceneInfo = new() { WorldId = (uint)Excel.WorldID, GameModeType = (uint)(CustomGameModeId > 0 ? CustomGameModeId : (int)Excel.PlaneType), PlaneId = (uint)PlaneId, FloorId = (uint)FloorId, EntryId = (uint)EntryId, SceneMissionInfo = new MissionStatusBySceneInfo() }; var playerGroupInfo = new SceneEntityGroupInfo(); // avatar group foreach (var avatar in AvatarInfo) playerGroupInfo.EntityList.Add(avatar.Value.AvatarInfo.ToSceneEntityInfo(avatar.Value.AvatarType)); if (playerGroupInfo.EntityList.Count > 0) { if (LeaderEntityId == 0) { LeaderEntityId = AvatarInfo.Values.First().AvatarInfo.EntityId; sceneInfo.LeaderEntityId = (uint)LeaderEntityId; } else { sceneInfo.LeaderEntityId = (uint)LeaderEntityId; } } sceneInfo.EntityGroupList.Add(playerGroupInfo); List groups = []; // other groups // add entities to groups foreach (var entity in Entities) { if (entity.Value.GroupID == 0) continue; if (groups.FindIndex(x => x.GroupId == entity.Value.GroupID) == -1) groups.Add(new SceneEntityGroupInfo { GroupId = (uint)entity.Value.GroupID }); groups[groups.FindIndex(x => x.GroupId == entity.Value.GroupID)].EntityList.Add(entity.Value.ToProto()); } foreach (var groupId in Groups) // Add for empty group if (groups.FindIndex(x => x.GroupId == groupId) == -1) groups.Add(new SceneEntityGroupInfo { GroupId = (uint)groupId }); foreach (var group in groups) sceneInfo.EntityGroupList.Add(group); // custom save data and floor saved data Player.SceneData!.CustomSaveData.TryGetValue(EntryId, out var data); if (data != null) foreach (var customData in data) sceneInfo.SaveDataList.Add(new CustomSaveData { GroupId = (uint)customData.Key, SaveData = customData.Value }); Player.SceneData!.FloorSavedData.TryGetValue(FloorId, out var floorData); foreach (var value in FloorInfo?.SavedValues ?? []) if (floorData != null && floorData.TryGetValue(value.Name, out var v)) sceneInfo.FloorSavedData[value.Name] = v; else sceneInfo.FloorSavedData[value.Name] = value.DefaultValue; foreach (var value in FloorInfo?.CustomValues ?? []) if (floorData != null && floorData.TryGetValue(value.Name, out var v)) { sceneInfo.FloorSavedData[value.Name] = v; } else { _ = int.TryParse(value.DefaultValue, out var x); sceneInfo.FloorSavedData[value.Name] = x; } // mission Player.MissionManager!.OnLoadScene(sceneInfo); // unlock section if (!ConfigManager.Config.ServerOption.AutoLightSection) { Player.SceneData!.UnlockSectionIdList.TryGetValue(FloorId, out var unlockSectionList); if (unlockSectionList != null) foreach (var sectionId in unlockSectionList) sceneInfo.LightenSectionList.Add((uint)sectionId); } else { for (uint i = 1; i <= 100; i++) sceneInfo.LightenSectionList.Add(i); } return sceneInfo; } #endregion #region Data public PlayerInstance Player; public MazePlaneExcel Excel; public FloorInfo? FloorInfo; public int FloorId; public int PlaneId; public int EntryId; public int LeaveEntityId; public int LastEntityId; public bool IsLoaded = false; public Dictionary AvatarInfo = []; public int LeaderEntityId; public Dictionary Entities = []; public List Groups = []; public List HealingSprings = []; public SceneEntityLoader? EntityLoader; public int CustomGameModeId; public SceneInstance(PlayerInstance player, MazePlaneExcel excel, int floorId, int entryId) { Player = player; Excel = excel; PlaneId = excel.PlaneID; FloorId = floorId; EntryId = entryId; LeaveEntityId = 0; System.Threading.Tasks.Task.Run(async () => { await SyncLineup(true, true); }).Wait(); GameData.GetFloorInfo(PlaneId, FloorId, out FloorInfo); if (FloorInfo == null) return; switch (Excel.PlaneType) { case PlaneTypeEnum.Rogue: if (Player.ChessRogueManager!.RogueInstance != null) { EntityLoader = new ChessRogueEntityLoader(this); CustomGameModeId = 16; // ChessRogue } else { EntityLoader = new RogueEntityLoader(this, Player); } break; case PlaneTypeEnum.Challenge: EntityLoader = new ChallengeEntityLoader(this, Player); break; default: if (Player.StoryLineManager?.StoryLineData.CurStoryLineId != 0) EntityLoader = new StoryLineEntityLoader(this); else EntityLoader = new SceneEntityLoader(this); break; } System.Threading.Tasks.Task.Run(async () => { await EntityLoader.LoadEntity(); }).Wait(); Player.TaskManager?.SceneTaskTrigger.TriggerFloor(PlaneId, FloorId); } #endregion #region Scene Actions public async ValueTask SyncLineup(bool notSendPacket = false, bool forceSetEntityId = false) { var oldAvatarInfo = AvatarInfo.Values.ToList(); AvatarInfo.Clear(); var sendPacket = false; var AddAvatar = new List(); var RemoveAvatar = new List(); foreach (var avatar in Player.LineupManager?.GetAvatarsFromCurTeam() ?? []) { if (avatar == null) continue; avatar.AvatarInfo.PlayerData = Player.Data; if (forceSetEntityId && avatar.AvatarInfo.EntityId != 0) { RemoveAvatar.Add(new AvatarSceneInfo(new AvatarInfo { EntityId = avatar.AvatarInfo.EntityId }, AvatarType.AvatarFormalType, Player)); avatar.AvatarInfo.EntityId = 0; sendPacket = true; } var avatarInstance = oldAvatarInfo.Find(x => x.AvatarInfo.AvatarId == avatar.AvatarInfo.AvatarId); if (avatarInstance == null) { if (avatar.AvatarInfo.EntityId == 0) avatar.AvatarInfo.EntityId = ++LastEntityId; AddAvatar.Add(avatar); AvatarInfo.Add(avatar.AvatarInfo.EntityId, avatar); sendPacket = true; } else { AvatarInfo.Add(avatarInstance.AvatarInfo.EntityId, avatarInstance); } } ; foreach (var avatar in oldAvatarInfo) if (AvatarInfo.Values.ToList().FindIndex(x => x.AvatarInfo.AvatarId == avatar.AvatarInfo.AvatarId) == -1) { RemoveAvatar.Add(new AvatarSceneInfo(new AvatarInfo { EntityId = avatar.AvatarInfo.EntityId }, AvatarType.AvatarFormalType, Player)); avatar.AvatarInfo.EntityId = 0; sendPacket = true; } var LeaderAvatarId = Player.LineupManager?.GetCurLineup()?.LeaderAvatarId; var LeaderAvatarSlot = Player.LineupManager?.GetCurLineup()?.BaseAvatars ?.FindIndex(x => x.BaseAvatarId == LeaderAvatarId); if (LeaderAvatarSlot == -1) LeaderAvatarSlot = 0; if (AvatarInfo.Count == 0) return; var info = AvatarInfo.Values.ToList()[LeaderAvatarSlot ?? 0]; LeaderEntityId = info.AvatarInfo.EntityId; if (sendPacket && !notSendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(AddAvatar, RemoveAvatar)); } public void SyncGroupInfo() { EntityLoader?.SyncEntity(); } #endregion #region Entity Management public async ValueTask AddEntity(IGameEntity entity) { await AddEntity(entity, IsLoaded); } public async ValueTask AddEntity(IGameEntity entity, bool SendPacket) { if (entity == null || entity.EntityID != 0) return; entity.EntityID = ++LastEntityId; Entities.Add(entity.EntityID, entity); if (SendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(entity)); } public async ValueTask RemoveEntity(IGameEntity monster) { await RemoveEntity(monster, IsLoaded); } public async ValueTask RemoveEntity(IGameEntity monster, bool SendPacket) { Entities.Remove(monster.EntityID); if (SendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(null, monster)); } public List GetEntitiesInGroup(int groupID) { List entities = []; foreach (var entity in Entities) if (entity.Value.GroupID == groupID && entity.Value is T t) entities.Add(t); return entities; } #endregion } public class AvatarSceneInfo(AvatarInfo avatarInfo, AvatarType avatarType, PlayerInstance Player) : IGameEntity { public AvatarInfo AvatarInfo = avatarInfo; public AvatarType AvatarType = avatarType; public List BuffList = []; public int EntityID { get; set; } = avatarInfo.EntityId; public int GroupID { get; set; } = 0; public async ValueTask AddBuff(SceneBuff buff) { var oldBuff = BuffList.Find(x => x.BuffID == buff.BuffID); if (oldBuff != null) { if (oldBuff.IsExpired()) { BuffList.Remove(oldBuff); BuffList.Add(buff); } else { oldBuff.CreatedTime = Extensions.GetUnixMs(); oldBuff.Duration = buff.Duration; await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, oldBuff)); return; } } BuffList.Add(buff); await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, buff)); } public async ValueTask ApplyBuff(BattleInstance instance) { foreach (var buff in BuffList) { if (buff.IsExpired()) continue; instance.Buffs.Add(new MazeBuff(buff)); } await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, BuffList)); BuffList.Clear(); } public SceneEntityInfo ToProto() { return AvatarInfo.ToSceneEntityInfo(AvatarType); } }