伤害计算、生命回复可显示更多调试日志;修复一些单位影响的问题

This commit is contained in:
milimoe 2026-01-16 01:55:26 +08:00
parent a93f9a274e
commit 44d5ffd3f7
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
7 changed files with 145 additions and 55 deletions

View File

@ -557,11 +557,12 @@ namespace Milimoe.FunGame.Core.Entity
/// 在完成死亡结算后 [ 全体广播 ]
/// </summary>
/// <param name="death"></param>
/// <param name="hasMaster"></param>
/// <param name="killer"></param>
/// <param name="continuousKilling"></param>
/// <param name="earnedMoney"></param>
/// <param name="assists"></param>
public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary<Character, int> continuousKilling, Dictionary<Character, int> earnedMoney, Character[] assists)
public virtual void AfterDeathCalculation(Character death, bool hasMaster, Character? killer, Dictionary<Character, int> continuousKilling, Dictionary<Character, int> 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;

View File

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

View File

@ -127,7 +127,7 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="changeCount"></param>
/// <param name="options"></param>
/// <returns></returns>
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);
/// <summary>
/// 计算魔法伤害
@ -141,7 +141,7 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="changeCount"></param>
/// <param name="options"></param>
/// <returns></returns>
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);
/// <summary>
/// 死亡结算

View File

@ -1,4 +1,6 @@
namespace Milimoe.FunGame.Core.Model
using Milimoe.FunGame.Core.Entity;
namespace Milimoe.FunGame.Core.Model
{
/// <summary>
/// 精准的分步控制伤害计算
@ -39,5 +41,45 @@
/// 无视免疫
/// </summary>
public bool IgnoreImmune { get; set; } = false;
/// <summary>
/// 伤害基底(期望)
/// </summary>
internal double ExpectedDamage { get; set; } = 0;
/// <summary>
/// 特效伤害加成记录乘区1暴击和减伤计算前
/// </summary>
internal Dictionary<Effect, double> BeforeDamageBonus { get; set; } = [];
/// <summary>
/// 暴击伤害
/// </summary>
internal double CriticalDamage { get; set; } = 0;
/// <summary>
/// 伤害减免
/// </summary>
internal double DefenseReduction { get; set; } = 0;
/// <summary>
/// 特效伤害加成记录乘区2暴击和减伤计算后
/// </summary>
internal Dictionary<Effect, double> AfterDamageBonus { get; set; } = [];
/// <summary>
/// 最终伤害
/// </summary>
internal double FinalDamage { get; set; } = 0;
/// <summary>
/// 护盾减免
/// </summary>
internal double ShieldReduction { get; set; } = 0;
/// <summary>
/// 实际造成伤害
/// </summary>
internal double ActualDamage { get; set; } = 0;
}
}

View File

@ -2061,18 +2061,19 @@ namespace Milimoe.FunGame.Core.Model
bool isEvaded = damageResult == DamageResult.Evaded;
List<Effect> effects = [];
options ??= new();
if (options.ExpectedDamage == 0) options.ExpectedDamage = damage;
Dictionary<Effect, double> totalDamageBonus = [];
if (options.TriggerEffects)
{
// 真实伤害跳过伤害加成区间
if (damageType != DamageType.True)
{
Dictionary<Effect, double> 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<Effect> 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);
}
/// <summary>
@ -2653,7 +2657,7 @@ namespace Milimoe.FunGame.Core.Model
WriteLine(msg);
}
DealWithCharacterDied(killer, death);
DealWithCharacterDied(killer, death, []);
}
/// <summary>
@ -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
/// </summary>
/// <param name="killer"></param>
/// <param name="death"></param>
/// <param name="assists"></param>
/// <returns></returns>
public void DealWithCharacterDied(Character killer, Character death)
public void DealWithCharacterDied(Character killer, Character death, Character[] assists)
{
// 给所有角色的特效广播角色死亡结算
List<Effect> 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
/// <param name="changeCount"></param>
/// <param name="options"></param>
/// <returns></returns>
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<Character> characters = [actor, enemy];
DamageType damageType = DamageType.Physical;
MagicType magicType = MagicType.None;
List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
Dictionary<Effect, double> 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<Effect, double> 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
/// <param name="changeCount"></param>
/// <param name="options"></param>
/// <returns></returns>
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<Character> characters = [actor, enemy];
DamageType damageType = DamageType.Magical;
List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
Dictionary<Effect, double> 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<Effect, double> 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<Character> 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);
}
/// <summary>
@ -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)

View File

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

View File

@ -135,7 +135,7 @@ namespace Milimoe.FunGame.Core.Model
/// <returns></returns>
protected override void OnDeathCalculation(Character death, Character killer)
{
if (killer == death)
if (killer == death || death.Master != null)
{
return;
}