diff --git a/Common/Data/Config/AdventureAbilityConfigInfo.cs b/Common/Data/Config/AdventureAbilityConfigInfo.cs new file mode 100644 index 00000000..481163a3 --- /dev/null +++ b/Common/Data/Config/AdventureAbilityConfigInfo.cs @@ -0,0 +1,39 @@ +using EggLink.DanhengServer.Data.Config.Task; +using Newtonsoft.Json.Linq; + +namespace EggLink.DanhengServer.Data.Config; + +public class AdventureAbilityConfigInfo +{ + public List OnAbort { get; set; } = []; + public string Name { get; set; } = ""; + public List OnAdd { get; set; } = []; + public List OnRemove { get; set; } = []; + public List OnStart { get; set; } = []; + + public static AdventureAbilityConfigInfo LoadFromJsonObject(JObject obj) + { + AdventureAbilityConfigInfo info = new(); + + if (obj.ContainsKey(nameof(OnAbort))) + info.OnAbort = obj[nameof(OnAbort)] + ?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)).ToList() ?? []; + + if (obj.ContainsKey(nameof(Name))) + info.Name = obj[nameof(Name)]?.ToObject() ?? ""; + + if (obj.ContainsKey(nameof(OnAdd))) + info.OnAdd = obj[nameof(OnAdd)] + ?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)).ToList() ?? []; + + if (obj.ContainsKey(nameof(OnRemove))) + info.OnRemove = obj[nameof(OnRemove)] + ?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)).ToList() ?? []; + + if (obj.ContainsKey(nameof(OnStart))) + info.OnStart = obj[nameof(OnStart)] + ?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)).ToList() ?? []; + + return info; + } +} \ No newline at end of file diff --git a/Common/Data/Config/AdventureAbilityConfigListInfo.cs b/Common/Data/Config/AdventureAbilityConfigListInfo.cs new file mode 100644 index 00000000..ba69cafc --- /dev/null +++ b/Common/Data/Config/AdventureAbilityConfigListInfo.cs @@ -0,0 +1,25 @@ +using EggLink.DanhengServer.Data.Config.AdventureAbility; +using Newtonsoft.Json.Linq; + +namespace EggLink.DanhengServer.Data.Config; + +public class AdventureAbilityConfigListInfo +{ + public List AbilityList { get; set; } = []; + public Dictionary GlobalModifiers { get; set; } = []; + + public static AdventureAbilityConfigListInfo LoadFromJsonObject(JObject obj) + { + AdventureAbilityConfigListInfo info = new(); + + if (obj.ContainsKey(nameof(AbilityList))) + info.AbilityList = obj[nameof(AbilityList)] + ?.Select(x => AdventureAbilityConfigInfo.LoadFromJsonObject((x as JObject)!)).ToList() ?? []; + + if (!obj.ContainsKey(nameof(GlobalModifiers))) return info; + foreach (var jObject in obj[nameof(GlobalModifiers)]!.ToObject>()!) + info.GlobalModifiers.Add(jObject.Key, AdventureModifierConfig.LoadFromJObject(jObject.Value)); + + return info; + } +} \ No newline at end of file diff --git a/Common/Data/Config/SkillAbilityInfo.cs b/Common/Data/Config/SkillAbilityInfo.cs deleted file mode 100644 index 446171ec..00000000 --- a/Common/Data/Config/SkillAbilityInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EggLink.DanhengServer.Data.Excel; - -namespace EggLink.DanhengServer.Data.Config; - -public class SkillAbilityInfo -{ - public List AbilityList { get; set; } = []; - - public void Loaded(AvatarConfigExcel excel) - { - foreach (var ability in AbilityList) - { - ability.Loaded(); - - excel.MazeAbility.Add(ability.Name, ability); - - if (ability.Name.EndsWith("MazeSkill")) - excel.MazeSkill = ability; - else if (ability.Name.Contains("NormalAtk")) excel.MazeAtk = ability; - } - } -} - -public class AbilityInfo -{ - public string Name { get; set; } = ""; - public List OnStart { get; set; } = []; - - public void Loaded() - { - foreach (var task in OnStart) task.Loaded(); - } -} \ No newline at end of file diff --git a/Common/Data/Config/Task/AddAdventureModifier.cs b/Common/Data/Config/Task/AddAdventureModifier.cs new file mode 100644 index 00000000..75ba9668 --- /dev/null +++ b/Common/Data/Config/Task/AddAdventureModifier.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json.Linq; + +namespace EggLink.DanhengServer.Data.Config.Task; + +public class AddAdventureModifier : TaskConfigInfo +{ + public TargetEvaluator TargetType { get; set; } = new(); + public string ModifierName { get; set; } = ""; + + public new static TaskConfigInfo LoadFromJsonObject(JObject obj) + { + var info = new AddAdventureModifier + { + Type = obj[nameof(Type)]!.ToObject()! + }; + + if (obj.TryGetValue(nameof(TargetType), out var value)) + { + var targetType = value as JObject; + var classType = + System.Type.GetType( + $"EggLink.DanhengServer.Data.Config.Task.{targetType?["Type"]?.ToString().Replace("RPG.GameCore.", "")}"); + classType ??= System.Type.GetType("EggLink.DanhengServer.Data.Config.Task.TargetEvaluator"); + info.TargetType = (targetType!.ToObject(classType!) as TargetEvaluator)!; + } + + if (obj.TryGetValue(nameof(ModifierName), out value)) info.ModifierName = value.ToObject()!; + + return info; + } +} \ No newline at end of file diff --git a/Common/Data/Config/Task/AdventureFireProjectile.cs b/Common/Data/Config/Task/AdventureFireProjectile.cs new file mode 100644 index 00000000..fa28eeb8 --- /dev/null +++ b/Common/Data/Config/Task/AdventureFireProjectile.cs @@ -0,0 +1,46 @@ +using Newtonsoft.Json.Linq; +using System.CodeDom.Compiler; + +namespace EggLink.DanhengServer.Data.Config.Task; + +public class AdventureFireProjectile : TaskConfigInfo +{ + public TargetEvaluator TargetType { get; set; } = new(); + //public ProjectileData Projectile { get; set; } + public List OnProjectileHit { get; set; } = []; + public List OnProjectileLifetimeFinish { get; set; } = []; + public bool WaitProjectileFinish { get; set; } + public string MutexName { get; set; } = ""; + + public new static TaskConfigInfo LoadFromJsonObject(JObject obj) + { + var info = new AdventureFireProjectile + { + Type = obj[nameof(Type)]!.ToObject()! + }; + + if (obj.TryGetValue(nameof(TargetType), out var value)) + { + var targetType = value as JObject; + var classType = + System.Type.GetType( + $"EggLink.DanhengServer.Data.Config.Task.{targetType?["Type"]?.ToString().Replace("RPG.GameCore.", "")}"); + classType ??= System.Type.GetType("EggLink.DanhengServer.Data.Config.Task.TargetEvaluator"); + info.TargetType = (targetType!.ToObject(classType!) as TargetEvaluator)!; + } + + foreach (var item in + obj[nameof(OnProjectileHit)]?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)) ?? []) + info.OnProjectileHit.Add(item); + + foreach (var item in + obj[nameof(OnProjectileLifetimeFinish)]?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)) ?? []) + info.OnProjectileLifetimeFinish.Add(item); + + if (obj.TryGetValue(nameof(WaitProjectileFinish), out value)) info.WaitProjectileFinish = value.ToObject(); + + if (obj.TryGetValue(nameof(MutexName), out value)) info.MutexName = value.ToObject()!; + + return info; + } +} \ No newline at end of file diff --git a/Common/Data/Config/Task/AdventureTriggerAttack.cs b/Common/Data/Config/Task/AdventureTriggerAttack.cs new file mode 100644 index 00000000..71ae8ebc --- /dev/null +++ b/Common/Data/Config/Task/AdventureTriggerAttack.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json.Linq; + +namespace EggLink.DanhengServer.Data.Config.Task; + +public class AdventureTriggerAttack : TaskConfigInfo +{ + public TargetEvaluator AttackTargetType { get; set; } = new(); + public TargetEvaluator AttackRootTargetType { get; set; } = new(); + public bool TriggerBattle { get; set; } + + public float TriggerBattleDelay { get; set; } + + // public AdventureAttackDetectSummonUnitTriggerConfig SummonUnitTriggerAttackDetectConfig { get; set; } + // public AdventureAttackDetectShapeConfig AttackDetectConfig { get; set; } + // public AdventureHitConfig HitConfig { get; set; } + public List OnAttack { get; set; } = []; + public List OnBattle { get; set; } = []; + public List OnHit { get; set; } = []; + public List OnKill { get; set; } = []; + public bool IncludeProps { get; set; } + public bool HitTargetFaceToAttacker { get; set; } + public bool TriggerBattleByAllHitTarget { get; set; } + public bool AttackDetectCollision { get; set; } + + + public new static TaskConfigInfo LoadFromJsonObject(JObject obj) + { + var info = new AdventureTriggerAttack + { + Type = obj[nameof(Type)]!.ToObject()! + }; + if (obj.TryGetValue(nameof(AttackTargetType), out var value)) + { + var targetType = value as JObject; + var classType = + System.Type.GetType( + $"EggLink.DanhengServer.Data.Config.Task.{targetType?["Type"]?.ToString().Replace("RPG.GameCore.", "")}"); + classType ??= System.Type.GetType("EggLink.DanhengServer.Data.Config.Task.TargetEvaluator"); + info.AttackTargetType = (targetType!.ToObject(classType!) as TargetEvaluator)!; + } + + if (obj.TryGetValue(nameof(AttackRootTargetType), out var v)) + { + var targetType = v as JObject; + var classType = + System.Type.GetType( + $"EggLink.DanhengServer.Data.Config.Task.{targetType?["Type"]?.ToString().Replace("RPG.GameCore.", "")}"); + classType ??= System.Type.GetType("EggLink.DanhengServer.Data.Config.Task.TargetEvaluator"); + info.AttackRootTargetType = (targetType!.ToObject(classType!) as TargetEvaluator)!; + } + + foreach (var item in + obj[nameof(OnAttack)]?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)) ?? []) + info.OnAttack.Add(item); + foreach (var item in + obj[nameof(OnBattle)]?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)) ?? []) + info.OnBattle.Add(item); + foreach (var item in + obj[nameof(OnHit)]?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)) ?? []) + info.OnHit.Add(item); + foreach (var item in + obj[nameof(OnKill)]?.Select(x => TaskConfigInfo.LoadFromJsonObject((x as JObject)!)) ?? []) + info.OnKill.Add(item); + if (obj.TryGetValue(nameof(TriggerBattle), out value)) info.TriggerBattle = value.ToObject(); + if (obj.TryGetValue(nameof(TriggerBattleDelay), out value)) + info.TriggerBattleDelay = value.ToObject(); + if (obj.TryGetValue(nameof(IncludeProps), out value)) info.IncludeProps = value.ToObject(); + if (obj.TryGetValue(nameof(HitTargetFaceToAttacker), out value)) + info.HitTargetFaceToAttacker = value.ToObject(); + if (obj.TryGetValue(nameof(TriggerBattleByAllHitTarget), out value)) + info.TriggerBattleByAllHitTarget = value.ToObject(); + if (obj.TryGetValue(nameof(AttackDetectCollision), out value)) + info.AttackDetectCollision = value.ToObject(); + return info; + } +} \ No newline at end of file diff --git a/Common/Data/Config/Task/ByCharacterDamageType.cs b/Common/Data/Config/Task/ByCharacterDamageType.cs new file mode 100644 index 00000000..4fff6a0f --- /dev/null +++ b/Common/Data/Config/Task/ByCharacterDamageType.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.Enums.Avatar; +using EggLink.DanhengServer.Enums.Task; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace EggLink.DanhengServer.Data.Config.Task; + +public class ByCharacterDamageType : PredicateConfigInfo +{ + + [JsonConverter(typeof(StringEnumConverter))] + public DamageTypeEnum DamageType { get; set; } = DamageTypeEnum.Fire; +} \ No newline at end of file diff --git a/Common/Data/Config/Task/CreateSummonUnit.cs b/Common/Data/Config/Task/CreateSummonUnit.cs new file mode 100644 index 00000000..3153eea3 --- /dev/null +++ b/Common/Data/Config/Task/CreateSummonUnit.cs @@ -0,0 +1,6 @@ +namespace EggLink.DanhengServer.Data.Config.Task; + +public class DestroySummonUnit : TaskConfigInfo +{ + public int SummonUnitID { get; set; } +} \ No newline at end of file diff --git a/Common/Data/Config/Task/DestroySummonUnit.cs b/Common/Data/Config/Task/DestroySummonUnit.cs new file mode 100644 index 00000000..b486b6ba --- /dev/null +++ b/Common/Data/Config/Task/DestroySummonUnit.cs @@ -0,0 +1,6 @@ +namespace EggLink.DanhengServer.Data.Config.Task; + +public class CreateSummonUnit : TaskConfigInfo +{ + public int SummonUnitID { get; set; } +} \ No newline at end of file diff --git a/Common/Data/Config/Task/PropStateExecute.cs b/Common/Data/Config/Task/PropStateExecute.cs index 9b654fb0..9b01529e 100644 --- a/Common/Data/Config/Task/PropStateExecute.cs +++ b/Common/Data/Config/Task/PropStateExecute.cs @@ -20,6 +20,7 @@ public class PropStateExecute : TaskConfigInfo { Type = obj[nameof(Type)]!.ToObject()! }; + if (obj.ContainsKey(nameof(TargetType))) { var targetType = obj[nameof(TargetType)] as JObject; diff --git a/Common/Data/Config/Task/RefreshMazeBuffTime.cs b/Common/Data/Config/Task/RefreshMazeBuffTime.cs index 9369a0db..3c342fbc 100644 --- a/Common/Data/Config/Task/RefreshMazeBuffTime.cs +++ b/Common/Data/Config/Task/RefreshMazeBuffTime.cs @@ -8,7 +8,6 @@ public class RefreshMazeBuffTime : TaskConfigInfo public int ID { get; set; } public DynamicFloat LifeTime { get; set; } = new(); - public new static TaskConfigInfo LoadFromJsonObject(JObject obj) { var info = new RefreshMazeBuffTime diff --git a/Common/Data/Config/Task/RemoveAdventureModifier.cs b/Common/Data/Config/Task/RemoveAdventureModifier.cs new file mode 100644 index 00000000..36c08ec7 --- /dev/null +++ b/Common/Data/Config/Task/RemoveAdventureModifier.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json.Linq; + +namespace EggLink.DanhengServer.Data.Config.Task; + +public class RemoveAdventureModifier : TaskConfigInfo +{ + public TargetEvaluator TargetType { get; set; } = new(); + public string ModifierName { get; set; } = ""; + + public new static TaskConfigInfo LoadFromJsonObject(JObject obj) + { + var info = new RemoveAdventureModifier + { + Type = obj[nameof(Type)]!.ToObject()! + }; + + if (obj.TryGetValue(nameof(TargetType), out var value)) + { + var targetType = value as JObject; + var classType = + System.Type.GetType( + $"EggLink.DanhengServer.Data.Config.Task.{targetType?["Type"]?.ToString().Replace("RPG.GameCore.", "")}"); + classType ??= System.Type.GetType("EggLink.DanhengServer.Data.Config.Task.TargetEvaluator"); + info.TargetType = (targetType!.ToObject(classType!) as TargetEvaluator)!; + } + + if (obj.TryGetValue(nameof(ModifierName), out value)) info.ModifierName = value.ToObject()!; + + return info; + } +} \ No newline at end of file diff --git a/Common/Data/Config/Task/TargetAlias.cs b/Common/Data/Config/Task/TargetAlias.cs new file mode 100644 index 00000000..a5bd4bc7 --- /dev/null +++ b/Common/Data/Config/Task/TargetAlias.cs @@ -0,0 +1,6 @@ +namespace EggLink.DanhengServer.Data.Config.Task; + +public class TargetAlias : TargetEvaluator +{ + public string Alias { get; set; } = ""; +} \ No newline at end of file diff --git a/Common/Data/Config/Task/TaskConfigInfo.cs b/Common/Data/Config/Task/TaskConfigInfo.cs index bf10c51a..41f8f6af 100644 --- a/Common/Data/Config/Task/TaskConfigInfo.cs +++ b/Common/Data/Config/Task/TaskConfigInfo.cs @@ -58,6 +58,34 @@ public class TaskConfigInfo return res; } + if (typeStr == "AdventureFireProjectile") + { + var res = AdventureFireProjectile.LoadFromJsonObject(json); + res.Type = type; + return res; + } + + if (typeStr == "AdventureTriggerAttack") + { + var res = AdventureTriggerAttack.LoadFromJsonObject(json); + res.Type = type; + return res; + } + + if (typeStr == "AddAdventureModifier") + { + var res = AddAdventureModifier.LoadFromJsonObject(json); + res.Type = type; + return res; + } + + if (typeStr == "RemoveAdventureModifier") + { + var res = RemoveAdventureModifier.LoadFromJsonObject(json); + res.Type = type; + return res; + } + if (typeClass != null) { var res = (TaskConfigInfo)json.ToObject(typeClass)!; diff --git a/Common/Data/Excel/AvatarConfigExcel.cs b/Common/Data/Excel/AvatarConfigExcel.cs index b80c34b2..772d2b5f 100644 --- a/Common/Data/Excel/AvatarConfigExcel.cs +++ b/Common/Data/Excel/AvatarConfigExcel.cs @@ -32,13 +32,6 @@ public class AvatarConfigExcel : ExcelResource [JsonIgnore] public int RankUpItemId { get; set; } - [JsonIgnore] public string NameKey { get; set; } = ""; - - [JsonIgnore] public AbilityInfo? MazeSkill { get; set; } - - [JsonIgnore] public AbilityInfo? MazeAtk { get; set; } - [JsonIgnore] public Dictionary MazeAbility { get; set; } = []; - public override int GetId() { return AvatarID; @@ -48,10 +41,6 @@ public class AvatarConfigExcel : ExcelResource { if (!GameData.AvatarConfigData.ContainsKey(AvatarID)) GameData.AvatarConfigData.Add(AvatarID, this); RankUpItemId = AvatarID + 10000; - - var regex = new Regex(@"(?<=Avatar_)(.*?)(?=_Config)"); - var match = regex.Match(JsonPath ?? ""); - if (match.Success) NameKey = match.Value; JsonPath = null; } } \ No newline at end of file diff --git a/Common/Data/Excel/MazeBuffExcel.cs b/Common/Data/Excel/MazeBuffExcel.cs index 3cbdc7b5..385a4d62 100644 --- a/Common/Data/Excel/MazeBuffExcel.cs +++ b/Common/Data/Excel/MazeBuffExcel.cs @@ -2,7 +2,7 @@ namespace EggLink.DanhengServer.Data.Excel; -[ResourceEntity("MazeBuff.json")] +[ResourceEntity("MazeBuff.json,AvatarMazeBuff.json", isMultifile:true)] public class MazeBuffExcel : ExcelResource { public int ID { get; set; } @@ -16,7 +16,7 @@ public class MazeBuffExcel : ExcelResource public override void Loaded() { - GameData.MazeBuffData.Add(GetId(), this); + GameData.MazeBuffData.TryAdd(GetId(), this); } public BattleBuff ToProto() diff --git a/Common/Data/Excel/NPCMonsterDataExcel.cs b/Common/Data/Excel/NPCMonsterDataExcel.cs index 395f33e3..cb6fba94 100644 --- a/Common/Data/Excel/NPCMonsterDataExcel.cs +++ b/Common/Data/Excel/NPCMonsterDataExcel.cs @@ -9,6 +9,8 @@ public class NPCMonsterDataExcel : ExcelResource { public int ID { get; set; } public HashName NPCName { get; set; } = new(); + public string JsonPath { get; set; } = ""; + public string ConfigEntityPath { get; set; } = ""; [JsonConverter(typeof(StringEnumConverter))] public MonsterRankEnum Rank { get; set; } diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index 0d8ae977..4cb13f07 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Data.Config.AdventureAbility; using EggLink.DanhengServer.Data.Config.Scene; using EggLink.DanhengServer.Data.Custom; @@ -39,6 +40,7 @@ public static class GameData #region Avatar public static Dictionary AvatarConfigData { get; private set; } = []; + public static Dictionary AdventureAbilityConfigListData { get; private set; } = []; public static Dictionary AvatarPromotionConfigData { get; private set; } = []; public static Dictionary AvatarExpItemConfigData { get; private set; } = []; public static Dictionary AvatarSkillTreeConfigData { get; private set; } = []; diff --git a/Common/Data/ResourceManager.cs b/Common/Data/ResourceManager.cs index 327981a7..03004fad 100644 --- a/Common/Data/ResourceManager.cs +++ b/Common/Data/ResourceManager.cs @@ -416,9 +416,44 @@ public class ResourceManager using var reader = file.OpenRead(); using StreamReader reader2 = new(reader); var text = reader2.ReadToEnd().Replace("$type", "Type"); - var skillAbilityInfo = JsonConvert.DeserializeObject(text); - skillAbilityInfo?.Loaded(avatar); - count += skillAbilityInfo == null ? 0 : 1; + var obj = JObject.Parse(text); + + var info = AdventureAbilityConfigListInfo.LoadFromJsonObject(obj); + + GameData.AdventureAbilityConfigListData.TryAdd(avatar.AvatarID, info); + count++; + } + catch (Exception ex) + { + ResourceCache.IsComplete = false; + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToReadItem", adventurePath, + I18NManager.Translate("Word.Error")), ex); + } + }); + + var res2 = Parallel.ForEach(GameData.NpcMonsterDataData.Values, adventure => + { + var adventurePath = adventure.ConfigEntityPath.Replace("_Entity.json", "_Ability.json").Replace("_Config.json", "_Ability.json") + .Replace("ConfigEntity", "ConfigAdventureAbility"); + + var path = ConfigManager.Config.Path.ResourcePath + "/" + adventurePath; + var file = new FileInfo(path); + if (!file.Exists) + { + return; + } + try + { + using var reader = file.OpenRead(); + using StreamReader reader2 = new(reader); + var text = reader2.ReadToEnd().Replace("$type", "Type"); + var obj = JObject.Parse(text); + + var info = AdventureAbilityConfigListInfo.LoadFromJsonObject(obj); + + GameData.AdventureAbilityConfigListData.TryAdd(adventure.ID, info); + count++; } catch (Exception ex) { @@ -430,9 +465,9 @@ public class ResourceManager }); // wait it done - while (!res.IsCompleted) Thread.Sleep(10); + while (!res.IsCompleted || !res2.IsCompleted) Thread.Sleep(10); - if (count < GameData.AdventurePlayerData.Count) + if (count < GameData.AdventurePlayerData.Count + GameData.NpcMonsterDataData.Count) Logger.Warn(I18NManager.Translate("Server.ServerInfo.ConfigMissing", I18NManager.Translate("Word.MazeSkillInfo"), $"{ConfigManager.Config.Path.ResourcePath}/Config/Level/AdventureAbility", diff --git a/GameServer/Game/Battle/BattleInstance.cs b/GameServer/Game/Battle/BattleInstance.cs index ffe7e9c3..43c9a815 100644 --- a/GameServer/Game/Battle/BattleInstance.cs +++ b/GameServer/Game/Battle/BattleInstance.cs @@ -3,6 +3,7 @@ using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Database.Avatar; using EggLink.DanhengServer.Database.Inventory; +using EggLink.DanhengServer.Enums.Avatar; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Game.Scene; using EggLink.DanhengServer.GameServer.Game.Scene.Entity; @@ -243,6 +244,14 @@ public class BattleInstance(PlayerInstance player, LineupInfo lineup, List x.BuffID == buff.BuffID && x.DynamicValues.Count == 0); + } + } + foreach (var eventInstance in BattleEvents.Values) proto.BattleEvent.Add(eventInstance.ToProto()); for (var i = 1; i <= 5; i++) diff --git a/GameServer/Game/Battle/BattleManager.cs b/GameServer/Game/Battle/BattleManager.cs index 3f8fe2d1..5416f38d 100644 --- a/GameServer/Game/Battle/BattleManager.cs +++ b/GameServer/Game/Battle/BattleManager.cs @@ -1,7 +1,6 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Database.Inventory; -using EggLink.DanhengServer.GameServer.Game.Battle.Skill; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Game.RogueMagic; using EggLink.DanhengServer.GameServer.Game.Scene; @@ -17,21 +16,21 @@ namespace EggLink.DanhengServer.GameServer.Game.Battle; public class BattleManager(PlayerInstance player) : BasePlayerManager(player) { - public StageConfigExcel? NextBattleStageConfig { get; set; } = null; - public async ValueTask StartBattle(SceneCastSkillCsReq req, MazeSkill skill, List hitTargetEntityIdList) + public StageConfigExcel? NextBattleStageConfig { get; set; } + + public async ValueTask StartBattle(IGameEntity attackEntity, List targetEntityList, bool isSkill) { - if (Player.BattleInstance != null) return; + if (Player.BattleInstance != null) return Player.BattleInstance; var targetList = new List(); var avatarList = new List(); var propList = new List(); - Player.SceneInstance!.AvatarInfo.TryGetValue((int)req.AttackedByEntityId, out var castAvatar); + Player.SceneInstance!.AvatarInfo.TryGetValue(attackEntity.EntityID, out var castAvatar); - if (Player.SceneInstance!.AvatarInfo.ContainsKey((int)req.AttackedByEntityId)) + if (castAvatar != null) { - foreach (var entity in hitTargetEntityIdList) + foreach (var entity in targetEntityList) { - Player.SceneInstance!.Entities.TryGetValue((int)entity, out var entityInstance); - switch (entityInstance) + switch (entity) { case EntityMonster monster: targetList.Add(monster); @@ -41,35 +40,24 @@ public class BattleManager(PlayerInstance player) : BasePlayerManager(player) break; } } - - foreach (var info in req.AssistMonsterEntityInfo) - foreach (var entity in info.EntityIdList) - { - Player.SceneInstance!.Entities.TryGetValue((int)entity, out var entityInstance); - if (entityInstance is not EntityMonster monster) continue; - if (targetList.Contains(monster)) continue; // avoid adding the same monster twice - targetList.Add(monster); - } } else { var isAmbushed = - hitTargetEntityIdList.Any(entity => Player.SceneInstance!.AvatarInfo.ContainsKey((int)entity)); + targetEntityList.Any(entity => Player.SceneInstance!.AvatarInfo.ContainsKey(entity.EntityID)); if (!isAmbushed) { - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, [])); - return; + return null; } - var monsterEntity = Player.SceneInstance!.Entities[(int)req.AttackedByEntityId]; + var monsterEntity = Player.SceneInstance!.Entities[attackEntity.EntityID]; if (monsterEntity is EntityMonster monster) targetList.Add(monster); } if (targetList.Count == 0 && propList.Count == 0) { - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, [])); - return; + return null; } foreach (var prop in propList) @@ -94,49 +82,20 @@ public class BattleManager(PlayerInstance player) : BasePlayerManager(player) if (targetList.Count > 0) { - // Skill handle - if (!skill.TriggerBattle) - { - // Skill is not supposed to trigger a battle - List hitMonsterInstances = []; - hitMonsterInstances.AddRange(targetList.Select(entityMonster => - new HitMonsterInstance(entityMonster.EntityID, MonsterBattleType.NoBattle))); - - skill.OnHitTarget(Player.SceneInstance!.AvatarInfo[(int)req.AttackedByEntityId], targetList); - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, hitMonsterInstances)); - return; - } - - if (castAvatar != null) - { - skill.OnAttack(Player.SceneInstance!.AvatarInfo[(int)req.AttackedByEntityId], targetList); - skill.OnCast(castAvatar, Player); - } - var triggerBattle = targetList.Any(target => target.IsAlive); if (!triggerBattle) { - List hitMonsterInstances = []; - hitMonsterInstances.AddRange(targetList.Select(entityMonster => - new HitMonsterInstance(entityMonster.EntityID, MonsterBattleType.DirectDieSimulateBattle))); - - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, hitMonsterInstances)); - return; + return null; } var inst = Player.RogueManager!.GetRogueInstance(); if (inst is RogueMagicInstance { CurLevel.CurRoom.AdventureInstance: not null } magic) { - List hitMonsterInstances = []; - hitMonsterInstances.AddRange(targetList.Select(entityMonster => - new HitMonsterInstance(entityMonster.EntityID, MonsterBattleType.DirectDieSkipBattle))); - await magic.HitMonsterInAdventure(targetList); foreach (var entityMonster in targetList) await entityMonster.Kill(); - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, hitMonsterInstances)); - return; + return null; } BattleInstance battleInstance = @@ -169,7 +128,7 @@ public class BattleManager(PlayerInstance player) : BasePlayerManager(player) if (avatarExcel != null) { mazeBuff = new MazeBuff((int)avatarExcel.DamageType, 1, index); - mazeBuff.DynamicValues.Add("SkillIndex", skill.IsMazeSkill ? 2 : 1); + mazeBuff.DynamicValues.Add("SkillIndex", isSkill ? 2 : 1); } } else @@ -181,7 +140,9 @@ public class BattleManager(PlayerInstance player) : BasePlayerManager(player) } if (mazeBuff != null && mazeBuff.BuffID != 0) // avoid adding a buff with ID 0 + { battleInstance.Buffs.Add(mazeBuff); + } battleInstance.AvatarInfo = avatarList; @@ -192,23 +153,14 @@ public class BattleManager(PlayerInstance player) : BasePlayerManager(player) Player.BattleInstance = battleInstance; - // Send battle start packet - List hitMonsterInstance = []; - hitMonsterInstance.AddRange(targetList.Where(x => x.IsAlive).Select(entityMonster => - new HitMonsterInstance(entityMonster.EntityID, MonsterBattleType.TriggerBattle))); - hitMonsterInstance.AddRange(targetList.Where(x => !x.IsAlive).Select(entityMonster => - new HitMonsterInstance(entityMonster.EntityID, MonsterBattleType.DirectDieSkipBattle))); - InvokeOnPlayerEnterBattle(Player, battleInstance); - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, battleInstance, - hitMonsterInstance)); Player.SceneInstance?.ClearSummonUnit(); + + return battleInstance; } - else - { - await Player.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, [])); - } + + return null; } public async ValueTask StartStage(int eventId) diff --git a/GameServer/Game/Battle/SceneSkillManager.cs b/GameServer/Game/Battle/SceneSkillManager.cs new file mode 100644 index 00000000..7c83081b --- /dev/null +++ b/GameServer/Game/Battle/SceneSkillManager.cs @@ -0,0 +1,78 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.GameServer.Game.Player; +using EggLink.DanhengServer.GameServer.Game.Scene; +using EggLink.DanhengServer.GameServer.Game.Scene.Entity; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.Battle; + +public class SceneSkillManager(PlayerInstance player) : BasePlayerManager(player) +{ + public async ValueTask OnCast(SceneCastSkillCsReq req) + { + // get entities + List targetEntities = []; // enemy + IGameEntity? attackEntity; // caster + List addEntityIds = []; + foreach (var id in req.AssistMonsterEntityIdList) + { + if (Player.SceneInstance!.Entities.TryGetValue((int)id, out var v)) + { + targetEntities.Add(v); + addEntityIds.Add((int)id); + } + } + + foreach (var info in req.AssistMonsterEntityInfo) + { + foreach (var id in info.EntityIdList) + { + if (addEntityIds.Contains((int)id)) continue; + if (Player.SceneInstance!.Entities.TryGetValue((int)id, out var v)) + { + targetEntities.Add(v); + addEntityIds.Add((int)id); + } + } + } + + attackEntity = Player.SceneInstance!.Entities.GetValueOrDefault((int)req.AttackedByEntityId); + if (attackEntity == null) return new SkillResultData(Retcode.RetSceneEntityNotExist); + // get ability file + var abilities = GetAbilityConfig(attackEntity); + if (abilities == null || abilities.AbilityList.Count < 1) + return new SkillResultData(Retcode.RetMazeNoAbility); + + var abilityName = !string.IsNullOrEmpty(req.MazeAbilityStr) ? req.MazeAbilityStr: req.SkillIndex == 0 ? "NormalAtk01" : "MazeSkill"; + var targetAbility = abilities.AbilityList.Find(x => x.Name.Contains(abilityName)); + if (targetAbility == null) + { + targetAbility = abilities.AbilityList.FirstOrDefault(); + if (targetAbility == null) + return new SkillResultData(Retcode.RetMazeNoAbility); + } + + // execute ability + var res = await Player.TaskManager!.AbilityLevelTask.TriggerTasks(abilities, targetAbility.OnStart, attackEntity, targetEntities, req); + + return new SkillResultData(Retcode.RetSucc, res.Instance, res.BattleInfos); + } + + private AdventureAbilityConfigListInfo? GetAbilityConfig(IGameEntity entity) + { + if (entity is EntityMonster monster) + { + return GameData.AdventureAbilityConfigListData.GetValueOrDefault(monster.MonsterData.ID); + } + + if (entity is AvatarSceneInfo avatar) + { + return GameData.AdventureAbilityConfigListData.GetValueOrDefault(avatar.AvatarInfo.GetAvatarId()); + } + + return null; + } +} + +public record SkillResultData(Retcode RetCode, BattleInstance? Instance = null, List? TriggerBattleInfos = null); \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/Action/MazeAddMazeBuff.cs b/GameServer/Game/Battle/Skill/Action/MazeAddMazeBuff.cs deleted file mode 100644 index 704d2dee..00000000 --- a/GameServer/Game/Battle/Skill/Action/MazeAddMazeBuff.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill.Action; - -public class MazeAddMazeBuff(int buffId, int duration) : IMazeSkillAction -{ - public int BuffId { get; } = buffId; - - public async ValueTask OnAttack(AvatarSceneInfo avatar, List entities) - { - foreach (var entity in entities) - entity.TempBuff = new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration); - - await System.Threading.Tasks.Task.CompletedTask; - } - - public async ValueTask OnCast(AvatarSceneInfo avatar, PlayerInstance player) - { - await avatar.AddBuff(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration)); - } - - public async ValueTask OnHitTarget(AvatarSceneInfo avatar, List entities) - { - foreach (var entity in entities) - await entity.AddBuff(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration)); - } -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/Action/MazeRemoveMazeBuff.cs b/GameServer/Game/Battle/Skill/Action/MazeRemoveMazeBuff.cs deleted file mode 100644 index 165bf737..00000000 --- a/GameServer/Game/Battle/Skill/Action/MazeRemoveMazeBuff.cs +++ /dev/null @@ -1,26 +0,0 @@ -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill.Action; - -public class MazeRemoveMazeBuff(int buffId) : IMazeSkillAction -{ - public int BuffId { get; } = buffId; - - public async ValueTask OnAttack(AvatarSceneInfo avatar, List entities) - { - await System.Threading.Tasks.Task.CompletedTask; - } - - public async ValueTask OnCast(AvatarSceneInfo avatar, PlayerInstance player) - { - await avatar.RemoveBuff(BuffId); - } - - public async ValueTask OnHitTarget(AvatarSceneInfo avatar, List entities) - { - foreach (var entity in entities) - await entity.RemoveBuff(BuffId); - } -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/Action/MazeRemoveSummonUnit.cs b/GameServer/Game/Battle/Skill/Action/MazeRemoveSummonUnit.cs deleted file mode 100644 index 193c5816..00000000 --- a/GameServer/Game/Battle/Skill/Action/MazeRemoveSummonUnit.cs +++ /dev/null @@ -1,27 +0,0 @@ -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill.Action; - -public class MazeRemoveSummonUnit(int unitId) : IMazeSkillAction -{ - public int SummonUnitId = unitId; - - public async ValueTask OnAttack(AvatarSceneInfo avatar, List entities) - { - await System.Threading.Tasks.Task.CompletedTask; - } - - public async ValueTask OnCast(AvatarSceneInfo avatar, PlayerInstance player) - { - if (player.SceneInstance!.SummonUnit?.SummonUnitId == SummonUnitId) - // remove - await player.SceneInstance!.ClearSummonUnit(); - } - - public async ValueTask OnHitTarget(AvatarSceneInfo avatar, List entities) - { - await System.Threading.Tasks.Task.CompletedTask; - } -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/Action/MazeSetTargetMonsterDie.cs b/GameServer/Game/Battle/Skill/Action/MazeSetTargetMonsterDie.cs deleted file mode 100644 index 177b91a6..00000000 --- a/GameServer/Game/Battle/Skill/Action/MazeSetTargetMonsterDie.cs +++ /dev/null @@ -1,46 +0,0 @@ -using EggLink.DanhengServer.Enums.RogueMagic; -using EggLink.DanhengServer.Enums.Scene; -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.RogueMagic; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill.Action; - -public class MazeSetTargetMonsterDie : IMazeSkillAction -{ - public async ValueTask OnAttack(AvatarSceneInfo avatar, List entities) - { - foreach (var entity in entities) - if (entity.MonsterData.Rank < MonsterRankEnum.Elite) - { - await entity.Kill(); - - await entity.Scene.Player.LineupManager!.CostMp(1, (uint)avatar.EntityID); - var instance = entity.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)); - } - } - - public async ValueTask OnCast(AvatarSceneInfo avatar, PlayerInstance player) - { - await System.Threading.Tasks.Task.CompletedTask; - } - - public async ValueTask OnHitTarget(AvatarSceneInfo avatar, List entities) - { - await System.Threading.Tasks.Task.CompletedTask; - } -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/Action/MazeSummonUnit.cs b/GameServer/Game/Battle/Skill/Action/MazeSummonUnit.cs deleted file mode 100644 index d88967a9..00000000 --- a/GameServer/Game/Battle/Skill/Action/MazeSummonUnit.cs +++ /dev/null @@ -1,37 +0,0 @@ -using EggLink.DanhengServer.Data.Excel; -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; -using EggLink.DanhengServer.Proto; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill.Action; - -public class MazeSummonUnit(SummonUnitDataExcel excel, MotionInfo motion) : IMazeSkillAction -{ - public async ValueTask OnCast(AvatarSceneInfo avatar, PlayerInstance player) - { - var unit = new EntitySummonUnit - { - EntityID = 0, - CreateAvatarEntityId = avatar.EntityID, - AttachEntityId = excel.ConfigInfo?.AttachPoint == "Origin" ? avatar.EntityID : 0, - SummonUnitId = excel.ID, - CreateAvatarId = avatar.AvatarInfo.GetAvatarId(), - LifeTimeMs = 20000, - TriggerList = excel.ConfigInfo?.TriggerConfig.CustomTriggers ?? [], - Motion = motion - }; - - await player.SceneInstance!.AddSummonUnitEntity(unit); - } - - public async ValueTask OnHitTarget(AvatarSceneInfo avatar, List entities) - { - await ValueTask.CompletedTask; - } - - public async ValueTask OnAttack(AvatarSceneInfo avatar, List entities) - { - await ValueTask.CompletedTask; - } -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/IMazeSkillAction.cs b/GameServer/Game/Battle/Skill/IMazeSkillAction.cs deleted file mode 100644 index cfdcedda..00000000 --- a/GameServer/Game/Battle/Skill/IMazeSkillAction.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill; - -public interface IMazeSkillAction -{ - public ValueTask OnCast(AvatarSceneInfo avatar, PlayerInstance player); - - public ValueTask OnHitTarget(AvatarSceneInfo avatar, List entities); - - public ValueTask OnAttack(AvatarSceneInfo avatar, List entities); -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/MazeSkill.cs b/GameServer/Game/Battle/Skill/MazeSkill.cs deleted file mode 100644 index 0a3d8916..00000000 --- a/GameServer/Game/Battle/Skill/MazeSkill.cs +++ /dev/null @@ -1,90 +0,0 @@ -using EggLink.DanhengServer.Data; -using EggLink.DanhengServer.Data.Config; -using EggLink.DanhengServer.Data.Excel; -using EggLink.DanhengServer.Enums.Avatar; -using EggLink.DanhengServer.GameServer.Game.Battle.Skill.Action; -using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Game.Scene; -using EggLink.DanhengServer.GameServer.Game.Scene.Entity; -using EggLink.DanhengServer.Proto; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill; - -public class MazeSkill -{ - public SceneCastSkillCsReq Req; - - public MazeSkill(List taskInfos, SceneCastSkillCsReq req, bool isSkill = false, - AvatarConfigExcel? excel = null) - { - Req = req; - IsMazeSkill = isSkill; - Excel = excel; - foreach (var task in taskInfos) AddAction(task); - - if (GameData.SummonUnitDataData.TryGetValue((excel?.AvatarID ?? 0) * 10 + 1, out var summonUnit) && isSkill && - !summonUnit.IsClient && - req.MazeAbilityStr == "") Actions.Add(new MazeSummonUnit(summonUnit, req.TargetMotion)); - } - - public List Actions { get; } = []; - public bool TriggerBattle { get; private set; } = true; - public bool IsMazeSkill { get; } = true; - public AvatarConfigExcel? Excel { get; private set; } - - public void AddAction(TaskInfo task) - { - switch (task.TaskType) - { - case TaskTypeEnum.None: - break; - case TaskTypeEnum.AddMazeBuff: - Actions.Add(new MazeAddMazeBuff(task.ID, task.LifeTime.GetLifeTime())); - break; - case TaskTypeEnum.RemoveMazeBuff: - Actions.Add(new MazeRemoveMazeBuff(task.ID)); - break; - case TaskTypeEnum.AdventureModifyTeamPlayerHP: - break; - case TaskTypeEnum.AdventureModifyTeamPlayerSP: - break; - case TaskTypeEnum.CreateSummonUnit: - //Actions.Add(new MazeSummonUnit(GameData.SummonUnitDataData[task.SummonUnitID], Req.TargetMotion)); - break; - case TaskTypeEnum.DestroySummonUnit: - Actions.Add(new MazeRemoveSummonUnit(task.SummonUnit.SummonUnitID)); - break; - case TaskTypeEnum.AdventureSetAttackTargetMonsterDie: - Actions.Add(new MazeSetTargetMonsterDie()); - break; - case TaskTypeEnum.SuccessTaskList: - foreach (var t in task.SuccessTaskList) AddAction(t); - break; - case TaskTypeEnum.AdventureTriggerAttack: - if (IsMazeSkill) TriggerBattle = task.TriggerBattle; - - foreach (var t in task.GetAttackInfo()) AddAction(t); - break; - case TaskTypeEnum.AdventureFireProjectile: - foreach (var t in task.OnProjectileHit) AddAction(t); - - foreach (var t in task.OnProjectileLifetimeFinish) AddAction(t); - break; - } - } - - public void OnCast(AvatarSceneInfo info, PlayerInstance player) - { - foreach (var action in Actions) action.OnCast(info, player); - } - - public void OnAttack(AvatarSceneInfo info, List entities) - { - foreach (var action in Actions) action.OnAttack(info, entities); - } - - public void OnHitTarget(AvatarSceneInfo info, List entities) - { - foreach (var action in Actions) action.OnHitTarget(info, entities); - } -} \ No newline at end of file diff --git a/GameServer/Game/Battle/Skill/MazeSkillManager.cs b/GameServer/Game/Battle/Skill/MazeSkillManager.cs deleted file mode 100644 index f53d5e59..00000000 --- a/GameServer/Game/Battle/Skill/MazeSkillManager.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EggLink.DanhengServer.Data; -using EggLink.DanhengServer.Data.Config; -using EggLink.DanhengServer.Proto; - -namespace EggLink.DanhengServer.GameServer.Game.Battle.Skill; - -public static class MazeSkillManager -{ - public static MazeSkill GetSkill(int baseAvatarId, int skillIndex, SceneCastSkillCsReq req) - { - GameData.AvatarConfigData.TryGetValue(baseAvatarId, out var avatarConfig); - MazeSkill mazeSkill = new([], req); - if (avatarConfig == null) return mazeSkill; - - if (skillIndex == 0) - // normal atk - mazeSkill = new MazeSkill(avatarConfig.MazeAtk?.OnStart.ToList() ?? [], req, false, avatarConfig); - else - // maze skill - mazeSkill = new MazeSkill(avatarConfig.MazeSkill?.OnStart.ToList() ?? [], req, true, avatarConfig); - return mazeSkill; - } - - public static MazeSkill GetSkill(int baseAvatarId, AbilityInfo ability, SceneCastSkillCsReq req) - { - GameData.AvatarConfigData.TryGetValue(baseAvatarId, out var avatarConfig); - MazeSkill mazeSkill = new([], req); - if (avatarConfig == null) return mazeSkill; - - mazeSkill = new MazeSkill(ability.OnStart, req, true, avatarConfig); - return mazeSkill; - } -} \ No newline at end of file diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs index e897e39b..969d61ba 100644 --- a/GameServer/Game/Player/PlayerInstance.cs +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -54,6 +54,7 @@ public class PlayerInstance(PlayerData data) public LineupManager? LineupManager { get; private set; } public InventoryManager? InventoryManager { get; private set; } public BattleManager? BattleManager { get; private set; } + public SceneSkillManager? SceneSkillManager { get; private set; } public BattleInstance? BattleInstance { get; set; } #endregion @@ -66,7 +67,6 @@ public class PlayerInstance(PlayerData data) #endregion - #region Quest & Mission Managers public MissionManager? MissionManager { get; private set; } @@ -166,6 +166,7 @@ public class PlayerInstance(PlayerData data) LineupManager = new LineupManager(this); InventoryManager = new InventoryManager(this); BattleManager = new BattleManager(this); + SceneSkillManager = new SceneSkillManager(this); MissionManager = new MissionManager(this); GachaManager = new GachaManager(this); MessageManager = new MessageManager(this); diff --git a/GameServer/Game/Scene/Component/IGameModifier.cs b/GameServer/Game/Scene/Component/IGameModifier.cs new file mode 100644 index 00000000..156d08bc --- /dev/null +++ b/GameServer/Game/Scene/Component/IGameModifier.cs @@ -0,0 +1,8 @@ +namespace EggLink.DanhengServer.GameServer.Game.Scene.Component; + +public interface IGameModifier +{ + public List Modifiers { get; set; } + public ValueTask AddModifier(string modifierName); + public ValueTask RemoveModifier(string modifierName); +} \ No newline at end of file diff --git a/GameServer/Game/Scene/SceneInstance.cs b/GameServer/Game/Scene/SceneInstance.cs index 88618630..365a0cb5 100644 --- a/GameServer/Game/Scene/SceneInstance.cs +++ b/GameServer/Game/Scene/SceneInstance.cs @@ -12,6 +12,7 @@ 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; @@ -290,6 +291,16 @@ public class SceneInstance LeaderEntityId = info.AvatarInfo.EntityId; if (sendPacket && !notSendPacket) await Player.SendPacket(new PacketSceneGroupRefreshScNotify(Player, addAvatar, removeAvatar)); + + foreach (var avatar in removeAvatar) + { + Entities.Remove(avatar.EntityID); + } + + foreach (var avatar in addAvatar) + { + Entities.Add(avatar.EntityID, avatar); + } } public void SyncGroupInfo() @@ -418,7 +429,7 @@ public class SceneInstance // enter var config = trigger.OnTriggerEnter; - Player.TaskManager!.AvatarLevelTask.TriggerTasks(config, targetEnter, SummonUnit); + Player.TaskManager!.SummonUnitLevelTask.TriggerTasks(config, targetEnter, SummonUnit); } if (targetExit.Count <= 0) return Retcode.RetSucc; @@ -426,7 +437,7 @@ public class SceneInstance // enter var config = trigger.OnTriggerExit; - Player.TaskManager!.AvatarLevelTask.TriggerTasks(config, targetExit, SummonUnit); + Player.TaskManager!.SummonUnitLevelTask.TriggerTasks(config, targetExit, SummonUnit); } diff --git a/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs b/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs new file mode 100644 index 00000000..40379cc1 --- /dev/null +++ b/GameServer/Game/Task/AvatarTask/AbilityLevelTask.cs @@ -0,0 +1,372 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Data.Config.Task; +using EggLink.DanhengServer.GameServer.Game.Battle; +using EggLink.DanhengServer.GameServer.Game.Player; +using EggLink.DanhengServer.GameServer.Game.Scene; +using EggLink.DanhengServer.GameServer.Game.Scene.Entity; +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 Manage + + public async ValueTask TriggerTasks(AdventureAbilityConfigListInfo abilities, List tasks, IGameEntity casterEntity, List targetEntities, SceneCastSkillCsReq req) + { + BattleInstance? instance = null; + List battleInfos = []; + foreach (var task in tasks) + { + try + { + var res = await TriggerTask(new AbilityLevelParam(abilities, task, casterEntity, targetEntities, req)); + 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.", ""); + + var method = GetType().GetMethod(methodName); + if (method != null) + { + var res = method.Invoke(this, [param]); + if (res is AbilityLevelResult result) + { + return result; + } + + if (res is ValueTask valueTask) + { + return await valueTask; + } + } + } + catch + { + // ignored + } + + return new AbilityLevelResult(); + } + + #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 = GetType().GetMethod(methodName); + if (method != null) + { + var resp = method.Invoke(this, [predicateTaskList.Predicate, param.CasterEntity, param.TargetEntities]); + if (resp is true) + { + 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; + } + } + } + } + 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 = GetType().GetMethod(methodName); + if (method != null) + { + var resp = method.Invoke(this, [adventureTriggerAttack.AttackTargetType, param.CasterEntity, param.TargetEntities]); + 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 = GetType().GetMethod(methodName); + if (method != null) + { + var resp = method.Invoke(this, + [addMazeBuff.TargetType, param.CasterEntity, param.TargetEntities]); + if (resp is List target) + { + foreach (var entity in target) + { + await entity.AddBuff(new SceneBuff(addMazeBuff.ID, 1, (param.CasterEntity as AvatarSceneInfo)?.AvatarInfo.GetAvatarId() ?? 0, + addMazeBuff.LifeTime.FixedValue.Value < -1 ? 20 : -1)); + + await AddAdventureModifier(param with + { + Act = new AddAdventureModifier + { + ModifierName = GameData.MazeBuffData[addMazeBuff.ID * 10 + 1].ModifierName + } + }); + } + } + } + } + + 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 CreateSummonUnit(AbilityLevelParam param) + { + if (param.Act is CreateSummonUnit createSummonUnit) + { + if (!GameData.SummonUnitDataData.TryGetValue(createSummonUnit.SummonUnitID, out var excel)) + 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.GetAvatarId() ?? 0, + LifeTimeMs = 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 CreateSummonUnit createSummonUnit) + { + await Player.SceneInstance!.ClearSummonUnit(); // TODO + } + + return new AbilityLevelResult(); + } + + public async ValueTask AddAdventureModifier(AbilityLevelParam param) + { + if (param.Act is AddAdventureModifier addAdventureModifier) + { + param.AdventureAbility.GlobalModifiers.TryGetValue(addAdventureModifier.ModifierName, out var modifier); + if (modifier == null) return new AbilityLevelResult(); + + foreach (var task in modifier.OnCreate) + { + await TriggerTask(param with { Act = task }); + } + } + + return new AbilityLevelResult(); + } + + public async ValueTask RemoveAdventureModifier(AbilityLevelParam param) + { + if (param.Act is RemoveAdventureModifier removeAdventureModifier) + { + param.AdventureAbility.GlobalModifiers.TryGetValue(removeAdventureModifier.ModifierName, out var modifier); + if (modifier == null) return new AbilityLevelResult(); + + foreach (var task in modifier.OnDestroy) + { + await TriggerTask(param with { Act = task }); + } + } + + return new AbilityLevelResult(); + } + + #endregion + + #region Selector + + public List TargetAlias(TargetEvaluator selector, IGameEntity casterEntity, List targetEntities) + { + if (selector is TargetAlias target) + { + if (target.Alias == "AllEnemy") + { + return targetEntities; + } + + if (target.Alias == "Caster") + { + return [casterEntity]; + } + + if (target.Alias == "AbilityTargetEntity") + { + return targetEntities; + } + + if (target.Alias == "ParamEntity") + { + return targetEntities; + } + } + + return []; + } + + #endregion +} + +public record AbilityLevelResult(BattleInstance? Instance = null, List? BattleInfos = null); + +public record AbilityLevelParam(AdventureAbilityConfigListInfo AdventureAbility, TaskConfigInfo Act, IGameEntity CasterEntity, List TargetEntities, SceneCastSkillCsReq Request); \ No newline at end of file diff --git a/GameServer/Game/Task/AvatarTask/AvatarTaskTrigger.cs b/GameServer/Game/Task/AvatarTask/AvatarTaskTrigger.cs deleted file mode 100644 index 78cb3432..00000000 --- a/GameServer/Game/Task/AvatarTask/AvatarTaskTrigger.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace EggLink.DanhengServer.GameServer.Game.Task.AvatarTask; - -public class AvatarTaskTrigger -{ -} \ No newline at end of file diff --git a/GameServer/Game/Task/AvatarTask/AvatarLevelTask.cs b/GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs similarity index 99% rename from GameServer/Game/Task/AvatarTask/AvatarLevelTask.cs rename to GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs index 01267f82..bc9659c1 100644 --- a/GameServer/Game/Task/AvatarTask/AvatarLevelTask.cs +++ b/GameServer/Game/Task/AvatarTask/SummonUnitLevelTask.cs @@ -6,7 +6,7 @@ using EggLink.DanhengServer.Proto; namespace EggLink.DanhengServer.GameServer.Game.Task.AvatarTask; -public class AvatarLevelTask +public class SummonUnitLevelTask { #region Task Condition diff --git a/GameServer/Game/Task/TaskManager.cs b/GameServer/Game/Task/TaskManager.cs index af85a040..7d11d271 100644 --- a/GameServer/Game/Task/TaskManager.cs +++ b/GameServer/Game/Task/TaskManager.cs @@ -7,7 +7,8 @@ public class TaskManager(PlayerInstance player) : BasePlayerManager(player) { public PerformanceTrigger PerformanceTrigger { get; } = new(player); public LevelTask LevelTask { get; } = new(player); - public AvatarLevelTask AvatarLevelTask { get; } = new(); + public SummonUnitLevelTask SummonUnitLevelTask { get; } = new(); + public AbilityLevelTask AbilityLevelTask { get; } = new(player); public MissionTaskTrigger MissionTaskTrigger { get; } = new(player); public SceneTaskTrigger SceneTaskTrigger { get; } = new(player); } \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerSceneCastSkillCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerSceneCastSkillCsReq.cs index 49a592c6..6455d189 100644 --- a/GameServer/Server/Packet/Recv/Scene/HandlerSceneCastSkillCsReq.cs +++ b/GameServer/Server/Packet/Recv/Scene/HandlerSceneCastSkillCsReq.cs @@ -1,7 +1,4 @@ -using EggLink.DanhengServer.Data; -using EggLink.DanhengServer.Data.Config; -using EggLink.DanhengServer.GameServer.Game.Battle.Skill; -using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene; using EggLink.DanhengServer.Kcp; using EggLink.DanhengServer.Proto; @@ -15,71 +12,8 @@ public class HandlerSceneCastSkillCsReq : Handler var req = SceneCastSkillCsReq.Parser.ParseFrom(data); var player = connection.Player!; - MazeSkill mazeSkill = new([], req); + var res = await player.SceneSkillManager!.OnCast(req); - // Get casting avatar - connection.Player!.SceneInstance!.AvatarInfo.TryGetValue((int)req.AttackedByEntityId, out var caster); - - if (caster != null) - { - if (req.MazeAbilityStr != "") - { - // overwrite - AbilityInfo? ability = null; - caster.AvatarInfo.Excel?.MazeAbility.TryGetValue(req.MazeAbilityStr, out ability); - if (ability != null) - { - mazeSkill = MazeSkillManager.GetSkill(caster.AvatarInfo.GetAvatarId(), ability, req); - mazeSkill.OnCast(caster, player); - } - } - else - { - // Check if normal attack or technique was used - if (req.SkillIndex > 0) - { - // Cast skill effects - var excel = caster.AvatarInfo.PathId > 0 - ? GameData.AvatarConfigData[caster.AvatarInfo.PathId] - : caster.AvatarInfo.Excel; - if (excel != null && excel.MazeSkill != null) - { - mazeSkill = MazeSkillManager.GetSkill(caster.AvatarInfo.GetAvatarId(), (int)req.SkillIndex, - req); - mazeSkill.OnCast(caster, player); - } - } - else - { - mazeSkill = MazeSkillManager.GetSkill(caster.AvatarInfo.GetAvatarId(), 0, req); - } - } - } - - if (req.AssistMonsterEntityIdList.Count > 0) - { - if (caster != null && caster.AvatarInfo.AvatarId == 1218 && req.SkillIndex == 1) - { - // Avoid Jiqoqiu's E skill - await connection.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, [])); - } - else - { - var hitTargetEntityIdList = new List(); - if (req.AssistMonsterEntityIdList.Count > 0) - foreach (var id in req.AssistMonsterEntityIdList) - hitTargetEntityIdList.Add(id); - else - foreach (var id in req.HitTargetEntityIdList) - hitTargetEntityIdList.Add(id); - // Start battle - await connection.Player!.BattleManager!.StartBattle(req, mazeSkill, [.. hitTargetEntityIdList]); - } - } - else - { - // We had no targets for some reason - await connection.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId, [])); - } + await connection.SendPacket(new PacketSceneCastSkillScRsp(res.RetCode, req.CastEntityId, res.Instance, res.TriggerBattleInfos ?? [])); } } \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Scene/PacketSceneCastSkillScRsp.cs b/GameServer/Server/Packet/Send/Scene/PacketSceneCastSkillScRsp.cs index 46b8b6f4..109de06a 100644 --- a/GameServer/Server/Packet/Send/Scene/PacketSceneCastSkillScRsp.cs +++ b/GameServer/Server/Packet/Send/Scene/PacketSceneCastSkillScRsp.cs @@ -32,4 +32,20 @@ public class PacketSceneCastSkillScRsp : BasePacket SetData(proto); } + + public PacketSceneCastSkillScRsp(Retcode retCode, uint castEntityId, BattleInstance? battle, List hitMonsters) : + base(CmdIds.SceneCastSkillScRsp) + { + var proto = new SceneCastSkillScRsp + { + Retcode = (uint)retCode, + CastEntityId = castEntityId + }; + + if (battle != null) proto.BattleInfo = battle.ToProto(); + + foreach (var hitMonster in hitMonsters) proto.MonsterBattleInfo.Add(hitMonster.ToProto()); + + SetData(proto); + } } \ No newline at end of file