From 75d1337ce1106bebda0fb82dce0fc8a434b9ec9d Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:30:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=B7=A5=E5=8E=82=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E6=9E=84=E9=80=A0=E4=B8=BB=E5=8A=A8=E6=8A=80=E8=83=BD?= =?UTF-8?q?=E4=BA=86=EF=BC=9B=E5=AE=8C=E5=96=84=E6=8A=80=E8=83=BD=E5=BA=95?= =?UTF-8?q?=E5=B1=82=E9=80=89=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=9B=E5=9B=A2?= =?UTF-8?q?=E9=98=9F=E6=A8=A1=E5=BC=8F=E5=AE=8C=E5=96=84=EF=BC=9B=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=9B=9E=E5=90=88=E6=97=A5=E5=BF=97=20(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复诸多复活角色的问题;添加了更多回合记录 * 完善了底层技能选取目标方法;添加筛选条件列表,使技能能够灵活的选取角色 * 优化死亡结算的逻辑;优化回合记录日志 * 现在动态工厂可以构造主动技能了 --- Api/Utility/General.cs | 2 +- Entity/Character/Character.cs | 17 +- Entity/Skill/Effect.cs | 33 +- Entity/Skill/NormalAttack.cs | 15 +- Entity/Skill/OpenSkill.cs | 86 ++++- Entity/Skill/Skill.cs | 86 ++++- Entity/Statistics/CharacterStatistics.cs | 1 + Entity/System/RoundRecord.cs | 123 +++++- Entity/System/Season.cs | 2 +- Interface/Base/IGamingQueue.cs | 24 ++ .../Common/JsonConverter/SkillConverter.cs | 16 + Library/Constant/ConstantSet.cs | 48 +++ Model/ActionQueue.cs | 364 +++++++++++++----- Model/EquilibriumConstant.cs | 10 + 14 files changed, 682 insertions(+), 145 deletions(-) diff --git a/Api/Utility/General.cs b/Api/Utility/General.cs index f3d055e..a31505d 100644 --- a/Api/Utility/General.cs +++ b/Api/Utility/General.cs @@ -675,7 +675,7 @@ namespace Milimoe.FunGame.Core.Api.Utility /// 此方法检查一个 百分比(%) 数值是否存在于 [0,1] 区间 /// /// - /// 如果超过0,则返回0;超过1则返回1。 + /// 如果低于0,则返回0;超过1则返回1。 public static double PercentageCheck(double value) { return Math.Max(0, Math.Min(value, 1)); diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs index 1243518..0ff2089 100644 --- a/Entity/Character/Character.cs +++ b/Entity/Character/Character.cs @@ -1307,7 +1307,6 @@ namespace Milimoe.FunGame.Core.Entity PrimaryAttribute = PrimaryAttribute, Level = Level, EXP = EXP, - CharacterState = CharacterState, InitialHP = InitialHP, ExHP2 = ExHP2, InitialMP = InitialMP, @@ -1363,8 +1362,9 @@ namespace Milimoe.FunGame.Core.Entity /// /// 复活此角色,回复出厂状态 /// + /// 需要一个原始的角色用于还原状态 /// - public void Respawn() + public void Respawn(Character original) { Item? mcp = UnEquip(EquipSlotType.MagicCardPack); Item? w = UnEquip(EquipSlotType.Weapon); @@ -1374,11 +1374,17 @@ namespace Milimoe.FunGame.Core.Entity Item? ac2 = UnEquip(EquipSlotType.Accessory2); List skills = new(Skills); List items = new(Items); - Character c = Copy(); + Character c = original.Copy(); + List effects = [.. Effects]; + foreach (Effect e in effects) + { + e.OnEffectLost(this); + } Effects.Clear(); Skills.Clear(); Items.Clear(); Id = c.Id; + Guid = original.Guid; Name = c.Name; FirstName = c.FirstName; NickName = c.NickName; @@ -1392,6 +1398,10 @@ namespace Milimoe.FunGame.Core.Entity Level = c.Level; EXP = c.EXP; CharacterState = c.CharacterState; + CharacterEffectStates.Clear(); + CharacterEffectTypes.Clear(); + IsUnselectable = false; + UpdateCharacterState(); InitialHP = c.InitialHP; ExHP2 = c.ExHP2; InitialMP = c.InitialMP; @@ -1432,6 +1442,7 @@ namespace Milimoe.FunGame.Core.Entity Skill newskill = skill.Copy(); newskill.Character = this; newskill.Level = skill.Level; + newskill.CurrentCD = 0; Skills.Add(newskill); } foreach (Item item in items) diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs index dc48c7f..4d57522 100644 --- a/Entity/Skill/Effect.cs +++ b/Entity/Skill/Effect.cs @@ -22,21 +22,6 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual EffectType EffectType { get; set; } = EffectType.None; - /// - /// 作用于自身 - /// - public virtual bool TargetSelf { get; set; } = false; - - /// - /// 作用目标数量 - /// - public virtual int TargetCount { get; set; } = 0; - - /// - /// 作用范围 - /// - public virtual double TargetRange { get; set; } = 0; - /// /// 持续性的 /// 配合 使用,而不是 。 @@ -232,7 +217,8 @@ namespace Milimoe.FunGame.Core.Entity /// 技能开始吟唱时 [ 爆发技插队可触发此项 ] /// /// - public virtual void OnSkillCasting(Character caster) + /// + public virtual void OnSkillCasting(Character caster, List targets) { } @@ -436,6 +422,18 @@ namespace Milimoe.FunGame.Core.Entity return result; } + /// + /// 治疗一个目标 [ 强烈建议使用此方法而不是自行调用 ] + /// + /// + /// + /// + /// + public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false) + { + GamingQueue?.HealToTarget(actor, target, heal, canRespawn); + } + /// /// 打断施法 [ 尽可能的调用此方法而不是直接调用 ,以防止中断性变更 ] /// @@ -484,9 +482,6 @@ namespace Milimoe.FunGame.Core.Entity copy.Name = Name; copy.Description = Description; copy.EffectType = EffectType; - copy.TargetSelf = TargetSelf; - copy.TargetCount = TargetCount; - copy.TargetRange = TargetRange; copy.Durative = Durative; copy.Duration = Duration; copy.DurationTurn = DurationTurn; diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs index 4891522..de6b497 100644 --- a/Entity/Skill/NormalAttack.cs +++ b/Entity/Skill/NormalAttack.cs @@ -15,7 +15,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 普通攻击说明 /// - public string Description => $"对目标敌人造成 {(1.0 + 0.05 * (Level - 1)) * 100:0.##}% [ {Damage:0.##} ] 点{(IsMagic ? CharacterSet.GetMagicDamageName(MagicType) : "物理伤害")}。"; + public string Description => $"对目标敌人造成 {(1.0 + 0.05 * (Level - 1)) * 100:0.##}% 攻击力 [ {Damage:0.##} ] 点{(IsMagic ? CharacterSet.GetMagicDamageName(MagicType) : "物理伤害")}。"; /// /// 所属的角色 @@ -67,10 +67,13 @@ namespace Milimoe.FunGame.Core.Entity { foreach (Character enemy in enemys) { - queue.WriteLine("[ " + Character + $" ] 对 [ {enemy} ] 发起了普通攻击!"); - double expected = Damage; - DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage); - queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic, MagicType, result); + if (enemy.HP > 0) + { + queue.WriteLine("[ " + Character + $" ] 对 [ {enemy} ] 发起了普通攻击!"); + double expected = Damage; + DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage); + queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic, MagicType, result); + } } } @@ -95,7 +98,7 @@ namespace Milimoe.FunGame.Core.Entity StringBuilder builder = new(); builder.AppendLine(Name + " - 等级 " + Level); - builder.AppendLine("技能描述:" + Description); + builder.AppendLine("描述:" + Description); builder.AppendLine("硬直时间:" + HardnessTime); return builder.ToString(); diff --git a/Entity/Skill/OpenSkill.cs b/Entity/Skill/OpenSkill.cs index 83d90c3..8e82797 100644 --- a/Entity/Skill/OpenSkill.cs +++ b/Entity/Skill/OpenSkill.cs @@ -18,8 +18,92 @@ namespace Milimoe.FunGame.Core.Entity foreach (string str in args.Keys) { Values[str] = args[str]; - switch (str) + switch (str.ToLower()) { + case "active": + if (bool.TryParse(args[str].ToString(), out bool isActive) && isActive) + { + SkillType = SkillType.Item; + } + break; + case "debuff": + if (bool.TryParse(args[str].ToString(), out bool isDebuff) && isDebuff) + { + IsDebuff = isDebuff; + } + break; + case "self": + if (bool.TryParse(args[str].ToString(), out bool self)) + { + CanSelectSelf = self; + } + break; + case "enemy": + if (bool.TryParse(args[str].ToString(), out bool enemy)) + { + CanSelectEnemy = enemy; + } + break; + case "teammate": + if (bool.TryParse(args[str].ToString(), out bool teammate)) + { + CanSelectTeammate = teammate; + } + break; + case "count": + if (int.TryParse(args[str].ToString(), out int count) && count > 0) + { + CanSelectTargetCount = count; + } + break; + case "range": + if (int.TryParse(args[str].ToString(), out int range) && range > 0) + { + CanSelectTargetRange = range; + } + break; + case "mpcost": + if (double.TryParse(args[str].ToString(), out double mpcost) && mpcost > 0) + { + MPCost = mpcost; + } + break; + case "epcost": + if (double.TryParse(args[str].ToString(), out double epcost) && epcost > 0) + { + EPCost = epcost; + } + break; + case "costall": + if (bool.TryParse(args[str].ToString(), out bool costall) && costall) + { + CostAllEP = costall; + } + break; + case "mincost": + if (double.TryParse(args[str].ToString(), out double mincost) && mincost > 0) + { + MinCostEP = mincost; + } + break; + case "cd": + if (double.TryParse(args[str].ToString(), out double cd) && cd > 0) + { + CD = cd; + } + break; + case "cast": + if (double.TryParse(args[str].ToString(), out double cast) && cast > 0) + { + CastTime = cast; + } + break; + case "ht": + if (double.TryParse(args[str].ToString(), out double ht) && ht > 0) + { + HardnessTime = ht; + } + break; default: break; } diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs index 86a48d6..468e1ab 100644 --- a/Entity/Skill/Skill.cs +++ b/Entity/Skill/Skill.cs @@ -82,11 +82,26 @@ namespace Milimoe.FunGame.Core.Entity [InitRequired] public bool IsMagic => SkillType == SkillType.Magic; + /// + /// 是否属于 Debuff + /// + public bool IsDebuff { get; set; } = false; + /// /// 可选取自身 /// public virtual bool CanSelectSelf { get; set; } = false; + /// + /// 可选取敌对角色 + /// + public virtual bool CanSelectEnemy { get; set; } = true; + + /// + /// 可选取友方角色 + /// + public virtual bool CanSelectTeammate { get; set; } = false; + /// /// 可选取的作用目标数量 /// @@ -97,10 +112,15 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual double CanSelectTargetRange { get; set; } = 0; + /// + /// 选取角色的条件 + /// + public List> SelectTargetPredicates { get; } = []; + /// /// 实际魔法消耗 [ 魔法 ] /// - public double RealMPCost => Math.Max(0, MPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * 0.00125))); + public double RealMPCost => Math.Max(0, MPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * General.GameplayEquilibriumConstant.INTtoCastMPReduce))); /// /// 魔法消耗 [ 魔法 ] @@ -122,7 +142,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 实际能量消耗 [ 战技 ] /// - public double RealEPCost => CostAllEP ? Math.Max(MinCostEP, Character?.EP ?? MinCostEP) : (IsSuperSkill ? EPCost : Math.Max(0, EPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * 0.00075)))); + public double RealEPCost => CostAllEP ? Math.Max(MinCostEP, Character?.EP ?? MinCostEP) : (IsSuperSkill ? EPCost : Math.Max(0, EPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * General.GameplayEquilibriumConstant.INTtoCastEPReduce)))); /// /// 能量消耗 [ 战技 ] @@ -140,10 +160,20 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual double MinCostEP { get; set; } = 100; + /// + /// 上一次释放此技能消耗的魔法 [ 魔法 ] + /// + public double LastCostMP { get; set; } = 0; + + /// + /// 上一次释放此技能消耗的能量 [ 战技 ] + /// + public double LastCostEP { get; set; } = 0; + /// /// 实际冷却时间 /// - public double RealCD => Math.Max(0, CD * (1 - Character?.CDR ?? 0)); + public double RealCD => Math.Max(0, CD * (1 - (Character?.CDR ?? 0))); /// /// 冷却时间 @@ -247,38 +277,64 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual List SelectTargets(Character caster, List enemys, List teammates) { + List tobeSelected = []; + if (CanSelectSelf) { - return [caster]; + tobeSelected.Add(caster); + } + + if (CanSelectEnemy) + { + tobeSelected.AddRange(enemys); + } + if (CanSelectTeammate) + { + tobeSelected.AddRange(teammates); + } + + // 筛选出符合条件的角色 + tobeSelected = [.. tobeSelected.Where(c => SelectTargetPredicates.All(f => f(c)))]; + + List targets = []; + + if (tobeSelected.Count <= CanSelectTargetCount) + { + targets.AddRange(tobeSelected); } else { - List targets = []; - - if (enemys.Count <= CanSelectTargetCount) - { - return [.. enemys]; - } - - return enemys.OrderBy(x => Random.Shared.Next()).Take(CanSelectTargetCount).ToList(); + targets.AddRange(tobeSelected.OrderBy(x => Random.Shared.Next()).Take(CanSelectTargetCount)); } + + return [.. targets.Distinct()]; } - + /// /// 技能开始吟唱时 [ 吟唱魔法、释放战技和爆发技、预释放爆发技均可触发 ] /// /// /// - public void OnSkillCasting(IGamingQueue queue, Character caster) + /// + public void OnSkillCasting(IGamingQueue queue, Character caster, List targets) { GamingQueue = queue; foreach (Effect e in Effects) { e.GamingQueue = GamingQueue; - e.OnSkillCasting(caster); + e.OnSkillCasting(caster, targets); } } + /// + /// 技能效果触发前 + /// + public void BeforeSkillCasted() + { + LastCostMP = RealMPCost; + LastCostEP = RealEPCost; + } + /// /// 触发技能效果 /// diff --git a/Entity/Statistics/CharacterStatistics.cs b/Entity/Statistics/CharacterStatistics.cs index eb0a110..52b3628 100644 --- a/Entity/Statistics/CharacterStatistics.cs +++ b/Entity/Statistics/CharacterStatistics.cs @@ -43,5 +43,6 @@ public int LastRank { get; set; } = 0; public double AvgRank { get; set; } = 0; public double Rating { get; set; } = 0; + public int MVPs { get; set; } = 0; } } diff --git a/Entity/System/RoundRecord.cs b/Entity/System/RoundRecord.cs index 6843c71..7cb08b0 100644 --- a/Entity/System/RoundRecord.cs +++ b/Entity/System/RoundRecord.cs @@ -1,14 +1,129 @@ -using Milimoe.FunGame.Core.Library.Constant; +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Constant; -namespace Milimoe.FunGame.Core.Entity.System +namespace Milimoe.FunGame.Core.Entity { - public class RoundRecord(int round, Character actor) + public class RoundRecord(int round) { public int Round { get; set; } = round; - public Character Actor { get; set; } = actor; + public Character Actor { get; set; } = Factory.GetCharacter(); public CharacterActionType ActionType { get; set; } = CharacterActionType.None; public List Targets { get; set; } = []; public Skill? Skill { get; set; } = null; + public string SkillCost { get; set; } = ""; public Item? Item { get; set; } = null; + public bool HasKill { get; set; } = false; + public Dictionary Damages { get; set; } = []; + public Dictionary IsCritical { get; set; } = []; + public Dictionary Heals { get; set; } = []; + public Dictionary Effects { get; set; } = []; + public List ActorContinuousKilling { get; set; } = []; + public List DeathContinuousKilling { get; set; } = []; + public double CastTime { get; set; } = 0; + public double HardnessTime { get; set; } = 0; + public Dictionary RespawnCountdowns { get; set; } = []; + public List Respawns { get; set; } = []; + public List RoundRewards { get; set; } = []; + + public override string ToString() + { + StringBuilder builder = new(); + + builder.AppendLine($"=== Round {Round} ==="); + if (RoundRewards.Count > 0) + { + builder.AppendLine($"[ {Actor} ] 回合奖励 -> {string.Join(" / ", RoundRewards.Select(s => s.Description)).Trim()}"); + } + if (ActionType == CharacterActionType.NormalAttack || ActionType == CharacterActionType.CastSkill || ActionType == CharacterActionType.CastSuperSkill) + { + if (ActionType == CharacterActionType.NormalAttack) + { + builder.Append($"[ {Actor} ] {Actor.NormalAttack.Name} -> "); + } + else if (ActionType == CharacterActionType.CastSkill || ActionType == CharacterActionType.CastSuperSkill) + { + if (Skill != null) + { + builder.Append($"[ {Actor} ] {Skill.Name}({SkillCost})-> "); + } + else + { + builder.Append($"释放魔法 -> "); + } + } + builder.AppendLine(string.Join(" / ", GetTargetsState())); + if (DeathContinuousKilling.Count > 0) builder.AppendLine($"{string.Join("\r\n", DeathContinuousKilling)}"); + if (ActorContinuousKilling.Count > 0) builder.AppendLine($"{string.Join("\r\n", ActorContinuousKilling)}"); + } + + if (ActionType == CharacterActionType.PreCastSkill && Skill != null) + { + if (Skill.IsMagic) + { + builder.AppendLine($"[ {Actor} ] 吟唱 [ {Skill.Name} ],持续时间:{CastTime:0.##}"); + } + else + { + builder.AppendLine($"[ {Actor} ]:{Skill.Name}({SkillCost})-> "); + builder.AppendLine(string.Join(" / ", GetTargetsState())); + builder.AppendLine($"[ {Actor} ] 回合结束,硬直时间:{HardnessTime:0.##}"); + } + } + else + { + builder.AppendLine($"[ {Actor} ] 回合结束,硬直时间:{HardnessTime:0.##}"); + } + + foreach (Character character in RespawnCountdowns.Keys) + { + builder.AppendLine($"[ {character} ] 进入复活倒计时:{RespawnCountdowns[character]:0.##}"); + } + + foreach (Character character in Respawns) + { + builder.AppendLine($"[ {character} ] 复活了"); + } + + return builder.ToString(); + } + + private List GetTargetsState() + { + List strings = []; + foreach (Character target in Targets.Distinct()) + { + string hasDamage = ""; + string hasHeal = ""; + string hasEffect = ""; + if (Damages.TryGetValue(target, out double damage)) + { + hasDamage = $"伤害:{damage:0.##}"; + if (IsCritical.TryGetValue(target, out bool isCritical) && isCritical) + { + hasDamage = "暴击," + hasDamage; + } + } + if (Heals.TryGetValue(target, out double heals)) + { + hasHeal = $"治疗:{heals:0.##}"; + } + if (Effects.TryGetValue(target, out EffectType effectType)) + { + hasEffect = $"施加:{SkillSet.GetEffectTypeName(effectType)}"; + } + if (ActionType == CharacterActionType.NormalAttack && hasDamage == "") + { + hasDamage = "完美闪避"; + } + if ((ActionType == CharacterActionType.PreCastSkill || ActionType == CharacterActionType.PreCastSkill || ActionType == CharacterActionType.CastSkill) && hasDamage == "" && target != Actor) + { + hasDamage = "免疫"; + } + string[] strs = [hasDamage, hasHeal, hasEffect]; + strings.Add($"[ {target}({string.Join(" / ", strs.Where(s => s != ""))})])"); + } + return strings; + } } } diff --git a/Entity/System/Season.cs b/Entity/System/Season.cs index bfeda9f..cf878a4 100644 --- a/Entity/System/Season.cs +++ b/Entity/System/Season.cs @@ -1,4 +1,4 @@ -namespace Milimoe.FunGame.Core.Entity.System +namespace Milimoe.FunGame.Core.Entity { public class Season(long id, string name, string description) { diff --git a/Interface/Base/IGamingQueue.cs b/Interface/Base/IGamingQueue.cs index c7c9e5d..7443523 100644 --- a/Interface/Base/IGamingQueue.cs +++ b/Interface/Base/IGamingQueue.cs @@ -13,11 +13,26 @@ namespace Milimoe.FunGame.Core.Interface.Base /// public Action WriteLine { get; } + /// + /// 原始的角色字典 + /// + public Dictionary Original { get; } + /// /// 当前的行动顺序 /// public List Queue { get; } + /// + /// 上回合记录 + /// + public RoundRecord LastRound { get; set; } + + /// + /// 所有回合的记录 + /// + public List Rounds { get; } + /// /// 当前已死亡的角色顺序(第一个是最早死的) /// @@ -63,6 +78,15 @@ namespace Milimoe.FunGame.Core.Interface.Base /// public void DamageToEnemy(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal); + /// + /// 治疗一个目标 + /// + /// + /// + /// + /// + public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false); + /// /// 计算物理伤害 /// diff --git a/Library/Common/JsonConverter/SkillConverter.cs b/Library/Common/JsonConverter/SkillConverter.cs index f7dcc44..73fb483 100644 --- a/Library/Common/JsonConverter/SkillConverter.cs +++ b/Library/Common/JsonConverter/SkillConverter.cs @@ -41,6 +41,12 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Skill.CanSelectSelf): result.CanSelectSelf = reader.GetBoolean(); break; + case nameof(Skill.CanSelectEnemy): + result.CanSelectEnemy = reader.GetBoolean(); + break; + case nameof(Skill.CanSelectTeammate): + result.CanSelectTeammate = reader.GetBoolean(); + break; case nameof(Skill.CanSelectTargetCount): result.CanSelectTargetCount = reader.GetInt32(); break; @@ -59,6 +65,12 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Skill.EPCost): result.EPCost = reader.GetDouble(); break; + case nameof(Skill.CostAllEP): + result.CostAllEP = reader.GetBoolean(); + break; + case nameof(Skill.MinCostEP): + result.MinCostEP = reader.GetDouble(); + break; case nameof(Skill.CastTime): result.CastTime = reader.GetDouble(); break; @@ -100,12 +112,16 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter if (value.Level > 0) writer.WriteNumber(nameof(Skill.Level), value.Level); writer.WriteNumber(nameof(Skill.SkillType), (int)value.SkillType); if (value.CanSelectSelf) writer.WriteBoolean(nameof(Skill.CanSelectSelf), value.CanSelectSelf); + if (!value.CanSelectEnemy) writer.WriteBoolean(nameof(Skill.CanSelectEnemy), value.CanSelectEnemy); + if (value.CanSelectTeammate) writer.WriteBoolean(nameof(Skill.CanSelectTeammate), value.CanSelectTeammate); if (value.CanSelectTargetCount != 0) writer.WriteNumber(nameof(Skill.CanSelectTargetCount), value.CanSelectTargetCount); if (value.CanSelectTargetRange != 0) writer.WriteNumber(nameof(Skill.CanSelectTargetRange), value.CanSelectTargetRange); if (!value.Enable) writer.WriteBoolean(nameof(Skill.Enable), value.Enable); if (value.IsInEffect) writer.WriteBoolean(nameof(Skill.IsInEffect), value.IsInEffect); if (value.MPCost > 0) writer.WriteNumber(nameof(Skill.MPCost), value.MPCost); if (value.EPCost > 0) writer.WriteNumber(nameof(Skill.EPCost), value.EPCost); + if (value.CostAllEP) writer.WriteBoolean(nameof(Skill.CostAllEP), value.CostAllEP); + if (value.MinCostEP > 0) writer.WriteNumber(nameof(Skill.MinCostEP), value.MinCostEP); if (value.CastTime > 0) writer.WriteNumber(nameof(Skill.CastTime), value.CastTime); if (value.CD > 0) writer.WriteNumber(nameof(Skill.CD), value.CD); if (value.CurrentCD > 0) writer.WriteNumber(nameof(Skill.CurrentCD), value.CurrentCD); diff --git a/Library/Constant/ConstantSet.cs b/Library/Constant/ConstantSet.cs index 9cb6f9b..aef6bef 100644 --- a/Library/Constant/ConstantSet.cs +++ b/Library/Constant/ConstantSet.cs @@ -521,5 +521,53 @@ namespace Milimoe.FunGame.Core.Library.Constant _ => General.GameplayEquilibriumConstant.MaxPassiveSkillLevel }; } + + public static string GetEffectTypeName(EffectType type) + { + return type switch + { + EffectType.None => "无特殊效果", + EffectType.Item => "装备特效", + EffectType.Mark => "标记", + EffectType.Stun => "眩晕", + EffectType.Freeze => "冰冻", + EffectType.Silence => "沉默", + EffectType.Root => "定身", + EffectType.Fear => "恐惧", + EffectType.Sleep => "睡眠", + EffectType.Knockback => "击退", + EffectType.Knockdown => "击倒", + EffectType.Taunt => "嘲讽", + EffectType.Slow => "减速", + EffectType.Weaken => "衰弱", + EffectType.Poison => "中毒", + EffectType.Burn => "燃烧", + EffectType.Bleed => "流血", + EffectType.Blind => "致盲", + EffectType.Cripple => "致残", + EffectType.Shield => "护盾", + EffectType.HealOverTime => "持续治疗", + EffectType.Haste => "加速", + EffectType.Invulnerable => "无敌", + EffectType.Unselectable => "不可选中", + EffectType.DamageBoost => "伤害提升", + EffectType.DefenseBoost => "防御提升", + EffectType.CritBoost => "暴击提升", + EffectType.ManaRegen => "魔法恢复", + EffectType.ArmorBreak => "破甲", + EffectType.MagicResistBreak => "降低魔抗", + EffectType.Curse => "诅咒", + EffectType.Exhaustion => "疲劳", + EffectType.ManaBurn => "魔力燃烧", + EffectType.Charm => "魅惑", + EffectType.Disarm => "缴械", + EffectType.Confusion => "混乱", + EffectType.Petrify => "石化", + EffectType.SilenceMagic => "法术沉默", + EffectType.Banish => "放逐", + EffectType.Doom => "毁灭", + _ => "未知效果" + }; + } } } diff --git a/Model/ActionQueue.cs b/Model/ActionQueue.cs index 2e26e7b..261b306 100644 --- a/Model/ActionQueue.cs +++ b/Model/ActionQueue.cs @@ -1,6 +1,5 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Entity.System; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; @@ -16,6 +15,11 @@ namespace Milimoe.FunGame.Core.Model /// public Action WriteLine { get; } + /// + /// 原始的角色字典 + /// + public Dictionary Original => _original; + /// /// 当前的行动顺序 /// @@ -82,8 +86,18 @@ namespace Milimoe.FunGame.Core.Model /// /// 上回合记录 /// - public RoundRecord LastRound { get; set; } = new(0, Factory.GetCharacter()); + public RoundRecord LastRound { get; set; } = new(0); + + /// + /// 所有回合的记录 + /// + public List Rounds { get; } = []; + /// + /// 原始的角色字典 + /// + protected readonly Dictionary _original = []; + /// /// 当前的行动顺序 /// @@ -119,6 +133,11 @@ namespace Milimoe.FunGame.Core.Model /// protected readonly Dictionary _earnedMoney = []; + /// + /// 角色最高连杀数 + /// + protected readonly Dictionary _maxContinuousKilling = []; + /// /// 角色目前的连杀数 /// @@ -154,6 +173,11 @@ namespace Milimoe.FunGame.Core.Model /// protected readonly Dictionary _respawnCountdown = []; + /// + /// 当前回合死亡角色 + /// + protected readonly List _roundDeaths = []; + /// /// 是否是团队模式 /// @@ -202,6 +226,15 @@ namespace Milimoe.FunGame.Core.Model /// public void InitCharacterQueue(List characters) { + // 保存原始的角色信息。用于复活时还原状态 + foreach (Character character in characters) + { + Character original = character.Copy(); + original.Guid = Guid.NewGuid(); + character.Guid = original.Guid; + _original.Add(original.Guid, original); + } + // 初始排序:按速度排序 List> groupedBySpeed = [.. characters .GroupBy(c => c.SPD) @@ -291,6 +324,7 @@ namespace Milimoe.FunGame.Core.Model public void ClearQueue() { FirstKiller = null; + _original.Clear(); _queue.Clear(); _hardnessTimes.Clear(); _assistDamage.Clear(); @@ -298,6 +332,7 @@ namespace Milimoe.FunGame.Core.Model _cutCount.Clear(); _castingSkills.Clear(); _castingSuperSkills.Clear(); + _maxContinuousKilling.Clear(); _continuousKilling.Clear(); _earnedMoney.Clear(); _eliminated.Clear(); @@ -491,8 +526,8 @@ namespace Milimoe.FunGame.Core.Model /// 是否结束游戏 public bool ProcessTurn(Character character) { - TotalRound++; - LastRound = new(TotalRound, character); + LastRound.Actor = character; + _roundDeaths.Clear(); if (!BeforeTurn(character)) { @@ -696,7 +731,7 @@ namespace Milimoe.FunGame.Core.Model character.CharacterState = CharacterState.Casting; _castingSkills.Add(character, new(skill, targets)); baseTime = skill.CastTime; - skill.OnSkillCasting(this, character); + skill.OnSkillCasting(this, character, targets); } } else @@ -708,12 +743,14 @@ namespace Milimoe.FunGame.Core.Model if (!cancel) { decided = true; - skill.OnSkillCasting(this, character); + skill.OnSkillCasting(this, character, targets); + skill.BeforeSkillCasted(); character.EP -= cost; baseTime = skill.HardnessTime; skill.CurrentCD = skill.RealCD; skill.Enable = false; + LastRound.SkillCost = $"{-cost:0.##} EP"; WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); skill.OnSkillCasted(this, character, targets); @@ -742,10 +779,13 @@ namespace Milimoe.FunGame.Core.Model // 判断是否能够释放技能 if (CheckCanCast(character, skill, out double cost)) { + skill.BeforeSkillCasted(); + character.MP -= cost; baseTime = skill.HardnessTime; skill.CurrentCD = skill.RealCD; skill.Enable = false; + LastRound.SkillCost = $"{-cost:0.##} MP"; WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点魔法值,释放了魔法 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); skill.OnSkillCasted(this, character, targets); @@ -779,10 +819,13 @@ namespace Milimoe.FunGame.Core.Model List targets = SelectTargets(character, skill, enemys, teammates, out _); LastRound.Targets = [.. targets]; + skill.BeforeSkillCasted(); + character.EP -= cost; baseTime = skill.HardnessTime; skill.CurrentCD = skill.RealCD; skill.Enable = false; + LastRound.SkillCost = $"{-cost:0.##} EP"; WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量值,释放了爆发技 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); skill.OnSkillCasted(this, character, targets); @@ -811,13 +854,16 @@ namespace Milimoe.FunGame.Core.Model WriteLine("[ " + character + $" ] 放弃了行动!"); } + LastRound.ActionType = type; + + // 统一在回合结束时处理角色的死亡 + ProcessCharacterDeath(character); + if (_isGameEnd) { return _isGameEnd; } - LastRound.ActionType = type; - // 减少硬直时间 double newHardnessTime = baseTime; if (character.CharacterState != CharacterState.Casting) @@ -829,8 +875,10 @@ namespace Milimoe.FunGame.Core.Model { newHardnessTime = Math.Max(0, Calculation.Round2Digits(baseTime * (1 - character.AccelerationCoefficient))); WriteLine("[ " + character + " ] 进行吟唱,持续时间: " + newHardnessTime); + LastRound.CastTime = newHardnessTime; } AddCharacter(character, newHardnessTime, isCheckProtected); + LastRound.HardnessTime = newHardnessTime; // 有人想要插队吗? WillPreCastSuperSkill(character); @@ -885,10 +933,38 @@ namespace Milimoe.FunGame.Core.Model // 获取第一个角色的硬直时间 double timeToReduce = _hardnessTimes[_queue[0]]; - TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce); + // 如果复活时间更快,应该先流逝复活时间 + if (_respawnCountdown.Count != 0) + { + double timeToRespawn = _respawnCountdown.Values.Min(); + if (timeToRespawn < timeToReduce) + { + timeToReduce = Calculation.Round2Digits(timeToRespawn); + } + } + TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce); WriteLine("时间流逝:" + timeToReduce); + // 减少复活倒计时 + foreach (Character character in _respawnCountdown.Keys) + { + _respawnCountdown[character] = Calculation.Round2Digits(_respawnCountdown[character] - timeToReduce); + if (_respawnCountdown[character] <= 0) + { + double hardnessTime = 5; + character.Respawn(_original[character.Guid]); + WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} 硬直时间。"); + AddCharacter(character, hardnessTime, false); + LastRound.Respawns.Add(character); + _respawnCountdown.Remove(character); + if (!_respawnTimes.TryAdd(character, 1)) + { + _respawnTimes[character] += 1; + } + } + } + foreach (Character character in _queue) { // 减少所有角色的硬直时间 @@ -970,25 +1046,13 @@ namespace Milimoe.FunGame.Core.Model } } - // 减少复活倒计时 - foreach (Character character in _respawnCountdown.Keys) - { - _respawnCountdown[character] -= timeToReduce; - if (_respawnCountdown[character] <= 0) - { - double hardnessTime = 5; - character.Respawn(); - WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} 硬直时间。"); - AddCharacter(character, hardnessTime, false); - _respawnCountdown.Remove(character); - if (!_respawnTimes.TryAdd(character, 1)) - { - _respawnTimes[character] += 1; - } - } - } - WriteLine("\r\n"); + + // 在时间流逝后,进入下一回合 + TotalRound++; + LastRound = new(TotalRound); + Rounds.Add(LastRound); + return timeToReduce; } @@ -1010,6 +1074,10 @@ namespace Milimoe.FunGame.Core.Model return; } + if (!LastRound.IsCritical.TryAdd(enemy, damageResult == DamageResult.Critical) && damageResult == DamageResult.Critical) + { + LastRound.IsCritical[enemy] = true; + } bool isEvaded = damageResult == DamageResult.Evaded; List effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); foreach (Effect effect in effects) @@ -1062,72 +1130,55 @@ namespace Milimoe.FunGame.Core.Model effect.AfterDamageCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult); } - if (enemy.HP <= 0 && !_eliminated.Contains(enemy)) + if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy)) { + LastRound.HasKill = true; + _roundDeaths.Add(enemy); DeathCalculation(actor, enemy); - // 给所有角色的特效广播角色死亡结算 - effects = _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList(); - foreach (Effect effect in effects) - { - effect.AfterDeathCalculation(enemy, actor, _continuousKilling, _earnedMoney); - } - // 将死者移出队列 - _queue.Remove(enemy); - if (_isTeamMode) - { - Team? killTeam = GetTeam(actor); - Team? deathTeam = GetTeam(enemy); - if (deathTeam != null) - { - if (deathTeam.GetActiveCharacters(this).Count == 0) - { - // 团灭了 - _eliminatedTeams.Add(deathTeam); - _teams.Remove(deathTeam.Name); - } - else - { - List remain = deathTeam.GetActiveTeammates(this, enemy); - int remainCount = remain.Count; - if (remainCount > 0) WriteLine($"[ {deathTeam} ] 剩余成员:[ {string.Join(" ] / [ ", remain)} ]({remainCount} 人)"); - } - } + } + } - if (killTeam != null) - { - if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin) - { - List combinedTeams = [.. _eliminatedTeams, .. _teams.Values]; - combinedTeams.Remove(killTeam); - _eliminatedTeams.Clear(); - _eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score)); - EndGameInfo(killTeam); - return; - } - if (!_teams.Keys.Where(str => str != killTeam.Name).Any()) - { - // 没有其他的团队了,游戏结束 - EndGameInfo(killTeam); - return; - } - else if (killTeam != null) - { - List actives = killTeam.GetActiveCharacters(this); - actives.Add(actor); - int remainCount = actives.Count; - if (remainCount > 0) WriteLine($"[ {killTeam} ] 剩余成员:[ {string.Join(" ] / [ ", actives)} ]({remainCount} 人)"); - } - } + /// + /// 治疗一个目标 + /// + /// + /// + /// + /// + public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false) + { + if (target.HP == target.MaxHP) + { + return; + } + + bool isDead = target.HP <= 0; + + if (heal < 0) heal = 0; + if (target.HP > 0 || (isDead && canRespawn)) + { + target.HP += heal; + if (!LastRound.Heals.TryAdd(target, heal)) + { + LastRound.Heals[target] += heal; + } + } + + if (isDead && canRespawn) + { + if (target != actor) + { + WriteLine($"[ {target} ] 被 [ {actor} ] 复苏了,并回复了 {heal:0.##} 点生命值!!"); } else { - if (!_queue.Where(c => c != actor).Any()) - { - // 没有其他的角色了,游戏结束 - EndGameInfo(actor); - } + WriteLine($"[ {target} ] 复苏了,并回复了 {heal:0.##} 点生命值!!"); } } + else + { + WriteLine($"[ {target} ] 回复了 {heal:0.##} 点生命值!"); + } } /// @@ -1211,10 +1262,10 @@ namespace Milimoe.FunGame.Core.Model double penetratedDEF = (1 - actor.PhysicalPenetration) * enemy.DEF; // 物理伤害减免 - double physicalDamageReduction = penetratedDEF / (penetratedDEF + 120); + double physicalDamageReduction = penetratedDEF / (penetratedDEF + General.GameplayEquilibriumConstant.DEFReductionFactor); // 最终的物理伤害 - finalDamage = expectedDamage * (1 - physicalDamageReduction); + finalDamage = expectedDamage * (1 - Calculation.PercentageCheck(physicalDamageReduction + enemy.ExPDR)); // 暴击判定 effects = actor.Effects.Where(e => e.Level > 0).ToList(); @@ -1355,6 +1406,79 @@ namespace Milimoe.FunGame.Core.Model return DamageResult.Normal; } + public void ProcessCharacterDeath(Character character) + { + foreach (Character enemy in _roundDeaths) + { + // 给所有角色的特效广播角色死亡结算 + List effects = _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList(); + foreach (Effect effect in effects) + { + effect.AfterDeathCalculation(enemy, character, _continuousKilling, _earnedMoney); + } + // 将死者移出队列 + _queue.Remove(enemy); + if (_isTeamMode) + { + Team? killTeam = GetTeam(character); + Team? deathTeam = GetTeam(enemy); + + if (MaxRespawnTimes != 0) + { + WriteLine($"\r\n=== 当前死亡竞赛比分 ===\r\n{string.Join("\r\n", Teams.OrderByDescending(kv => kv.Value.Score).Select(kv => kv.Key + ":" + kv.Value.Score + "(剩余存活人数:" + kv.Value.GetActiveCharacters(this).Count + ")"))}"); + } + + if (deathTeam != null) + { + List remain = deathTeam.GetActiveCharacters(this); + int remainCount = remain.Count; + if (remainCount == 0) + { + // 团灭了 + _eliminatedTeams.Add(deathTeam); + _teams.Remove(deathTeam.Name); + } + else if (MaxRespawnTimes == 0) + { + WriteLine($"[ {deathTeam} ] 剩余成员:[ {string.Join(" ] / [ ", remain)} ]({remainCount} 人)"); + } + } + + if (killTeam != null) + { + List actives = killTeam.GetActiveCharacters(this); + actives.Add(character); + int remainCount = actives.Count; + if (remainCount > 0 && MaxRespawnTimes == 0) + { + WriteLine($"[ {killTeam} ] 剩余成员:[ {string.Join(" ] / [ ", actives)} ]({remainCount} 人)"); + } + if (!_teams.Keys.Where(str => str != killTeam.Name).Any()) + { + // 没有其他的团队了,游戏结束 + EndGameInfo(killTeam); + } + if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin) + { + List combinedTeams = [.. _eliminatedTeams, .. _teams.Values]; + combinedTeams.Remove(killTeam); + _eliminatedTeams.Clear(); + _eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score)); + EndGameInfo(killTeam); + } + } + } + else + { + if (!_queue.Where(c => c != character).Any()) + { + // 没有其他的角色了,游戏结束 + EndGameInfo(character); + } + } + } + } + /// /// 死亡结算 /// @@ -1363,6 +1487,10 @@ namespace Milimoe.FunGame.Core.Model public void DeathCalculation(Character killer, Character death) { if (!_continuousKilling.TryAdd(killer, 1)) _continuousKilling[killer] += 1; + if (!_maxContinuousKilling.TryAdd(killer, 1) && _continuousKilling[killer] > _maxContinuousKilling[killer]) + { + _maxContinuousKilling[killer] = _continuousKilling[killer]; + } _stats[killer].Kills += 1; _stats[death].Deaths += 1; int money = Random.Shared.Next(250, 350); @@ -1392,6 +1520,7 @@ namespace Milimoe.FunGame.Core.Model money += (coefficient + 1) * Random.Shared.Next(50, 100); string termination = CharacterSet.GetContinuousKilling(coefficient); string msg = $"[ {killer} ] 终结了 [ {death} ]{(termination != "" ? " 的" + termination : "")},获得 {money} {General.GameplayEquilibriumConstant.InGameCurrency}!"; + LastRound.DeathContinuousKilling.Add(msg); if (assists.Length > 1) { msg += "助攻:[ " + string.Join(" ] / [ ", assists.Where(c => c != killer)) + " ]"; @@ -1401,6 +1530,7 @@ namespace Milimoe.FunGame.Core.Model else { string msg = $"[ {killer} ] 杀死了 [ {death} ],获得 {money} {General.GameplayEquilibriumConstant.InGameCurrency}!"; + LastRound.DeathContinuousKilling.Add(msg); if (assists.Length > 1) { msg += "助攻:[ " + string.Join(" ] / [ ", assists.Where(c => c != killer)) + " ]"; @@ -1419,21 +1549,27 @@ namespace Milimoe.FunGame.Core.Model int kills = _continuousKilling[killer]; string continuousKilling = CharacterSet.GetContinuousKilling(kills); + string actorContinuousKilling = ""; if (kills == 2 || kills == 3) { - WriteLine("[ " + killer + " ] 完成了" + continuousKilling + "!"); + actorContinuousKilling = "[ " + killer + " ] 完成了一次" + continuousKilling + "!"; } else if (kills == 4) { - WriteLine("[ " + killer + " ] 正在" + continuousKilling + "!"); + actorContinuousKilling = "[ " + killer + " ] 正在" + continuousKilling + "!"; } else if (kills > 4 && kills < 10) { - WriteLine("[ " + killer + " ] 已经" + continuousKilling + "!"); + actorContinuousKilling = "[ " + killer + " ] 已经" + continuousKilling + "!"; } else if (kills >= 10) { - WriteLine("[ " + killer + " ] 已经" + continuousKilling + "!拜托谁去杀了他吧!!!"); + actorContinuousKilling = "[ " + killer + " ] 已经" + continuousKilling + "!拜托谁去杀了他吧!!!"; + } + if (actorContinuousKilling != "") + { + LastRound.ActorContinuousKilling.Add(actorContinuousKilling); + WriteLine(actorContinuousKilling); } if (!_earnedMoney.TryAdd(killer, money)) _earnedMoney[killer] += money; @@ -1467,8 +1603,34 @@ namespace Milimoe.FunGame.Core.Model else { // 进入复活倒计时 - double respawnTime = Math.Min(90, death.Level * 0.36 + times * 2.77); + double respawnTime = Calculation.Round2Digits(Math.Min(90, death.Level * 0.36 + times * 2.77 + kills * Random.Shared.Next(0, 3))); _respawnCountdown.TryAdd(death, respawnTime); + LastRound.RespawnCountdowns.TryAdd(death, respawnTime); + WriteLine($"[ {death} ] 进入复活倒计时:{respawnTime:0.##} 时间!"); + } + + // 移除死者的施法 + _castingSkills.Remove(death); + _castingSuperSkills.Remove(death); + + // 因丢失目标而中断施法 + List castingSkills = [.. _castingSkills.Keys]; + foreach (Character caster in castingSkills) + { + SkillTarget st = _castingSkills[caster]; + if (st.Targets.Remove(death) && st.Targets.Count == 0) + { + _castingSkills.Remove(caster); + if (caster.CharacterState == CharacterState.Casting) + { + caster.CharacterState = CharacterState.Actionable; + } + WriteLine($"[ {caster} ] 终止了 [ {st.Skill.Name} ] 的施法" + (_hardnessTimes[caster] > 3 ? ",并获得了 3 硬直时间的补偿。" : "。")); + if (_hardnessTimes[caster] > 3) + { + _hardnessTimes[caster] = 3; + } + } } } @@ -1489,7 +1651,7 @@ namespace Milimoe.FunGame.Core.Model CharacterStatistics statistics = CharacterStatistics[ec]; string topCharacter = ec.ToString() + (statistics.FirstKills > 0 ? " [ 第一滴血 ]" : "") + - (_continuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") + + (_maxContinuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") + (_earnedMoney.TryGetValue(ec, out int earned) ? $" [ 已赚取 {earned} {General.GameplayEquilibriumConstant.InGameCurrency} ]" : ""); if (top == 1) { @@ -1571,7 +1733,7 @@ namespace Milimoe.FunGame.Core.Model string topCharacter = respawning + ec.ToString() + (statistics.FirstKills > 0 ? " [ 第一滴血 ]" : "") + - (_continuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") + + (_maxContinuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") + (_earnedMoney.TryGetValue(ec, out int earned) ? $" [ 已赚取 {earned} {General.GameplayEquilibriumConstant.InGameCurrency} ]" : "") + $"({statistics.Kills} / {statistics.Assists}{(MaxRespawnTimes != 0 ? " / " + statistics.Deaths : "")})"; topTeam += topCharacter + "\r\n"; @@ -1671,7 +1833,7 @@ namespace Milimoe.FunGame.Core.Model _hardnessTimes[c] = Calculation.Round2Digits(_hardnessTimes[c] + 0.01); } } - skill.OnSkillCasting(this, other); + skill.OnSkillCasting(this, other, []); } } } @@ -1779,6 +1941,14 @@ namespace Milimoe.FunGame.Core.Model } statsTaken.TotalTakenDamage = Calculation.Round2Digits(statsTaken.TotalTakenDamage + damage); } + if (LastRound.Damages.TryGetValue(characterTaken, out double damageTotal)) + { + LastRound.Damages[characterTaken] = damageTotal + damage; + } + else + { + LastRound.Damages[characterTaken] = damage; + } } /// @@ -1837,6 +2007,10 @@ namespace Milimoe.FunGame.Core.Model cancel = false; if (skill.SkillType == SkillType.SuperSkill) cancel = false; List targets = skill.SelectTargets(caster, enemys, teammates); + if (targets.Count == 0) + { + cancel = true; + } return targets; } diff --git a/Model/EquilibriumConstant.cs b/Model/EquilibriumConstant.cs index af1c6ea..4c36a82 100644 --- a/Model/EquilibriumConstant.cs +++ b/Model/EquilibriumConstant.cs @@ -224,6 +224,16 @@ /// 每 1 点智力增加魔法回复力 /// public double INTtoMRFactor { get; set; } = 0.1; + + /// + /// 每 1 点智力减少魔法消耗 + /// + public double INTtoCastMPReduce { get; set; } = 0.00125; + + /// + /// 每 1 点智力减少能量消耗 + /// + public double INTtoCastEPReduce { get; set; } = 0.00075; /// /// 每 1 点敏捷增加行动速度