using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config.Scene; using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Database.Avatar; using EggLink.DanhengServer.Enums.Avatar; using EggLink.DanhengServer.Enums.Scene; using EggLink.DanhengServer.GameServer.Game.Activity.Loaders; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.Challenge; using EggLink.DanhengServer.GameServer.Game.ChessRogue.Cell; using EggLink.DanhengServer.GameServer.Game.Mission; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Game.Rogue.Scene; using EggLink.DanhengServer.GameServer.Game.RogueMagic.Scene; using EggLink.DanhengServer.GameServer.Game.RogueTourn.Scene; using EggLink.DanhengServer.GameServer.Game.Scene.Component; using EggLink.DanhengServer.GameServer.Game.Scene.Entity; using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Util; using System.Management; using EggLink.DanhengServer.Enums.Mission; namespace EggLink.DanhengServer.GameServer.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 == 100 ? GameConstants.LAST_TRAIN_WORLD_ID : Excel.WorldID), GameModeType = (uint)GameModeType, PlaneId = (uint)PlaneId, FloorId = (uint)FloorId, EntryId = (uint)EntryId, SceneMissionInfo = new MissionStatusBySceneInfo(), DimensionId = (uint)(EntityLoader is StoryLineEntityLoader loader ? loader.DimensionId : 0), GameStoryLineId = (uint)(Player.StoryLineManager?.StoryLineData.CurStoryLineId ?? 0) }; var playerGroupInfo = new SceneEntityGroupInfo(); // avatar group foreach (var avatar in AvatarInfo) playerGroupInfo.EntityList.Add(avatar.Value.ToProto()); if (playerGroupInfo.EntityList.Count > 0) { if (LeaderEntityId == 0) LeaderEntityId = AvatarInfo.Values.First().EntityId; sceneInfo.LeaderEntityId = (uint)LeaderEntityId; } foreach (var summonUnit in SummonUnit.Values) playerGroupInfo.EntityList.Add(summonUnit.ToProto()); 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) { var property = FloorInfo?.Groups.GetValueOrDefault(entity.Value.GroupId)?.GroupPropertyMap ?? []; Dictionary resProperty = []; var savedProp = Player.SceneData!.GroupPropertyData.GetValueOrDefault(FloorId, []) .GetValueOrDefault(entity.Value.GroupId, []); foreach (var info in property.Values.Where(x => x.Side != GroupPropertySideEnum.ClientOnly)) { resProperty.Add(info.Name, savedProp.GetValueOrDefault(info.Name, info.DefaultValue)); } groups.Add(new SceneEntityGroupInfo { GroupId = (uint)entity.Value.GroupId, GroupPropertyMap = { resProperty } }); } 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) { var property = FloorInfo?.Groups.GetValueOrDefault(groupId)?.GroupPropertyMap ?? []; Dictionary resProperty = []; var savedProp = Player.SceneData!.GroupPropertyData.GetValueOrDefault(FloorId, []) .GetValueOrDefault(groupId, []); foreach (var info in property.Values.Where(x => x.Side != GroupPropertySideEnum.ClientOnly)) { resProperty.Add(info.Name, savedProp.GetValueOrDefault(info.Name, info.DefaultValue)); } groups.Add(new SceneEntityGroupInfo { GroupId = (uint)groupId, GroupPropertyMap = { resProperty } }); } 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.CustomDataList.Add(new CustomSaveData { GroupId = (uint)customData.Key, SaveData = customData.Value }); Player.SceneData!.FloorSavedData.TryGetValue(FloorId, out var floorData); foreach (var value in FloorInfo?.FloorSavedValue ?? []) if (floorData != null && floorData.TryGetValue(value.Name, out var v)) sceneInfo.FloorSavedData[value.Name] = v; else if (value.Name.Contains("_IsHidden")) sceneInfo.FloorSavedData[value.Name] = 0; else sceneInfo.FloorSavedData[value.Name] = value.DefaultValue; foreach (var value in floorData ?? []) sceneInfo.FloorSavedData[value.Key] = value.Value; // 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 { GameData.GetFloorInfo(PlaneId, FloorId, out var floorInfo); sceneInfo.LightenSectionList.AddRange(floorInfo.MapSections.Select(x => (uint)x)); } return sceneInfo; } #endregion #region Data public PlayerInstance Player { get; set; } public MazePlaneExcel Excel { get; set; } public FloorInfo? FloorInfo { get; set; } public int FloorId { get; set; } public int PlaneId { get; set; } public int EntryId { get; set; } public int LeaveEntryId { get; set; } public int LastEntityId { get; set; } public bool IsLoaded { get; set; } = false; public Dictionary AvatarInfo { get; set; } = []; public int LeaderEntityId { get; set; } public Dictionary Entities { get; set; } = []; public List Groups { get; set; } = []; public List HealingSprings { get; set; } = []; public SceneEntityLoader? EntityLoader { get; set; } public GameModeTypeEnum GameModeType { get; set; } public List Components { get; set; } = []; public Dictionary SummonUnit { get; set; } = []; public SceneInstance(PlayerInstance player, MazePlaneExcel excel, int floorId, int entryId) { Player = player; Excel = excel; PlaneId = excel.PlaneID; FloorId = floorId; EntryId = entryId; LeaveEntryId = 0; GameData.GetFloorInfo(PlaneId, FloorId, out var floor); FloorInfo = floor; if (FloorInfo == null) return; GameModeType = (GameModeTypeEnum)excel.PlaneType; switch (Excel.PlaneType) { case PlaneTypeEnum.Rogue: if (Player.ChessRogueManager!.RogueInstance != null) { EntityLoader = new ChessRogueEntityLoader(this); GameModeType = GameModeTypeEnum.ChessRogue; // ChessRogue } else if (Player.RogueTournManager!.RogueTournInstance != null) { EntityLoader = new RogueTournEntityLoader(this, Player); GameModeType = GameModeTypeEnum.TournRogue; // TournRogue } else if (Player.RogueMagicManager!.RogueMagicInstance != null) { EntityLoader = new RogueMagicEntityLoader(this, Player); GameModeType = GameModeTypeEnum.MagicRogue; // MagicRogue } else { EntityLoader = new RogueEntityLoader(this, Player); } break; case PlaneTypeEnum.Challenge: EntityLoader = new ChallengeEntityLoader(this, Player); break; case PlaneTypeEnum.TrialActivity: EntityLoader = new TrialActivityEntityLoader(this, Player); break; default: EntityLoader = Player.StoryLineManager?.StoryLineData.CurStoryLineId != 0 ? new StoryLineEntityLoader(this) : new SceneEntityLoader(this); break; } foreach (var module in floor.LevelFeatureModules.ToHashSet()) { switch (module) { case LevelFeatureTypeEnum.EraFlipper: Components.Add(new EraFlipperSceneComponent(this)); break; case LevelFeatureTypeEnum.RotatableRegion: Components.Add(new RotatableRegionSceneComponent(this)); break; } } if (GameData.SceneRainbowGroupPropertyData.FloorProperty.ContainsKey(FloorId)) { Components.Add(new RainbowSceneComponent(this)); } System.Threading.Tasks.Task.Run(async () => { await EntityLoader.LoadEntity(); }).Wait(); Player.TaskManager?.SceneTaskTrigger.TriggerFloor(PlaneId, FloorId); _ = InitializeComponents(); } #endregion #region Event public delegate ValueTask GroupPropertyUpdateArg(GroupPropertyRefreshData data); public event GroupPropertyUpdateArg? GroupPropertyUpdated; #endregion #region Scene Actions public async ValueTask UpdateGroupProperty(int groupId, string name, int value, bool callEvent = true) { // save if (!Player.SceneData!.GroupPropertyData.TryGetValue(FloorId, out var groupData)) { groupData = []; Player.SceneData.GroupPropertyData[FloorId] = groupData; } var group = FloorInfo?.Groups.GetValueOrDefault(groupId); if (group == null) return new GroupPropertyRefreshData(groupId, name, 0, value); var property = group.GroupPropertyMap.Values .FirstOrDefault(x => x.Name == name); if (property == null) return new GroupPropertyRefreshData(groupId, name, 0, value); if (!groupData.TryGetValue(groupId, out var propertyData)) { propertyData = []; groupData[groupId] = propertyData; } var oldValue = propertyData.GetValueOrDefault(name, property.DefaultValue); propertyData[name] = value; // notify var res = new GroupPropertyRefreshData(groupId, name, oldValue, value); await Player.SendPacket( new PacketSceneGroupRefreshScNotify(this, [res])); if (callEvent && GroupPropertyUpdated != null) await GroupPropertyUpdated(res); if (name == "SGP_PuzzleState" && group.ControlFloorSavedValue.Count > 0) { // set fsv foreach (var key in group.ControlFloorSavedValue) { await UpdateFloorSavedValue(key, value); } } return res; } public async ValueTask UpdateFloorSavedValue(string name, int value) { if (!Player.SceneData!.FloorSavedData.TryGetValue(FloorId, out var floorSavedData)) { floorSavedData = []; Player.SceneData.FloorSavedData[FloorId] = floorSavedData; } if (FloorInfo?.FloorSavedValue.All(x => x.Name != name) == true) return; // not exist floorSavedData[name] = value; await Player.SendPacket(new PacketUpdateFloorSavedValueNotify(name, value, Player)); await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.FloorSavedValue); } public int GetFloorSavedValue(string name) { return Player.SceneData!.GetFloorSavedValue(FloorId, name); } public int GetGroupProperty(int groupId, string name) { if (!Player.SceneData!.GroupPropertyData.TryGetValue(FloorId, out var groupData)) { groupData = []; Player.SceneData.GroupPropertyData[FloorId] = groupData; } if (!groupData.TryGetValue(groupId, out var propertyData)) { propertyData = []; groupData[groupId] = propertyData; } var property = FloorInfo?.Groups.GetValueOrDefault(groupId)?.GroupPropertyMap.Values .FirstOrDefault(x => x.Name == name); if (property == null) return 0; // default value var oldValue = propertyData.GetValueOrDefault(name, property.DefaultValue); return oldValue; } public async ValueTask SyncLineup(bool notSendPacket = false) { var oldAvatarInfo = AvatarInfo.Values.ToList(); AvatarInfo.Clear(); var sendPacket = false; var addAvatar = new List(); var removeAvatar = new List(); var avatars = Player.LineupManager?.GetAvatarsFromCurTeam() ?? []; foreach (var sceneInfo in oldAvatarInfo) if (avatars.FindIndex(x => x.AvatarInfo.BaseAvatarId == sceneInfo.AvatarInfo.BaseAvatarId) != -1) // avatar still in team { AvatarInfo.Add(sceneInfo.EntityId, sceneInfo); } else // avatar leave { removeAvatar.Add(sceneInfo); sendPacket = true; } foreach (var avatar in avatars) // check team avatar { if (AvatarInfo.Any(x => x.Value.AvatarInfo.BaseAvatarId == avatar.AvatarInfo.BaseAvatarId)) continue; // avatar already in team var avatarInfo = new AvatarSceneInfo(avatar.AvatarInfo, avatar.AvatarType, Player) { // assign entity id EntityId = ++LastEntityId }; AvatarInfo.Add(avatarInfo.EntityId, avatarInfo); addAvatar.Add(avatarInfo); sendPacket = true; } foreach (var avatar in removeAvatar) { Entities.Remove(avatar.EntityId); if (avatar is AvatarSceneInfo info) await info.OnDestroyInstance(); } foreach (var avatar in addAvatar) Entities.Add(avatar.EntityId, avatar); if (AvatarInfo.Count == 0) return; var leaderAvatarId = Player.LineupManager?.GetCurLineup()?.LeaderAvatarId; var leaderAvatar = AvatarInfo.Values.FirstOrDefault(x => x.AvatarInfo.BaseAvatarId == leaderAvatarId); if (leaderAvatar == null) { leaderAvatar = AvatarInfo.Values.First(); Player.LineupManager!.GetCurLineup()!.LeaderAvatarId = leaderAvatar.AvatarInfo.BaseAvatarId; } LeaderEntityId = leaderAvatar.EntityId; if (sendPacket && !notSendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, addAvatar, removeAvatar)); } public void SyncGroupInfo() { EntityLoader?.SyncEntity(); } public async ValueTask OnUseSkill(SceneCastSkillCsReq req) { foreach (var entity in Entities.Values.OfType()) { if (!GameData.AvatarConfigData.TryGetValue(entity.AvatarInfo.AvatarId, out var excel)) continue; GameData.AdventureAbilityConfigListData.TryGetValue(excel.AdventurePlayerID, out var avatarAbility); if (avatarAbility == null) continue; foreach (var modifier in entity.Modifiers.ToArray()) { // get modifier info if (!GameData.AdventureModifierData.TryGetValue(modifier, out var config)) continue; if (config.OnAfterLocalPlayerUseSkill.Count > 0) await Player.TaskManager!.AbilityLevelTask.TriggerTasks(avatarAbility, config.OnAfterLocalPlayerUseSkill, entity, [], req, modifier); } } } public async ValueTask OnChangeLeader(int curBaseAvatarId) { foreach (var entity in Entities.Values.OfType()) { if (!GameData.AvatarConfigData.TryGetValue(entity.AvatarInfo.AvatarId, out var excel)) continue; if (curBaseAvatarId == entity.AvatarInfo.BaseAvatarId) continue; // unstage modifier GameData.AdventureAbilityConfigListData.TryGetValue(excel.AdventurePlayerID, out var avatarAbility); if (avatarAbility == null) continue; foreach (var modifier in entity.Modifiers.ToArray()) { // get modifier info if (!GameData.AdventureModifierData.TryGetValue(modifier, out var config)) continue; if (config.OnUnstage.Count > 0) await Player.TaskManager!.AbilityLevelTask.TriggerTasks(avatarAbility, config.OnUnstage, entity, [], new SceneCastSkillCsReq { CastEntityId = (uint)entity.EntityId }, modifier); } } } public async ValueTask OnDestroy() { foreach (var value in AvatarInfo.Values) await value.OnDestroyInstance(); } #endregion #region Components public async ValueTask InitializeComponents() { foreach (var component in Components) { await component.Initialize(); } } public T? GetComponent() where T : BaseSceneComponent { return Components.FirstOrDefault(x => x is T) as T; } #endregion #region Entity Management public async ValueTask AddEntity(BaseGameEntity entity) { await AddEntity(entity, IsLoaded); } public async ValueTask AddEntity(BaseGameEntity entity, bool sendPacket) { if (entity.EntityId != 0) return; entity.EntityId = ++LastEntityId; Entities.Add(entity.EntityId, entity); if (sendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, entity)); } public async ValueTask AddSummonUnitEntity(EntitySummonUnit entity) { if (entity.EntityId != 0) return Retcode.RetServerInternalError; entity.EntityId = ++LastEntityId; // get summon unit excel if (!GameData.SummonUnitDataData.TryGetValue(entity.SummonUnitId, out var summonUnitExcel)) return Retcode.RetMonsterConfigNotExist; BaseGameEntity? removeEntity = null; // get old summon unit var summonUnitKey = summonUnitExcel.UniqueGroup == SummonUnitUniqueGroupEnum.None ? summonUnitExcel.ID : 1; if (SummonUnit.TryGetValue(summonUnitKey, out var oldSummonUnit)) { // clear old summon unit removeEntity = oldSummonUnit; foreach (var e in Entities.Values.Where(x => x is EntityMonster)) { var monster = e as EntityMonster; List buffList = [.. monster!.BuffList]; foreach (var sceneBuff in buffList) if (sceneBuff.SummonUnitEntityId == oldSummonUnit.EntityId) // clear old buff await monster.RemoveBuff(sceneBuff.BuffId); } } await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, entity, removeEntity)); SummonUnit[summonUnitKey] = entity; return Retcode.RetSucc; } public async ValueTask RemoveEntity(BaseGameEntity monster) { await RemoveEntity(monster, IsLoaded); } public async ValueTask RemoveEntity(BaseGameEntity monster, bool sendPacket) { Entities.Remove(monster.EntityId); if (sendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, 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 #region SummonUnit public async ValueTask TriggerSummonUnit(int entityId, string triggerName, List targetIds) { var summonUnit = SummonUnit.Values.FirstOrDefault(x => x.EntityId == entityId); if (summonUnit == null) return Retcode.RetSceneEntityNotExist; // check trigger var trigger = summonUnit.TriggerList.Find(x => x.TriggerName == triggerName); if (trigger == null) return Retcode.RetSceneUseSkillFail; await Player.SendPacket( new PacketRefreshTriggerByClientScNotify(triggerName, (uint)summonUnit.EntityId, targetIds)); // check target List targetEnter = []; List targetExit = []; foreach (var targetId in targetIds) { if (!Entities.TryGetValue((int)targetId, out var entity)) continue; EntityMonster? monster = null; EntityProp? prop = null; switch (entity) { case EntityMonster m: monster = m; break; case EntityProp p: prop = p; break; } if (monster != null) { if (!monster.IsAlive) continue; if (summonUnit.CaughtEntityIds.Contains(monster.EntityId)) continue; summonUnit.CaughtEntityIds.Add(monster.EntityId); targetEnter.Add(monster); } if (prop != null) { if (summonUnit.CaughtEntityIds.Contains(prop.EntityId)) continue; summonUnit.CaughtEntityIds.Add(prop.EntityId); targetEnter.Add(prop); } } foreach (var gameEntity in Entities.Values) { if (gameEntity is not EntityMonster monster) continue; if (summonUnit.CaughtEntityIds.Contains(monster.EntityId) && !targetEnter.Contains(monster)) { summonUnit.CaughtEntityIds.Remove(monster.EntityId); targetExit.Add(monster); } } if (targetEnter.Count > 0) { // enter var config = trigger.OnTriggerEnter; Player.TaskManager!.SummonUnitLevelTask.TriggerTasks(config, targetEnter, summonUnit); } if (targetExit.Count <= 0) return Retcode.RetSucc; { // enter var config = trigger.OnTriggerExit; Player.TaskManager!.SummonUnitLevelTask.TriggerTasks(config, targetExit, summonUnit); } return Retcode.RetSucc; } public async ValueTask RemoveSummonUnitById(int summonUnitId) { var summonUnit = SummonUnit.FirstOrDefault(x => x.Value.SummonUnitId == summonUnitId); if (summonUnit.Value == null) return; await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, null, summonUnit.Value)); SummonUnit.Remove(summonUnit.Key); foreach (var entity in Entities.Values.Where(x => x is EntityMonster)) { var monster = entity as EntityMonster; List buffList = [.. monster!.BuffList]; foreach (var sceneBuff in buffList) if (sceneBuff.SummonUnitEntityId == summonUnit.Value.EntityId) // clear old buff await monster.RemoveBuff(sceneBuff.BuffId); } } public async ValueTask OnEnterStage() { List removeEntities = []; foreach (var unit in SummonUnit.ToArray()) { if (!GameData.SummonUnitDataData.TryGetValue(unit.Value.SummonUnitId, out var excel)) continue; if (!excel.DestroyOnEnterBattle) continue; foreach (var entity in Entities.Values.Where(x => x is EntityMonster)) { var monster = entity as EntityMonster; List buffList = [.. monster!.BuffList]; foreach (var sceneBuff in buffList) if (sceneBuff.SummonUnitEntityId == unit.Value.EntityId) // clear old buff await monster.RemoveBuff(sceneBuff.BuffId); } removeEntities.Add(unit.Value); SummonUnit.Remove(unit.Key); } await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, [], removeEntities)); } public async ValueTask OnHeartBeat() { foreach (var gameEntity in Entities.Values.Clone().Where(x => x is EntityMonster).OfType()) foreach (var sceneBuff in gameEntity.BuffList.Clone().Where(sceneBuff => sceneBuff.IsExpired())) await gameEntity.RemoveBuff(sceneBuff.BuffId); foreach (var gameEntity in AvatarInfo.Values.Clone()) foreach (var sceneBuff in gameEntity.BuffList.Clone().Where(sceneBuff => sceneBuff.IsExpired())) await gameEntity.RemoveBuff(sceneBuff.BuffId); foreach (var unitValue in SummonUnit.Values) { if (unitValue.LifeTimeMs == -1) continue; var endTime = unitValue.CreateTimeMs + unitValue.LifeTimeMs; if (endTime < Extensions.GetUnixMs()) await RemoveSummonUnitById(unitValue.SummonUnitId); } } #endregion } public class AvatarSceneInfo : BaseGameEntity, IGameModifier { public BaseAvatarInfo AvatarInfo { get; set; } public AvatarType AvatarType { get; set; } public PlayerInstance Player { get; set; } public AvatarSceneInfo(BaseAvatarInfo avatarInfo, AvatarType avatarType, PlayerInstance player) { AvatarInfo = avatarInfo; AvatarType = avatarType; Player = player; // initialize enter ability if (!GameData.AvatarConfigData.TryGetValue(avatarInfo.AvatarId, out var excel)) return; var configInfo = GameData.CharacterConfigInfoData.GetValueOrDefault(excel.AdventurePlayerID); GameData.AdventureAbilityConfigListData.TryGetValue(excel.AdventurePlayerID, out var avatarAbility); if (configInfo == null || avatarAbility == null) return; foreach (var info in configInfo.SkillList.Where(x => x.UseType == SkillUseTypeEnum.Passive)) { // cast ability var abilityStr = info.EntryAbility; // get ability var ability = avatarAbility.AbilityList.FirstOrDefault(x => x.Name == abilityStr); if (ability == null) continue; _ = Player.TaskManager!.AbilityLevelTask.TriggerTasks(avatarAbility, ability.OnStart, this, [], new SceneCastSkillCsReq()); } } public override int EntityId { get; set; } public override int GroupId { get; set; } = 0; public override async ValueTask AddBuff(SceneBuff buff) { if (!GameData.MazeBuffData.TryGetValue(buff.BuffId * 10 + buff.BuffLevel, out var buffExcel)) return; var oldBuff = BuffList.Find(x => x.BuffId == buff.BuffId); if (oldBuff != null) { if (oldBuff.IsExpired()) { BuffList.Remove(oldBuff); } else { oldBuff.CreatedTime = Extensions.GetUnixMs(); oldBuff.Duration = buff.Duration; await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, oldBuff)); await AddModifier(buffExcel.ModifierName); return; } } BuffList.Add(buff); await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, buff)); await AddModifier(buffExcel.ModifierName); } public override async ValueTask ApplyBuff(BattleInstance instance) { if (BuffList.Count == 0) return; foreach (var buff in BuffList.Where(buff => !buff.IsExpired())) instance.Buffs.Add(new MazeBuff(buff)); await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, BuffList)); foreach (var sceneBuff in BuffList) { if (!GameData.MazeBuffData.TryGetValue(sceneBuff.BuffId * 10 + sceneBuff.BuffLevel, out var buffExcel)) continue; await RemoveModifier(buffExcel.ModifierName); } BuffList.Clear(); } public override SceneEntityInfo ToProto() { return new SceneEntityInfo { EntityId = (uint)EntityId, Motion = new MotionInfo { Pos = Player.Data.Pos?.ToProto() ?? new Vector(), Rot = Player.Data.Rot?.ToProto() ?? new Vector() }, Actor = new SceneActorInfo { BaseAvatarId = (uint)AvatarInfo.BaseAvatarId, AvatarType = AvatarType } }; } public List Modifiers { get; set; } = []; public async ValueTask AddModifier(string modifierName) { if (Modifiers.Contains(modifierName)) return; Modifiers.Add(modifierName); GameData.AdventureModifierData.TryGetValue(modifierName, out var modifier); GameData.AdventureAbilityConfigListData.TryGetValue(AvatarInfo.AvatarId, out var avatarAbility); if (modifier == null || avatarAbility == null) return; await Player.TaskManager!.AbilityLevelTask.TriggerTasks(avatarAbility, modifier.OnCreate, this, [], new SceneCastSkillCsReq { TargetMotion = new MotionInfo { Pos = Player.Data.Pos?.ToProto() ?? new Vector(), Rot = Player.Data.Rot?.ToProto() ?? new Vector() } }); } public async ValueTask RemoveModifier(string modifierName) { if (!Modifiers.Contains(modifierName)) return; Modifiers.Remove(modifierName); GameData.AdventureModifierData.TryGetValue(modifierName, out var modifier); GameData.AdventureAbilityConfigListData.TryGetValue(AvatarInfo.AvatarId, out var avatarAbility); if (modifier == null || avatarAbility == null) return; await Player.TaskManager!.AbilityLevelTask.TriggerTasks(avatarAbility, modifier.OnDestroy, this, [], new SceneCastSkillCsReq()); } public async ValueTask RemoveBuff(int buffId) { if (!GameData.MazeBuffData.TryGetValue(buffId * 10 + 1, out var buffExcel)) return; var buff = BuffList.Find(x => x.BuffId == buffId); if (buff == null) return; BuffList.Remove(buff); await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, [buff])); await RemoveModifier(buffExcel.ModifierName); } public async ValueTask ClearAllBuff() { if (BuffList.Count == 0) return; await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, BuffList)); foreach (var sceneBuff in BuffList) { if (!GameData.MazeBuffData.TryGetValue(sceneBuff.BuffId * 10 + sceneBuff.BuffLevel, out var buffExcel)) continue; await RemoveModifier(buffExcel.ModifierName); } BuffList.Clear(); } public async ValueTask OnDestroyInstance() { foreach (var modifier in Modifiers.ToArray()) await RemoveModifier(modifier); foreach (var monsterInfo in Player.SceneInstance!.Entities.Values.OfType().ToArray()) foreach (var buff in monsterInfo.BuffList.Where(x => x.OwnerAvatarId == AvatarInfo.BaseAvatarId).ToArray()) await monsterInfo.RemoveBuff(buff.BuffId); } }