using System.Collections.Concurrent; using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Data.Config.Task; using EggLink.DanhengServer.Enums.Avatar; using EggLink.DanhengServer.Enums.RogueMagic; using EggLink.DanhengServer.Enums.Scene; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Game.RogueMagic; using EggLink.DanhengServer.GameServer.Game.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; namespace EggLink.DanhengServer.GameServer.Game.Task.AvatarTask; public class AbilityLevelTask(PlayerInstance player) { public PlayerInstance Player { get; set; } = player; #region Selector public async ValueTask TargetAlias(AbilityLevelParam param) { await ValueTask.CompletedTask; var selector = param.TargetEvaluator!; var casterEntity = param.CasterEntity; var targetEntities = param.TargetEntities; if (selector is TargetAlias target) return target.Alias switch { "Caster" or "ModifierOwnerEntity" => [casterEntity], "ParamEntity" or "AllEnemy" or "AbilityTargetEntity" => targetEntities, _ => targetEntities }; return new List(); } #endregion #region Manage public async ValueTask TriggerTasks(AdventureAbilityConfigListInfo abilities, List tasks, BaseGameEntity casterEntity, List targetEntities, SceneCastSkillCsReq req, string? modifierName = null) { BattleInstance? instance = null; List battleInfos = []; foreach (var task in tasks) try { var res = await TriggerTask(new AbilityLevelParam(abilities, task, casterEntity, targetEntities, req, modifierName)); if (res.BattleInfos != null) battleInfos.AddRange(res.BattleInfos); if (res.Instance != null) instance = res.Instance; } catch (Exception e) { Logger.GetByClassName().Error("An error occured, ", e); } return new AbilityLevelResult(instance, battleInfos); } public async ValueTask TriggerTask(AbilityLevelParam param) { try { var methodName = param.Act.Type.Replace("RPG.GameCore.", ""); // try to get from cache var method = GetOrCreateExecuteTask(methodName); if (method == null) return new AbilityLevelResult(); var res = method(param); var re = await res; if (re is AbilityLevelResult result) return result; } catch (Exception e) { Logger.GetByClassName().Error("An error occured, ", e); } return new AbilityLevelResult(); } private ExecuteTask? GetOrCreateExecuteTask(string methodName) { // try to get from cache if (_cachedTasks.TryGetValue(methodName, out var method)) return method; var methodProp = GetType().GetMethod(methodName); if (methodProp == null) return null; method = (ExecuteTask)Delegate.CreateDelegate(typeof(ExecuteTask), this, methodProp); _cachedTasks[methodName] = method; // cached return method; } private delegate ValueTask ExecuteTask(AbilityLevelParam param); private readonly ConcurrentDictionary _cachedTasks = []; #endregion #region Task public async ValueTask PredicateTaskList(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; if (param.Act is PredicateTaskList predicateTaskList) { // handle predicateCondition var methodName = predicateTaskList.Predicate.Type.Replace("RPG.GameCore.", ""); var method = GetOrCreateExecuteTask(methodName); var res = true; if (method != null) { var resp = await method(param with { Act = predicateTaskList.Predicate }); if (resp is not bool r) res = false; else res = predicateTaskList.Predicate.Inverse ? !r : r; } if (res) foreach (var task in predicateTaskList.SuccessTaskList) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); if (result.Instance != null) instance = result.Instance; } else foreach (var task in predicateTaskList.FailedTaskList) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); if (result.Instance != null) instance = result.Instance; } } return new AbilityLevelResult(instance, battleInfos); } public async ValueTask AdventureTriggerAttack(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; if (param.Act is AdventureTriggerAttack adventureTriggerAttack) { var methodName = adventureTriggerAttack.AttackTargetType.Type.Replace("RPG.GameCore.", ""); var method = GetOrCreateExecuteTask(methodName); if (method == null) return new AbilityLevelResult(); var resp = await method(param with { TargetEvaluator = adventureTriggerAttack.AttackTargetType }); if (resp is List target) { foreach (var task in adventureTriggerAttack.OnAttack) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); } if (target.Count > 0 && adventureTriggerAttack.TriggerBattle) { foreach (var task in adventureTriggerAttack.OnBattle) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); } foreach (var entity in param.TargetEntities) { var type = MonsterBattleType.TriggerBattle; if (entity is EntityMonster { IsAlive: false }) type = MonsterBattleType.DirectDieSkipBattle; battleInfos.Add(new HitMonsterInstance(entity.EntityId, type)); } instance = await Player.BattleManager!.StartBattle(param.CasterEntity, param.TargetEntities, param.Request.SkillIndex == 1); } } } return new AbilityLevelResult(instance, battleInfos); } public async ValueTask AddMazeBuff(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; if (param.Act is AddMazeBuff addMazeBuff) { var methodName = addMazeBuff.TargetType.Type.Replace("RPG.GameCore.", ""); var method = GetOrCreateExecuteTask(methodName); if (method == null) return new AbilityLevelResult(); var resp = await method(param with { TargetEvaluator = addMazeBuff.TargetType }); Dictionary dynamic = []; foreach (var dynamicValue in addMazeBuff.DynamicValues) dynamic.Add(dynamicValue.Key, dynamicValue.Value.GetValue()); if (resp is not List target) return new AbilityLevelResult(instance, battleInfos); foreach (var entity in target) await entity.AddBuff(new SceneBuff(addMazeBuff.ID, 1, (param.CasterEntity as AvatarSceneInfo)?.AvatarInfo.BaseAvatarId ?? 0, addMazeBuff.LifeTime.FixedValue.Value < -1 ? 20 : -1) { DynamicValues = dynamic }); } return new AbilityLevelResult(instance, battleInfos); } public async ValueTask AdventureFireProjectile(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; if (param.Act is AdventureFireProjectile adventureFireProjectile) { foreach (var task in adventureFireProjectile.OnProjectileHit) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); if (result.Instance != null) instance = result.Instance; } foreach (var task in adventureFireProjectile.OnProjectileLifetimeFinish) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); if (result.Instance != null) instance = result.Instance; } } return new AbilityLevelResult(instance, battleInfos); } public async ValueTask NewAdventureFireProjectile(AbilityLevelParam param) { BattleInstance? instance = null; List battleInfos = []; if (param.Act is AdventureFireProjectile adventureFireProjectile) { foreach (var task in adventureFireProjectile.OnProjectileHit) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); if (result.Instance != null) instance = result.Instance; } foreach (var task in adventureFireProjectile.OnProjectileLifetimeFinish) { var result = await TriggerTask(param with { Act = task }); if (result.BattleInfos != null) battleInfos.AddRange(result.BattleInfos); if (result.Instance != null) instance = result.Instance; } } return new AbilityLevelResult(instance, battleInfos); } public async ValueTask CreateSummonUnit(AbilityLevelParam param) { if (param.Act is CreateSummonUnit createSummonUnit) { if (!GameData.SummonUnitDataData.TryGetValue(createSummonUnit.SummonUnitID, out var excel)) return new AbilityLevelResult(); if (excel.IsClient) return new AbilityLevelResult(); var unit = new EntitySummonUnit { EntityId = 0, CreateAvatarEntityId = param.CasterEntity.EntityId, AttachEntityId = excel.ConfigInfo?.AttachPoint == "Origin" ? param.CasterEntity.EntityId : 0, SummonUnitId = excel.ID, CreateAvatarId = (param.CasterEntity as AvatarSceneInfo)?.AvatarInfo.BaseAvatarId ?? 0, LifeTimeMs = createSummonUnit.Duration.FixedValue.Value == -1 ? -1 : 20000, TriggerList = excel.ConfigInfo?.TriggerConfig.CustomTriggers ?? [], Motion = param.Request.TargetMotion }; await Player.SceneInstance!.AddSummonUnitEntity(unit); } return new AbilityLevelResult(); } public async ValueTask DestroySummonUnit(AbilityLevelParam param) { if (param.Act is DestroySummonUnit destroySummonUnit) await Player.SceneInstance!.RemoveSummonUnitById(destroySummonUnit.SummonUnit.SummonUnitID); // TODO return new AbilityLevelResult(); } public async ValueTask AddAdventureModifier(AbilityLevelParam param) { if (param.Act is AddAdventureModifier addAdventureModifier) { GameData.AdventureModifierData.TryGetValue(addAdventureModifier.ModifierName, out var modifier); if (modifier == null) return new AbilityLevelResult(); if (param.CasterEntity is IGameModifier mod) await mod.AddModifier(addAdventureModifier.ModifierName); } return new AbilityLevelResult(); } public async ValueTask RemoveAdventureModifier(AbilityLevelParam param) { if (param.Act is RemoveAdventureModifier removeAdventureModifier) { GameData.AdventureModifierData.TryGetValue(removeAdventureModifier.ModifierName, out var modifier); if (modifier == null) return new AbilityLevelResult(); if (param.CasterEntity is IGameModifier mod) await mod.RemoveModifier(removeAdventureModifier.ModifierName); } return new AbilityLevelResult(); } public async ValueTask RemoveSelfModifier(AbilityLevelParam param) { if (param.ModifierName != null) if (param.CasterEntity is IGameModifier mod) await mod.RemoveModifier(param.ModifierName); return new AbilityLevelResult(); } public async ValueTask RefreshMazeBuffTime(AbilityLevelParam param) { if (param.Act is RefreshMazeBuffTime refreshMazeBuffTime) { // get buff var buff = param.CasterEntity.BuffList.FirstOrDefault(x => x.BuffId == refreshMazeBuffTime.ID); if (buff == null) return new AbilityLevelResult(); buff.Duration = refreshMazeBuffTime.LifeTime.GetValue(); await Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(param.CasterEntity, buff)); } return new AbilityLevelResult(); } public async ValueTask AdvModifyMaxMazeMP(AbilityLevelParam param) { await ValueTask.CompletedTask; if (param.Act is AdvModifyMaxMazeMP advModifyMaxMazeMp) switch (advModifyMaxMazeMp.ModifyFunction) { case PropertyModifyFunctionEnum.Add: Player.LineupManager!.LineupData.ExtraMpCount += advModifyMaxMazeMp.ModifyValue.GetValue(); break; case PropertyModifyFunctionEnum.Set: Player.LineupManager!.LineupData.ExtraMpCount = advModifyMaxMazeMp.ModifyValue.GetValue() - 5; break; } return new AbilityLevelResult(); } public async ValueTask AdventureSetAttackTargetMonsterDie(AbilityLevelParam param) { var avatar = param.CasterEntity as AvatarSceneInfo; if (GameData.AvatarConfigData.TryGetValue(avatar?.AvatarInfo.AvatarId ?? 0, out var excel)) { var adventurePlayerExcel = GameData.AdventurePlayerData.GetValueOrDefault(excel.AdventurePlayerID); if (adventurePlayerExcel != null && adventurePlayerExcel.MazeSkillIdList.Count > param.Request.SkillIndex) { var skill = GameData.MazeSkillData.GetValueOrDefault( adventurePlayerExcel.MazeSkillIdList[(int)param.Request.SkillIndex]); await Player.LineupManager!.CostMp(skill?.MPCost ?? 1, param.Request.CastEntityId); } } foreach (var targetEntity in param.TargetEntities) { if (targetEntity is not EntityMonster monster) continue; if (monster.MonsterData.Rank < MonsterRankEnum.Elite) { await monster.Kill(); var instance = monster.Scene.Player.RogueManager!.GetRogueInstance(); switch (instance) { case null: continue; case RogueMagicInstance magic: await magic.RollMagicUnit(1, 1, [RogueMagicUnitCategoryEnum.Common]); break; default: await instance.RollBuff(1); break; } await instance.GainMoney(Random.Shared.Next(20, 60)); } } return new AbilityLevelResult(); } #endregion #region Predicate public async ValueTask ByAllowInstantKill(AbilityLevelParam param) { await ValueTask.CompletedTask; foreach (var targetEntity in param.TargetEntities) if (targetEntity is EntityMonster monster) if (monster.MonsterData.Rank < MonsterRankEnum.Elite) return true; return false; } public async ValueTask ByIsContainAdventureModifier(AbilityLevelParam param) { await ValueTask.CompletedTask; if (param.Act is ByIsContainAdventureModifier byIsContain) { // get target var result = false; var methodName = byIsContain.TargetType.Type.Replace("RPG.GameCore.", ""); var method = GetOrCreateExecuteTask(methodName); if (method == null) return false; var resp = await method(param with { TargetEvaluator = byIsContain.TargetType }); if (resp is List target) foreach (var entity in target) { if (entity is not IGameModifier modifier) continue; if (modifier.Modifiers.Contains(byIsContain.ModifierName)) { result = true; break; } } return result; } return false; } public async ValueTask AdventureByInMotionState(AbilityLevelParam param) { await ValueTask.CompletedTask; return true; } public async ValueTask AdventureByPlayerCurrentSkillType(AbilityLevelParam param) { await ValueTask.CompletedTask; if (param.Act is AdventureByPlayerCurrentSkillType byPlayerCurrentSkillType) return param.Request.SkillIndex == (uint)byPlayerCurrentSkillType.SkillType; return false; } public async ValueTask ByCompareCarryMazebuff(AbilityLevelParam param) { await ValueTask.CompletedTask; if (param.Act is ByCompareCarryMazebuff byCompareCarryMazebuff) return param.CasterEntity.BuffList.Any(x => x.BuffId == byCompareCarryMazebuff.BuffID); return false; } public async ValueTask ByAnd(AbilityLevelParam param) { await ValueTask.CompletedTask; if (param.Act is ByAnd byAnd) { foreach (var task in byAnd.PredicateList) { var methodName = task.Type.Replace("RPG.GameCore.", ""); var method = GetOrCreateExecuteTask(methodName); if (method == null) return false; var resp = await method(param with { Act = task }); if (resp is not bool res) return false; res = task.Inverse ? !res : res; if (!res) return false; } return true; } return false; } #endregion } public record AbilityLevelResult(BattleInstance? Instance = null, List? BattleInfos = null); public record AbilityLevelParam( AdventureAbilityConfigListInfo AdventureAbility, TaskConfigInfo Act, BaseGameEntity CasterEntity, List TargetEntities, SceneCastSkillCsReq Request, string? ModifierName, TargetEvaluator? TargetEvaluator = null);