refactor: maze ability

This commit is contained in:
Somebody
2025-04-19 23:03:32 +08:00
committed by EggLink
parent 1398278245
commit 1b8f745971
39 changed files with 879 additions and 502 deletions

View File

@@ -0,0 +1,39 @@
using EggLink.DanhengServer.Data.Config.Task;
using Newtonsoft.Json.Linq;
namespace EggLink.DanhengServer.Data.Config;
public class AdventureAbilityConfigInfo
{
public List<TaskConfigInfo> OnAbort { get; set; } = [];
public string Name { get; set; } = "";
public List<TaskConfigInfo> OnAdd { get; set; } = [];
public List<TaskConfigInfo> OnRemove { get; set; } = [];
public List<TaskConfigInfo> 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<string>() ?? "";
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;
}
}

View File

@@ -0,0 +1,25 @@
using EggLink.DanhengServer.Data.Config.AdventureAbility;
using Newtonsoft.Json.Linq;
namespace EggLink.DanhengServer.Data.Config;
public class AdventureAbilityConfigListInfo
{
public List<AdventureAbilityConfigInfo> AbilityList { get; set; } = [];
public Dictionary<string, AdventureModifierConfig> 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<Dictionary<string, JObject>>()!)
info.GlobalModifiers.Add(jObject.Key, AdventureModifierConfig.LoadFromJObject(jObject.Value));
return info;
}
}

View File

@@ -1,33 +0,0 @@
using EggLink.DanhengServer.Data.Excel;
namespace EggLink.DanhengServer.Data.Config;
public class SkillAbilityInfo
{
public List<AbilityInfo> 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<TaskInfo> OnStart { get; set; } = [];
public void Loaded()
{
foreach (var task in OnStart) task.Loaded();
}
}

View File

@@ -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<string>()!
};
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<string>()!;
return info;
}
}

View File

@@ -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<TaskConfigInfo> OnProjectileHit { get; set; } = [];
public List<TaskConfigInfo> 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<string>()!
};
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<bool>();
if (obj.TryGetValue(nameof(MutexName), out value)) info.MutexName = value.ToObject<string>()!;
return info;
}
}

View File

@@ -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<TaskConfigInfo> OnAttack { get; set; } = [];
public List<TaskConfigInfo> OnBattle { get; set; } = [];
public List<TaskConfigInfo> OnHit { get; set; } = [];
public List<TaskConfigInfo> 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<string>()!
};
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<bool>();
if (obj.TryGetValue(nameof(TriggerBattleDelay), out value))
info.TriggerBattleDelay = value.ToObject<float>();
if (obj.TryGetValue(nameof(IncludeProps), out value)) info.IncludeProps = value.ToObject<bool>();
if (obj.TryGetValue(nameof(HitTargetFaceToAttacker), out value))
info.HitTargetFaceToAttacker = value.ToObject<bool>();
if (obj.TryGetValue(nameof(TriggerBattleByAllHitTarget), out value))
info.TriggerBattleByAllHitTarget = value.ToObject<bool>();
if (obj.TryGetValue(nameof(AttackDetectCollision), out value))
info.AttackDetectCollision = value.ToObject<bool>();
return info;
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,6 @@
namespace EggLink.DanhengServer.Data.Config.Task;
public class DestroySummonUnit : TaskConfigInfo
{
public int SummonUnitID { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace EggLink.DanhengServer.Data.Config.Task;
public class CreateSummonUnit : TaskConfigInfo
{
public int SummonUnitID { get; set; }
}

View File

@@ -20,6 +20,7 @@ public class PropStateExecute : TaskConfigInfo
{
Type = obj[nameof(Type)]!.ToObject<string>()!
};
if (obj.ContainsKey(nameof(TargetType)))
{
var targetType = obj[nameof(TargetType)] as JObject;

View File

@@ -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

View File

@@ -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<string>()!
};
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<string>()!;
return info;
}
}

View File

@@ -0,0 +1,6 @@
namespace EggLink.DanhengServer.Data.Config.Task;
public class TargetAlias : TargetEvaluator
{
public string Alias { get; set; } = "";
}

View File

@@ -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)!;

View File

@@ -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<string, AbilityInfo> 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;
}
}

View File

@@ -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()

View File

@@ -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; }

View File

@@ -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<int, AvatarConfigExcel> AvatarConfigData { get; private set; } = [];
public static Dictionary<int, AdventureAbilityConfigListInfo> AdventureAbilityConfigListData { get; private set; } = [];
public static Dictionary<int, AvatarPromotionConfigExcel> AvatarPromotionConfigData { get; private set; } = [];
public static Dictionary<int, AvatarExpItemConfigExcel> AvatarExpItemConfigData { get; private set; } = [];
public static Dictionary<int, AvatarSkillTreeConfigExcel> AvatarSkillTreeConfigData { get; private set; } = [];

View File

@@ -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<SkillAbilityInfo>(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",

View File

@@ -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<Stage
await avatar.ApplyBuff(this);
}).Wait();
foreach (var buff in Buffs.Clone())
{
if (Enum.IsDefined(typeof(DamageTypeEnum), buff.BuffID))
{
Buffs.RemoveAll(x => 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++)

View File

@@ -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<uint> hitTargetEntityIdList)
public StageConfigExcel? NextBattleStageConfig { get; set; }
public async ValueTask<BattleInstance?> StartBattle(IGameEntity attackEntity, List<IGameEntity> targetEntityList, bool isSkill)
{
if (Player.BattleInstance != null) return;
if (Player.BattleInstance != null) return Player.BattleInstance;
var targetList = new List<EntityMonster>();
var avatarList = new List<AvatarSceneInfo>();
var propList = new List<EntityProp>();
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<HitMonsterInstance> 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<HitMonsterInstance> 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<HitMonsterInstance> 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 = [];
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)

View File

@@ -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<SkillResultData> OnCast(SceneCastSkillCsReq req)
{
// get entities
List<IGameEntity> targetEntities = []; // enemy
IGameEntity? attackEntity; // caster
List<int> 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<HitMonsterInstance>? TriggerBattleInfos = null);

View File

@@ -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<EntityMonster> 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<EntityMonster> entities)
{
foreach (var entity in entities)
await entity.AddBuff(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration));
}
}

View File

@@ -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<EntityMonster> 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<EntityMonster> entities)
{
foreach (var entity in entities)
await entity.RemoveBuff(BuffId);
}
}

View File

@@ -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<EntityMonster> 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<EntityMonster> entities)
{
await System.Threading.Tasks.Task.CompletedTask;
}
}

View File

@@ -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<EntityMonster> 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<EntityMonster> entities)
{
await System.Threading.Tasks.Task.CompletedTask;
}
}

View File

@@ -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<EntityMonster> entities)
{
await ValueTask.CompletedTask;
}
public async ValueTask OnAttack(AvatarSceneInfo avatar, List<EntityMonster> entities)
{
await ValueTask.CompletedTask;
}
}

View File

@@ -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<EntityMonster> entities);
public ValueTask OnAttack(AvatarSceneInfo avatar, List<EntityMonster> entities);
}

View File

@@ -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<TaskInfo> 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<IMazeSkillAction> 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<EntityMonster> entities)
{
foreach (var action in Actions) action.OnAttack(info, entities);
}
public void OnHitTarget(AvatarSceneInfo info, List<EntityMonster> entities)
{
foreach (var action in Actions) action.OnHitTarget(info, entities);
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -0,0 +1,8 @@
namespace EggLink.DanhengServer.GameServer.Game.Scene.Component;
public interface IGameModifier
{
public List<string> Modifiers { get; set; }
public ValueTask AddModifier(string modifierName);
public ValueTask RemoveModifier(string modifierName);
}

View File

@@ -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);
}

View File

@@ -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<AbilityLevelResult> TriggerTasks(AdventureAbilityConfigListInfo abilities, List<TaskConfigInfo> tasks, IGameEntity casterEntity, List<IGameEntity> targetEntities, SceneCastSkillCsReq req)
{
BattleInstance? instance = null;
List<HitMonsterInstance> 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<AbilityLevelResult> 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<AbilityLevelResult> valueTask)
{
return await valueTask;
}
}
}
catch
{
// ignored
}
return new AbilityLevelResult();
}
#endregion
#region Task
public async ValueTask<AbilityLevelResult> PredicateTaskList(AbilityLevelParam param)
{
BattleInstance? instance = null;
List<HitMonsterInstance> 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<AbilityLevelResult> AdventureTriggerAttack(AbilityLevelParam param)
{
BattleInstance? instance = null;
List<HitMonsterInstance> 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<IGameEntity> 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<AbilityLevelResult> AddMazeBuff(AbilityLevelParam param)
{
BattleInstance? instance = null;
List<HitMonsterInstance> 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<IGameEntity> 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<AbilityLevelResult> AdventureFireProjectile(AbilityLevelParam param)
{
BattleInstance? instance = null;
List<HitMonsterInstance> 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<AbilityLevelResult> 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<AbilityLevelResult> DestroySummonUnit(AbilityLevelParam param)
{
if (param.Act is CreateSummonUnit createSummonUnit)
{
await Player.SceneInstance!.ClearSummonUnit(); // TODO
}
return new AbilityLevelResult();
}
public async ValueTask<AbilityLevelResult> 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<AbilityLevelResult> 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<IGameEntity> TargetAlias(TargetEvaluator selector, IGameEntity casterEntity, List<IGameEntity> 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<HitMonsterInstance>? BattleInfos = null);
public record AbilityLevelParam(AdventureAbilityConfigListInfo AdventureAbility, TaskConfigInfo Act, IGameEntity CasterEntity, List<IGameEntity> TargetEntities, SceneCastSkillCsReq Request);

View File

@@ -1,5 +0,0 @@
namespace EggLink.DanhengServer.GameServer.Game.Task.AvatarTask;
public class AvatarTaskTrigger
{
}

View File

@@ -6,7 +6,7 @@ using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Game.Task.AvatarTask;
public class AvatarLevelTask
public class SummonUnitLevelTask
{
#region Task Condition

View File

@@ -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);
}

View File

@@ -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<uint>();
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 ?? []));
}
}

View File

@@ -32,4 +32,20 @@ public class PacketSceneCastSkillScRsp : BasePacket
SetData(proto);
}
public PacketSceneCastSkillScRsp(Retcode retCode, uint castEntityId, BattleInstance? battle, List<HitMonsterInstance> 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);
}
}