From 44d5ffd3f7d4713741b62e3d0c2348d70bd873eb Mon Sep 17 00:00:00 2001 From: milimoe Date: Fri, 16 Jan 2026 01:55:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=A4=E5=AE=B3=E8=AE=A1=E7=AE=97=E3=80=81?= =?UTF-8?q?=E7=94=9F=E5=91=BD=E5=9B=9E=E5=A4=8D=E5=8F=AF=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E8=B0=83=E8=AF=95=E6=97=A5=E5=BF=97=EF=BC=9B?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B=E5=8D=95=E4=BD=8D=E5=BD=B1?= =?UTF-8?q?=E5=93=8D=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Entity/Skill/Effect.cs | 6 +- Entity/Skill/NormalAttack.cs | 2 +- Interface/Base/IGamingQueue.cs | 4 +- Model/DamageCalculationOptions.cs | 44 +++++++++- Model/GamingQueue.cs | 136 +++++++++++++++++++----------- Model/MixGamingQueue.cs | 6 +- Model/TeamGamingQueue.cs | 2 +- 7 files changed, 145 insertions(+), 55 deletions(-) diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs index 4d00bd1..834d988 100644 --- a/Entity/Skill/Effect.cs +++ b/Entity/Skill/Effect.cs @@ -557,11 +557,12 @@ namespace Milimoe.FunGame.Core.Entity /// 在完成死亡结算后 [ 全体广播 ] /// /// + /// /// /// /// /// - public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary continuousKilling, Dictionary earnedMoney, Character[] assists) + public virtual void AfterDeathCalculation(Character death, bool hasMaster, Character? killer, Dictionary continuousKilling, Dictionary earnedMoney, Character[] assists) { } @@ -955,9 +956,10 @@ namespace Milimoe.FunGame.Core.Entity DamageResult result = DamageResult.Normal; double damage = expectedDamage; options ??= new(); + if (options.ExpectedDamage == 0) options.ExpectedDamage = expectedDamage; if (options.NeedCalculate && damageType != DamageType.True) { - result = damageType == DamageType.Physical ? GamingQueue.CalculatePhysicalDamage(actor, enemy, false, expectedDamage, out damage, ref changeCount, options) : GamingQueue.CalculateMagicalDamage(actor, enemy, false, MagicType, expectedDamage, out damage, ref changeCount, options); + result = damageType == DamageType.Physical ? GamingQueue.CalculatePhysicalDamage(actor, enemy, false, expectedDamage, out damage, ref changeCount, ref options) : GamingQueue.CalculateMagicalDamage(actor, enemy, false, MagicType, expectedDamage, out damage, ref changeCount, ref options); } GamingQueue.DamageToEnemy(actor, enemy, damage, false, damageType, magicType, result, options); return result; diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs index bb5dace..6dcf101 100644 --- a/Entity/Skill/NormalAttack.cs +++ b/Entity/Skill/NormalAttack.cs @@ -352,7 +352,7 @@ namespace Milimoe.FunGame.Core.Entity queue.WriteLine($"[ {Character} ] 对 [ {enemy} ] 发起了普通攻击!"); double expected = Damage; int changeCount = 0; - DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage, ref changeCount, options) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage, ref changeCount, options); + DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage, ref changeCount, ref options) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage, ref changeCount, ref options); queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic ? DamageType.Magical : DamageType.Physical, MagicType, result, options); } } diff --git a/Interface/Base/IGamingQueue.cs b/Interface/Base/IGamingQueue.cs index ed95675..c8f562a 100644 --- a/Interface/Base/IGamingQueue.cs +++ b/Interface/Base/IGamingQueue.cs @@ -127,7 +127,7 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public DamageResult CalculatePhysicalDamage(Character actor, Character enemy, bool isNormalAttack, double expectedDamage, out double finalDamage, ref int changeCount, DamageCalculationOptions? options = null); + public DamageResult CalculatePhysicalDamage(Character actor, Character enemy, bool isNormalAttack, double expectedDamage, out double finalDamage, ref int changeCount, ref DamageCalculationOptions? options); /// /// 计算魔法伤害 @@ -141,7 +141,7 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage, ref int changeCount, DamageCalculationOptions? options = null); + public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage, ref int changeCount, ref DamageCalculationOptions? options); /// /// 死亡结算 diff --git a/Model/DamageCalculationOptions.cs b/Model/DamageCalculationOptions.cs index 7862dd5..52f0b26 100644 --- a/Model/DamageCalculationOptions.cs +++ b/Model/DamageCalculationOptions.cs @@ -1,4 +1,6 @@ -namespace Milimoe.FunGame.Core.Model +using Milimoe.FunGame.Core.Entity; + +namespace Milimoe.FunGame.Core.Model { /// /// 精准的分步控制伤害计算 @@ -39,5 +41,45 @@ /// 无视免疫 /// public bool IgnoreImmune { get; set; } = false; + + /// + /// 伤害基底(期望) + /// + internal double ExpectedDamage { get; set; } = 0; + + /// + /// 特效伤害加成记录(乘区1:暴击和减伤计算前) + /// + internal Dictionary BeforeDamageBonus { get; set; } = []; + + /// + /// 暴击伤害 + /// + internal double CriticalDamage { get; set; } = 0; + + /// + /// 伤害减免 + /// + internal double DefenseReduction { get; set; } = 0; + + /// + /// 特效伤害加成记录(乘区2:暴击和减伤计算后) + /// + internal Dictionary AfterDamageBonus { get; set; } = []; + + /// + /// 最终伤害 + /// + internal double FinalDamage { get; set; } = 0; + + /// + /// 护盾减免 + /// + internal double ShieldReduction { get; set; } = 0; + + /// + /// 实际造成伤害 + /// + internal double ActualDamage { get; set; } = 0; } } diff --git a/Model/GamingQueue.cs b/Model/GamingQueue.cs index 8c3fe2c..0566b06 100644 --- a/Model/GamingQueue.cs +++ b/Model/GamingQueue.cs @@ -2061,18 +2061,19 @@ namespace Milimoe.FunGame.Core.Model bool isEvaded = damageResult == DamageResult.Evaded; List effects = []; options ??= new(); + if (options.ExpectedDamage == 0) options.ExpectedDamage = damage; + Dictionary totalDamageBonus = []; if (options.TriggerEffects) { // 真实伤害跳过伤害加成区间 if (damageType != DamageType.True) { - Dictionary totalDamageBonus = []; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; foreach (Effect effect in effects) { double damageBonus = effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, damageType, magicType, damageResult, ref isEvaded, totalDamageBonus); - totalDamageBonus[effect] = damageBonus; + if (damageBonus != 0) totalDamageBonus[effect] = damageBonus; if (isEvaded) { damageResult = DamageResult.Evaded; @@ -2092,6 +2093,8 @@ namespace Milimoe.FunGame.Core.Model } } } + options.AfterDamageBonus = totalDamageBonus; + options.FinalDamage = damage; double actualDamage = damage; // 闪避了就没伤害了 @@ -2322,9 +2325,11 @@ namespace Milimoe.FunGame.Core.Model { stats.TotalShield += damage - actualDamage; } + options.ShieldReduction += damage - actualDamage; } } + options.ActualDamage = actualDamage; enemy.HP -= actualDamage; string strDamageMessage = $"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageTypeString}!{shieldMsg}"; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; @@ -2332,6 +2337,23 @@ namespace Milimoe.FunGame.Core.Model { effect.OnApplyDamage(enemy, actor, damage, actualDamage, isNormalAttack, damageType, magicType, damageResult, shieldMsg, ref strDamageMessage); } + if (IsDebug) + { + string strBeforeBonus = ""; + if (options.BeforeDamageBonus.Count > 0) + { + strBeforeBonus = string.Join("", options.BeforeDamageBonus.Select(kv => $"{(kv.Value >= 0 ? " + " : " - ")}{Math.Abs(kv.Value):0.##}({kv.Key.Name})")); + } + string strDefenseReduction = options.DefenseReduction == 0 ? "" : ($"{(options.DefenseReduction >= 0 ? " - " : " + ")}{Math.Abs(options.DefenseReduction):0.##}(减伤)"); + string strCriticalDamage = options.CriticalDamage == 0 ? "" : ($"{(options.CriticalDamage >= 0 ? " + " : " - ")}{Math.Abs(options.CriticalDamage):0.##}(暴击)"); + string strAfterBonus = ""; + if (options.AfterDamageBonus.Count > 0) + { + strAfterBonus = string.Join("", options.AfterDamageBonus.Select(kv => $"{(kv.Value >= 0 ? " + " : " - ")}{Math.Abs(kv.Value):0.##}({kv.Key.Name})")); + } + string strShieldReduction = options.ShieldReduction == 0 ? "" : ($"{(options.ShieldReduction >= 0 ? " - " : " + ")}{Math.Abs(options.ShieldReduction):0.##}(护盾)"); + strDamageMessage += $"【{options.ExpectedDamage:0.##}(基础){strBeforeBonus}{strDefenseReduction}{strCriticalDamage}{strAfterBonus}{strShieldReduction} = {options.ActualDamage:0.##} 点{damageTypeString}】"; + } WriteLine(strDamageMessage); // 生命偷取 @@ -2371,17 +2393,18 @@ namespace Milimoe.FunGame.Core.Model // 计算助攻 if (actor != enemy && !IsTeammate(actor, enemy)) { - if (actor.Master != null) + Character a = actor, e = enemy; + if (a.Master != null) { - actor = actor.Master; + a = a.Master; } - if (enemy.Master != null) + if (e.Master != null) { - enemy = enemy.Master; + e = e.Master; } - if (actor != enemy) + if (a != e) { - _assistDetail[actor][enemy, TotalTime] += damage; + _assistDetail[a][e, TotalTime] += damage; } } } @@ -2422,16 +2445,12 @@ namespace Milimoe.FunGame.Core.Model if (killer == death) { - if (!OnDeathCalculationEvent(killer, death)) - { - return; - } if (killer.Master is null) { _stats[death].Deaths += 1; } WriteLine($"[ {death} ] 自杀了!"); - DealWithCharacterDied(killer, death); + DealWithCharacterDied(killer, death, []); return; } @@ -2446,13 +2465,9 @@ namespace Milimoe.FunGame.Core.Model return; } - if (!OnDeathCalculationEvent(killer, death)) - { - return; - } - if (death.Master != null) { + DealWithCharacterDied(killer, death, []); return; } @@ -2610,18 +2625,7 @@ namespace Milimoe.FunGame.Core.Model WriteLine(actorContinuousKilling); } - DealWithCharacterDied(killer, death); - - // 给所有角色的特效广播角色死亡结算 - List effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Union(killer.Effects).Distinct()]; - foreach (Effect effect in effects) - { - effect.AfterDeathCalculation(death, killer, _continuousKilling, _earnedMoney, assists); - } - // 将死者移出队列 - _queue.Remove(death); - - OnCharacterDeathEvent(death, killer, assists); + DealWithCharacterDied(killer, death, assists); } /// @@ -2653,7 +2657,7 @@ namespace Milimoe.FunGame.Core.Model WriteLine(msg); } - DealWithCharacterDied(killer, death); + DealWithCharacterDied(killer, death, []); } /// @@ -2678,6 +2682,7 @@ namespace Milimoe.FunGame.Core.Model } bool isDead = target.HP <= 0; + string healString = $"【{heal:0.##}(基础)"; if (triggerEffects) { @@ -2691,9 +2696,16 @@ namespace Milimoe.FunGame.Core.Model { canRespawn = true; } + if (healBonus != 0) + { + totalHealBonus[effect]= healBonus; + healString += $"{(healBonus > 0 ? " + " : " - ")}{Math.Abs(healBonus):0.##}({effect.Name})"; + } } heal += totalHealBonus.Sum(kv => kv.Value); } + healString += $" = {heal:0.##} 点生命值】"; + if (!IsDebug) healString = ""; if (target.HP > 0 || (isDead && canRespawn)) { @@ -2719,11 +2731,11 @@ namespace Milimoe.FunGame.Core.Model { if (target != actor) { - WriteLine($"[ {target} ] 被 [ {actor} ] 复苏了,并回复了 {heal:0.##} 点生命值!!"); + WriteLine($"[ {target} ] 被 [ {actor} ] 复苏了,并回复了 {heal:0.##} 点生命值!!{healString}"); } else { - WriteLine($"[ {target} ] 复苏了,并回复了 {heal:0.##} 点生命值!!"); + WriteLine($"[ {target} ] 复苏了,并回复了 {heal:0.##} 点生命值!!{healString}"); } double hp = target.HP; double mp = target.MP; @@ -2733,7 +2745,7 @@ namespace Milimoe.FunGame.Core.Model } else { - WriteLine($"[ {target} ] 回复了 {heal:0.##} 点生命值!"); + WriteLine($"[ {target} ] 回复了 {heal:0.##} 点生命值!{healString}"); } // 添加助攻 @@ -2830,9 +2842,24 @@ namespace Milimoe.FunGame.Core.Model /// /// /// + /// /// - public void DealWithCharacterDied(Character killer, Character death) + public void DealWithCharacterDied(Character killer, Character death, Character[] assists) { + // 给所有角色的特效广播角色死亡结算 + List effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Union(killer.Effects).Distinct()]; + foreach (Effect effect in effects) + { + effect.AfterDeathCalculation(death, death.Master != null, killer, _continuousKilling, _earnedMoney, assists); + } + // 将死者移出队列 + _queue.Remove(death); + + if (!OnCharacterDeathEvent(death, killer, assists)) + { + return; + } + OnDeathCalculation(death, killer); death.EP = 0; @@ -3239,13 +3266,15 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public DamageResult CalculatePhysicalDamage(Character actor, Character enemy, bool isNormalAttack, double expectedDamage, out double finalDamage, ref int changeCount, DamageCalculationOptions? options = null) + public DamageResult CalculatePhysicalDamage(Character actor, Character enemy, bool isNormalAttack, double expectedDamage, out double finalDamage, ref int changeCount, ref DamageCalculationOptions? options) { options ??= new(); + if (options.ExpectedDamage == 0) options.ExpectedDamage = expectedDamage; List characters = [actor, enemy]; DamageType damageType = DamageType.Physical; MagicType magicType = MagicType.None; List effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; + Dictionary totalDamageBonus = []; if (options.TriggerEffects) { if (changeCount < 3) @@ -3257,19 +3286,19 @@ namespace Milimoe.FunGame.Core.Model if (damageType == DamageType.Magical) { changeCount++; - return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage, ref changeCount, options); + return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage, ref changeCount, ref options); } } - Dictionary totalDamageBonus = []; effects = [.. actor.Effects.Union(enemy.Effects).Distinct().Where(e => e.IsInEffect)]; foreach (Effect effect in effects) { double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, DamageType.Physical, MagicType.None, totalDamageBonus); - totalDamageBonus[effect] = damageBonus; + if (damageBonus != 0) totalDamageBonus[effect] = damageBonus; } expectedDamage += totalDamageBonus.Sum(kv => kv.Value); } + options.BeforeDamageBonus = totalDamageBonus; double dice = Random.Shared.NextDouble(); double throwingBonus = 0; @@ -3321,11 +3350,13 @@ namespace Milimoe.FunGame.Core.Model { penetratedDEF = (1 - actor.PhysicalPenetration) * enemy.DEF; physicalDamageReduction = penetratedDEF / (penetratedDEF + GameplayEquilibriumConstant.DEFReductionFactor); - finalDamage = expectedDamage * (1 - Calculation.PercentageCheck(physicalDamageReduction + enemy.ExPDR)); + options.DefenseReduction = expectedDamage * Calculation.PercentageCheck(physicalDamageReduction + enemy.ExPDR); + finalDamage = expectedDamage - options.DefenseReduction; } if (options.CalculateCritical) - { // 暴击检定 + { + // 暴击检定 effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; foreach (Effect effect in effects) { @@ -3340,6 +3371,7 @@ namespace Milimoe.FunGame.Core.Model dice = Random.Shared.NextDouble(); if (dice < (actor.CritRate + throwingBonus)) { + options.CriticalDamage = finalDamage * (actor.CritDMG - 1); finalDamage *= actor.CritDMG; // 暴击伤害倍率加成 WriteLine("暴击生效!!"); effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; @@ -3368,12 +3400,14 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage, ref int changeCount, DamageCalculationOptions? options = null) + public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage, ref int changeCount, ref DamageCalculationOptions? options) { options ??= new(); + if (options.ExpectedDamage == 0) options.ExpectedDamage = expectedDamage; List characters = [actor, enemy]; DamageType damageType = DamageType.Magical; List effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; + Dictionary totalDamageBonus = []; if (options.TriggerEffects) { if (changeCount < 3) @@ -3385,19 +3419,19 @@ namespace Milimoe.FunGame.Core.Model if (damageType == DamageType.Physical) { changeCount++; - return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage, ref changeCount, options); + return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage, ref changeCount, ref options); } } - Dictionary totalDamageBonus = []; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; foreach (Effect effect in effects) { double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, DamageType.Magical, magicType, totalDamageBonus); - totalDamageBonus[effect] = damageBonus; + if (damageBonus != 0) totalDamageBonus[effect] = damageBonus; } expectedDamage += totalDamageBonus.Sum(kv => kv.Value); } + options.BeforeDamageBonus = totalDamageBonus; double dice = Random.Shared.NextDouble(); double throwingBonus = 0; @@ -3446,8 +3480,11 @@ namespace Milimoe.FunGame.Core.Model // 魔法穿透后的魔法抗性 MDF = (1 - actor.MagicalPenetration) * MDF; + // 魔法抗性减伤 + options.DefenseReduction = expectedDamage * MDF; + // 最终的魔法伤害 - finalDamage = expectedDamage * (1 - MDF); + finalDamage = expectedDamage - options.DefenseReduction; } if (options.CalculateCritical) @@ -3467,6 +3504,7 @@ namespace Milimoe.FunGame.Core.Model dice = Random.Shared.NextDouble(); if (dice < (actor.CritRate + throwingBonus)) { + options.CriticalDamage = finalDamage * (actor.CritDMG - 1); finalDamage *= actor.CritDMG; // 暴击伤害倍率加成 WriteLine("暴击生效!!"); effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; @@ -3524,7 +3562,7 @@ namespace Milimoe.FunGame.Core.Model public bool IsTeammate(Character character, Character target) { List teammates = GetTeammates(character); - return teammates.Contains(target) && (character.Master == target || target == character.Master || (target.Master != null && character.Master != null && target.Master == character.Master)); + return teammates.Contains(target) || character.Master == target || target == character.Master || (target.Master != null && character.Master != null && target.Master == character.Master); } /// @@ -4225,6 +4263,10 @@ namespace Milimoe.FunGame.Core.Model { character = character.Master; } + if (characterTaken.Master != null) + { + characterTaken = characterTaken.Master; + } if (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null) { if (damageType == DamageType.True) diff --git a/Model/MixGamingQueue.cs b/Model/MixGamingQueue.cs index bc168b4..3e496bb 100644 --- a/Model/MixGamingQueue.cs +++ b/Model/MixGamingQueue.cs @@ -23,7 +23,7 @@ namespace Milimoe.FunGame.Core.Model .Select(kv => $"[ {kv.Key} ] {kv.Value.Kills} 分"))}\r\n剩余存活人数:{_queue.Count}"); } - if (!_queue.Any(c => c != killer && c.Master != killer && killer?.Master != c)) + if (!_queue.Any(c => c != killer && c.Master != killer && killer?.Master != c && killer?.Master != c.Master)) { // 没有其他的角色了,游戏结束 EndGameInfo(killer); @@ -66,6 +66,10 @@ namespace Milimoe.FunGame.Core.Model WriteLine("游戏结束。"); return; } + if (winner.Master != null) + { + winner = winner.Master; + } WriteLine("[ " + winner + " ] 是胜利者。"); foreach (Character character in _stats.OrderBy(kv => kv.Value.Kills) .ThenByDescending(kv => kv.Value.Deaths) diff --git a/Model/TeamGamingQueue.cs b/Model/TeamGamingQueue.cs index e3adf9d..9f053b9 100644 --- a/Model/TeamGamingQueue.cs +++ b/Model/TeamGamingQueue.cs @@ -135,7 +135,7 @@ namespace Milimoe.FunGame.Core.Model /// protected override void OnDeathCalculation(Character death, Character killer) { - if (killer == death) + if (killer == death || death.Master != null) { return; }