using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Enums.Scene; using EggLink.DanhengServer.Game.Scene.Entity; using EggLink.DanhengServer.Server.Packet.Send.Scene; using EggLink.DanhengServer.Util; namespace EggLink.DanhengServer.Game.Scene { public class SceneEntityLoader(SceneInstance scene) { public SceneInstance Scene { get; set; } = scene; public virtual void LoadEntity() { if (Scene.IsLoaded) return; foreach (var group in Scene?.FloorInfo?.Groups.Values!) // Sanity check in SceneInstance { if (group.LoadSide == GroupLoadSideEnum.Client) { continue; } if (group.GroupName.Contains("TrainVisitor")) { continue; } LoadGroup(group); } Scene.IsLoaded = true; } public void SyncEntity() { bool refreshed = false; var oldGroupId = new List(); foreach (var entity in Scene.Entities.Values) { if (!oldGroupId.Contains(entity.GroupID)) oldGroupId.Add(entity.GroupID); } var removeList = new List(); var addList = new List(); foreach (var group in Scene.FloorInfo!.Groups.Values) { if (group.LoadSide == GroupLoadSideEnum.Client) { continue; } if (group.GroupName.Contains("TrainVisitor")) { continue; } if (oldGroupId.Contains(group.Id)) // check if it should be unloaded { if (group.ForceUnloadCondition.IsTrue(Scene.Player.MissionManager!.Data, false) || group.UnloadCondition.IsTrue(Scene.Player.MissionManager!.Data, false)) { foreach (var entity in Scene.Entities.Values) { if (entity.GroupID == group.Id) { Scene.RemoveEntity(entity, false); removeList.Add(entity); refreshed = true; } } } else if (group.OwnerMainMissionID != 0 && Scene.Player.MissionManager!.GetMainMissionStatus(group.OwnerMainMissionID) != Enums.MissionPhaseEnum.Accept) { foreach (var entity in Scene.Entities.Values) { if (entity.GroupID == group.Id) { Scene.RemoveEntity(entity, false); removeList.Add(entity); refreshed = true; } } } } else // check if it should be loaded { var groupList = LoadGroup(group); refreshed = groupList != null || refreshed; addList.AddRange(groupList ?? []); } } if (refreshed) { Scene.Player.SendPacket(new PacketSceneGroupRefreshScNotify(addList, removeList)); } } public virtual List? LoadGroup(GroupInfo info, bool forceLoad = false) { var missionData = Scene.Player.MissionManager!.Data; if (!(info.OwnerMainMissionID == 0 || Scene.Player.MissionManager!.GetMainMissionStatus(info.OwnerMainMissionID) == Enums.MissionPhaseEnum.Accept)) { return null; } if ((!info.LoadCondition.IsTrue(missionData) || info.UnloadCondition.IsTrue(missionData, false) || info.ForceUnloadCondition.IsTrue(missionData, false)) && !forceLoad) { return null; } if (Scene.Entities.Values.ToList().FindIndex(x => x.GroupID == info.Id) != -1) // check if group is already loaded { return null; } var entityList = new List(); foreach (var npc in info.NPCList) { try { if (LoadNpc(npc, info) is EntityNpc entity) { entityList.Add(entity); } } catch{ } } foreach (var monster in info.MonsterList) { try { if (LoadMonster(monster, info) is EntityMonster entity) { entityList.Add(entity); } } catch { } } foreach (var prop in info.PropList) { try { if (LoadProp(prop, info) is EntityProp entity) { entityList.Add(entity); } } catch { } } return entityList; } public virtual List? LoadGroup(int groupId, bool sendPacket = true) { var group = Scene.FloorInfo?.Groups.TryGetValue(groupId, out GroupInfo? v1) == true ? v1 : null; if (group == null) { return null; } var entities = LoadGroup(group, true); if (sendPacket && entities != null && entities.Count > 0) { Scene.Player.SendPacket(new PacketSceneGroupRefreshScNotify(addEntity: entities)); } return entities; } public virtual void UnloadGroup(int groupId) { var group = Scene.FloorInfo?.Groups.TryGetValue(groupId, out GroupInfo? v1) == true ? v1 : null; if (group == null) return; var removeList = new List(); bool refreshed = false; foreach (var entity in Scene.Entities.Values) { if (entity.GroupID == group.Id) { Scene.RemoveEntity(entity, false); removeList.Add(entity); refreshed = true; } } if (refreshed) { Scene.Player.SendPacket(new PacketSceneGroupRefreshScNotify(removeEntity:removeList)); } } public virtual EntityNpc? LoadNpc(NpcInfo info, GroupInfo group, bool sendPacket = false) { if (info.IsClientOnly || info.IsDelete) { return null; } if (!GameData.NpcDataData.ContainsKey(info.NPCID)) { return null; } bool hasDuplicateNpcId = false; foreach (IGameEntity entity in Scene.Entities.Values) { if (entity is EntityNpc eNpc && eNpc.NpcId == info.NPCID) { hasDuplicateNpcId = true; break; } } if (hasDuplicateNpcId) { return null; } EntityNpc npc = new(Scene, group, info); Scene.AddEntity(npc, sendPacket); return npc; } public virtual EntityMonster? LoadMonster(MonsterInfo info, GroupInfo group, bool sendPacket = false) { if (info.IsClientOnly || info.IsDelete) { return null; } GameData.NpcMonsterDataData.TryGetValue(info.NPCMonsterID, out var excel); if (excel == null) { return null; } EntityMonster entity = new(Scene, info.ToPositionProto(), info.ToRotationProto(), group.Id, info.ID, excel, info); Scene.AddEntity(entity, sendPacket); return entity; } public virtual EntityProp? LoadProp(PropInfo info, GroupInfo group, bool sendPacket = false) { if (info.IsClientOnly || info.IsDelete) { return null; } GameData.MazePropData.TryGetValue(info.PropID, out var excel); if (excel == null) { return null; } var prop = new EntityProp(Scene, excel, group, info); if (prop.PropInfo.PropID == 1003) { if (prop.PropInfo.MappingInfoID == 2220) { prop.SetState(PropStateEnum.Open); Scene.AddEntity(prop, sendPacket); } } else { Scene.AddEntity(prop, sendPacket); } if (excel.PropType == PropTypeEnum.PROP_SPRING) { Scene.HealingSprings.Add(prop); prop.SetState(PropStateEnum.CheckPointEnable); } // load from database var propData = Scene.Player.GetScenePropData(Scene.FloorId, group.Id, info.ID); if (propData != null && Scene.Excel.PlaneType != PlaneTypeEnum.Raid) // raid is not saved { prop.State = propData.State; } else { prop.State = info.State; } return prop; } } }