底层支持真实伤害 (#141)

This commit is contained in:
milimoe 2025-06-19 21:20:53 +08:00 committed by GitHub
parent a2fcbce157
commit 6bf3bb09a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 378 additions and 263 deletions

View File

@ -129,9 +129,13 @@ namespace Milimoe.FunGame.Core.Entity
} }
set set
{ {
int past = _Level;
_Level = Math.Min(Math.Max(1, value), GameplayEquilibriumConstant.MaxLevel); _Level = Math.Min(Math.Max(1, value), GameplayEquilibriumConstant.MaxLevel);
OnAttributeChanged(); if (past != _Level)
Recovery(); {
OnAttributeChanged();
Recovery();
}
} }
} }
@ -900,8 +904,8 @@ namespace Milimoe.FunGame.Core.Entity
{ {
if (time > 0) if (time > 0)
{ {
HP = Math.Min(MaxHP, HP + HR * time); HP += HR * time;
MP = Math.Min(MaxMP, MP + MR * time); MP += MR * time;
if (EP != -1) this.EP = EP; if (EP != -1) this.EP = EP;
} }
} }
@ -915,10 +919,16 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="pastMaxMP"></param> /// <param name="pastMaxMP"></param>
public void Recovery(double pastHP, double pastMP, double pastMaxHP, double pastMaxMP) public void Recovery(double pastHP, double pastMP, double pastMaxHP, double pastMaxMP)
{ {
double pHP = pastHP / pastMaxHP; if (pastHP > 0 && pastMaxHP > 0)
double pMP = pastMP / pastMaxMP; {
HP = MaxHP * pHP; double pHP = pastHP / pastMaxHP;
MP = MaxMP * pMP; HP = MaxHP * pHP;
}
if (pastMP > 0 && pastMaxMP > 0)
{
double pMP = pastMP / pastMaxMP;
MP = MaxMP * pMP;
}
} }
/// <summary> /// <summary>
@ -1147,6 +1157,32 @@ namespace Milimoe.FunGame.Core.Entity
return result; return result;
} }
/// <summary>
/// 设置角色等级,并默认完全回复状态
/// </summary>
/// <param name="level">新的等级</param>
/// <param name="recovery">false 为按百分比回复</param>
public void SetLevel(int level, bool recovery = true)
{
if (!recovery)
{
double pastHP = HP;
double pastMP = MP;
double pastMaxHP = MaxHP;
double pastMaxMP = MaxMP;
int pastLevel = Level;
Level = level;
if (pastLevel != Level)
{
Recovery(pastHP, pastMP, pastMaxHP, pastMaxMP);
}
}
else
{
Level = level;
}
}
/// <summary> /// <summary>
/// 角色升级 /// 角色升级
/// </summary> /// </summary>

View File

@ -154,7 +154,7 @@ namespace Milimoe.FunGame.Core.Entity
{ {
ShieldOfEffects[soe.Effect] = soe; ShieldOfEffects[soe.Effect] = soe;
} }
/// <summary> /// <summary>
/// 移除某个特效的护盾 /// 移除某个特效的护盾
/// </summary> /// </summary>

View File

@ -59,7 +59,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 附属于某个特效 /// 附属于某个特效
/// </summary> /// </summary>
public Effect? ParentEffect { get; set; } = null; public Effect? ParentEffect { get; set; } = null;
/// <summary> /// <summary>
/// 是否是某个特效的附属 /// 是否是某个特效的附属
/// </summary> /// </summary>
@ -218,9 +218,9 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="character"></param> /// <param name="character"></param>
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
public virtual void AlterDamageTypeBeforeCalculation(Character character, Character enemy, ref bool isNormalAttack, ref bool isMagicDamage, ref MagicType magicType) public virtual void AlterDamageTypeBeforeCalculation(Character character, Character enemy, ref bool isNormalAttack, ref DamageType damageType, ref MagicType magicType)
{ {
} }
@ -232,11 +232,11 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="totalDamageBonus"></param> /// <param name="totalDamageBonus"></param>
/// <returns>返回伤害增减值</returns> /// <returns>返回伤害增减值</returns>
public virtual double AlterExpectedDamageBeforeCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, Dictionary<Effect, double> totalDamageBonus) public virtual double AlterExpectedDamageBeforeCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, Dictionary<Effect, double> totalDamageBonus)
{ {
return 0; return 0;
} }
@ -248,13 +248,13 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damageResult"></param> /// <param name="damageResult"></param>
/// <param name="isEvaded"></param> /// <param name="isEvaded"></param>
/// <param name="totalDamageBonus"></param> /// <param name="totalDamageBonus"></param>
/// <returns>返回伤害增减值</returns> /// <returns>返回伤害增减值</returns>
public virtual double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary<Effect, double> totalDamageBonus) public virtual double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary<Effect, double> totalDamageBonus)
{ {
return 0; return 0;
} }
@ -335,7 +335,7 @@ namespace Milimoe.FunGame.Core.Entity
} }
/// <summary> /// <summary>
/// 对目标触发技能效果 /// 对目标触发技能效果(局外)
/// </summary> /// </summary>
/// <param name="user"></param> /// <param name="user"></param>
/// <param name="targets"></param> /// <param name="targets"></param>
@ -363,10 +363,10 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="actualDamage"></param> /// <param name="actualDamage"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damageResult"></param> /// <param name="damageResult"></param>
public virtual void AfterDamageCalculation(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult) public virtual void AfterDamageCalculation(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult)
{ {
} }
@ -656,13 +656,13 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary> /// </summary>
/// <param name="character"></param> /// <param name="character"></param>
/// <param name="attacker"></param> /// <param name="attacker"></param>
/// <param name="isMagic"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="damageReduce"></param> /// <param name="damageReduce"></param>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns>返回 false 可以跳过护盾结算</returns> /// <returns>返回 false 可以跳过护盾结算</returns>
public virtual bool BeforeShieldCalculation(Character character, Character attacker, bool isMagic, MagicType magicType, double damage, ref double damageReduce, ref string message) public virtual bool BeforeShieldCalculation(Character character, Character attacker, DamageType damageType, MagicType magicType, double damage, ref double damageReduce, ref string message)
{ {
return true; return true;
} }
@ -672,11 +672,11 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary> /// </summary>
/// <param name="character"></param> /// <param name="character"></param>
/// <param name="attacker"></param> /// <param name="attacker"></param>
/// <param name="isMagic"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="shieldType"></param> /// <param name="shieldType"></param>
public virtual void OnShieldNeutralizeDamage(Character character, Character attacker, bool isMagic, MagicType magicType, double damage, ShieldType shieldType) public virtual void OnShieldNeutralizeDamage(Character character, Character attacker, DamageType damageType, MagicType magicType, double damage, ShieldType shieldType)
{ {
} }
@ -726,11 +726,11 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="actor"></param> /// <param name="actor"></param>
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagic"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <returns>false免疫检定不通过</returns> /// <returns>false免疫检定不通过</returns>
public virtual bool OnDamageImmuneCheck(Character actor, Character enemy, bool isNormalAttack, bool isMagic, MagicType magicType, double damage) public virtual bool OnDamageImmuneCheck(Character actor, Character enemy, bool isNormalAttack, DamageType damageType, MagicType magicType, double damage)
{ {
return true; return true;
} }
@ -740,16 +740,21 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary> /// </summary>
/// <param name="actor"></param> /// <param name="actor"></param>
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="isMagic"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="expectedDamage"></param> /// <param name="expectedDamage"></param>
/// <returns></returns> /// <returns></returns>
public DamageResult DamageToEnemy(Character actor, Character enemy, bool isMagic, MagicType magicType, double expectedDamage) public DamageResult DamageToEnemy(Character actor, Character enemy, DamageType damageType, MagicType magicType, double expectedDamage)
{ {
if (GamingQueue is null) return DamageResult.Evaded; if (GamingQueue is null) return DamageResult.Evaded;
int changeCount = 0; int changeCount = 0;
DamageResult result = !isMagic ? GamingQueue.CalculatePhysicalDamage(actor, enemy, false, expectedDamage, out double damage, ref changeCount) : GamingQueue.CalculateMagicalDamage(actor, enemy, false, MagicType, expectedDamage, out damage, ref changeCount); DamageResult result = DamageResult.Normal;
GamingQueue.DamageToEnemyAsync(actor, enemy, damage, false, isMagic, magicType, result); double damage = expectedDamage;
if (damageType != DamageType.True)
{
result = damageType == DamageType.Physical ? GamingQueue.CalculatePhysicalDamage(actor, enemy, false, expectedDamage, out damage, ref changeCount) : GamingQueue.CalculateMagicalDamage(actor, enemy, false, MagicType, expectedDamage, out damage, ref changeCount);
}
GamingQueue.DamageToEnemyAsync(actor, enemy, damage, false, damageType, magicType, result);
return result; return result;
} }

View File

@ -181,7 +181,7 @@ namespace Milimoe.FunGame.Core.Entity
double expected = Damage; double expected = Damage;
int changeCount = 0; int changeCount = 0;
DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage, ref changeCount) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage, ref changeCount); DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage, ref changeCount) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage, ref changeCount);
queue.DamageToEnemyAsync(attacker, enemy, damage, true, IsMagic, MagicType, result); queue.DamageToEnemyAsync(attacker, enemy, damage, true, IsMagic ? DamageType.Magical : DamageType.Physical, MagicType, result);
} }
} }
} }

View File

@ -5,19 +5,19 @@
public double TotalDamage { get; set; } = 0; public double TotalDamage { get; set; } = 0;
public double TotalPhysicalDamage { get; set; } = 0; public double TotalPhysicalDamage { get; set; } = 0;
public double TotalMagicDamage { get; set; } = 0; public double TotalMagicDamage { get; set; } = 0;
public double TotalRealDamage { get; set; } = 0; public double TotalTrueDamage { get; set; } = 0;
public double TotalTakenDamage { get; set; } = 0; public double TotalTakenDamage { get; set; } = 0;
public double TotalTakenPhysicalDamage { get; set; } = 0; public double TotalTakenPhysicalDamage { get; set; } = 0;
public double TotalTakenMagicDamage { get; set; } = 0; public double TotalTakenMagicDamage { get; set; } = 0;
public double TotalTakenRealDamage { get; set; } = 0; public double TotalTakenTrueDamage { get; set; } = 0;
public double AvgDamage { get; set; } = 0; public double AvgDamage { get; set; } = 0;
public double AvgPhysicalDamage { get; set; } = 0; public double AvgPhysicalDamage { get; set; } = 0;
public double AvgMagicDamage { get; set; } = 0; public double AvgMagicDamage { get; set; } = 0;
public double AvgRealDamage { get; set; } = 0; public double AvgTrueDamage { get; set; } = 0;
public double AvgTakenDamage { get; set; } = 0; public double AvgTakenDamage { get; set; } = 0;
public double AvgTakenPhysicalDamage { get; set; } = 0; public double AvgTakenPhysicalDamage { get; set; } = 0;
public double AvgTakenMagicDamage { get; set; } = 0; public double AvgTakenMagicDamage { get; set; } = 0;
public double AvgTakenRealDamage { get; set; } = 0; public double AvgTakenTrueDamage { get; set; } = 0;
public double TotalHeal { get; set; } = 0; public double TotalHeal { get; set; } = 0;
public double AvgHeal { get; set; } = 0; public double AvgHeal { get; set; } = 0;
public double TotalShield { get; set; } = 0; public double TotalShield { get; set; } = 0;

View File

@ -1,15 +1,15 @@
namespace Milimoe.FunGame.Core.Entity namespace Milimoe.FunGame.Core.Entity
{ {
public class GameStatistics public class GameStatistics(Room Room)
{ {
public long Id => Room.Id; public long Id => Room.Id;
public Room Room { get; } public Room Room { get; } = Room;
public DateTime RecordTime { get; set; } = DateTime.Now; public DateTime RecordTime { get; set; } = DateTime.Now;
public string Record { get; set; } = ""; public string Record { get; set; } = "";
public Dictionary<User, double> DamageStats { get; set; } = new(); public Dictionary<User, double> DamageStats { get; set; } = [];
public Dictionary<User, double> PhysicalDamageStats { get; } = new(); public Dictionary<User, double> PhysicalDamageStats { get; } = [];
public Dictionary<User, double> MagicDamageStats { get; } = new(); public Dictionary<User, double> MagicDamageStats { get; } = [];
public Dictionary<User, double> RealDamageStats { get; } = new(); public Dictionary<User, double> TrueDamageStats { get; } = [];
public double AvgDamageStats public double AvgDamageStats
{ {
get get
@ -46,30 +46,25 @@
return Math.Round(total / MagicDamageStats.Count, 2); return Math.Round(total / MagicDamageStats.Count, 2);
} }
} }
public double AvgRealDamageStats public double AvgTrueDamageStats
{ {
get get
{ {
double total = 0; double total = 0;
foreach (User user in RealDamageStats.Keys) foreach (User user in TrueDamageStats.Keys)
{ {
total += RealDamageStats[user]; total += TrueDamageStats[user];
} }
return Math.Round(total / RealDamageStats.Count, 2); return Math.Round(total / TrueDamageStats.Count, 2);
} }
} }
public Dictionary<User, double> KillStats { get; } = new(); public Dictionary<User, double> KillStats { get; } = [];
public Dictionary<User, Dictionary<User, int>> KillDetailStats { get; } = new(); // 子字典记录的是被击杀者以及被击杀次数 public Dictionary<User, Dictionary<User, int>> KillDetailStats { get; } = []; // 子字典记录的是被击杀者以及被击杀次数
public Dictionary<User, double> DeathStats { get; } = new(); public Dictionary<User, double> DeathStats { get; } = [];
public Dictionary<User, Dictionary<User, int>> DeathDetailStats { get; } = new(); // 子字典记录的是击杀者以及击杀次数 public Dictionary<User, Dictionary<User, int>> DeathDetailStats { get; } = []; // 子字典记录的是击杀者以及击杀次数
public Dictionary<User, long> AssistStats { get; } = new(); public Dictionary<User, long> AssistStats { get; } = [];
public Dictionary<User, double> RatingStats { get; } = new(); // 结算后的Rating public Dictionary<User, double> RatingStats { get; } = []; // 结算后的Rating
public Dictionary<User, double> EloStats { get; } = new(); // Elo分数变化(+/-) public Dictionary<User, double> EloStats { get; } = []; // Elo分数变化(+/-)
public Dictionary<User, string> RankStats { get; } = new(); // 结算后的Rank非比赛前 public Dictionary<User, string> RankStats { get; } = []; // 结算后的Rank非比赛前
public GameStatistics(Room Room)
{
this.Room = Room;
}
} }
} }

View File

@ -11,7 +11,7 @@ namespace Milimoe.FunGame.Core.Entity
public Dictionary<long, double> DamageStats { get; } = []; public Dictionary<long, double> DamageStats { get; } = [];
public Dictionary<long, double> PhysicalDamageStats { get; } = []; public Dictionary<long, double> PhysicalDamageStats { get; } = [];
public Dictionary<long, double> MagicDamageStats { get; } = []; public Dictionary<long, double> MagicDamageStats { get; } = [];
public Dictionary<long, double> RealDamageStats { get; } = []; public Dictionary<long, double> TrueDamageStats { get; } = [];
public Dictionary<long, double> AvgDamageStats public Dictionary<long, double> AvgDamageStats
{ {
get get
@ -66,7 +66,7 @@ namespace Milimoe.FunGame.Core.Entity
return avgdamage; return avgdamage;
} }
} }
public Dictionary<long, double> AvgRealDamageStats public Dictionary<long, double> AvgTrueDamageStats
{ {
get get
{ {
@ -75,9 +75,9 @@ namespace Milimoe.FunGame.Core.Entity
{ {
long plays = Plays[key]; long plays = Plays[key];
double total = 0; double total = 0;
if (RealDamageStats.ContainsKey(key)) if (TrueDamageStats.ContainsKey(key))
{ {
total = RealDamageStats.Values.Sum(); total = TrueDamageStats.Values.Sum();
} }
avgdamage.Add(key, Math.Round(total / plays, 2)); avgdamage.Add(key, Math.Round(total / plays, 2));
} }

View File

@ -79,10 +79,10 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damageResult"></param> /// <param name="damageResult"></param>
public Task DamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal); public Task DamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, DamageType damageType = DamageType.Physical, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal);
/// <summary> /// <summary>
/// 治疗一个目标 /// 治疗一个目标
@ -198,5 +198,15 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="isPercentage">是否是百分比</param> /// <param name="isPercentage">是否是百分比</param>
/// <param name="isCheckProtected">是否使用插队保护机制</param> /// <param name="isCheckProtected">是否使用插队保护机制</param>
public void ChangeCharacterHardnessTime(Character character, double addValue, bool isPercentage, bool isCheckProtected); public void ChangeCharacterHardnessTime(Character character, double addValue, bool isPercentage, bool isCheckProtected);
/// <summary>
/// 计算角色的数据
/// </summary>
/// <param name="character"></param>
/// <param name="characterTaken"></param>
/// <param name="damage"></param>
/// <param name="damageType"></param>
/// <param name="takenDamage"></param>
public void CalculateCharacterDamageStatistics(Character character, Character characterTaken, double damage, DamageType damageType, double takenDamage = -1);
} }
} }

View File

@ -455,6 +455,16 @@ namespace Milimoe.FunGame.Core.Library.Constant
_ => "★" _ => "★"
}; };
} }
public static string GetDamageTypeName(DamageType damageType, MagicType magicType = MagicType.None)
{
return damageType switch
{
DamageType.Magical => GetMagicDamageName(magicType),
DamageType.True => "真实伤害",
_ => "物理伤害"
};
}
} }
public class ItemSet public class ItemSet

View File

@ -312,12 +312,12 @@ namespace Milimoe.FunGame.Core.Library.Constant
Poison, Poison,
/// <summary> /// <summary>
/// 燃烧,目标受到火焰伤害,持续一段时间 /// 燃烧,目标受到伤害,持续一段时间
/// </summary> /// </summary>
Burn, Burn,
/// <summary> /// <summary>
/// 流血,目标持续受到物理伤害 /// 流血,目标持续受到伤害
/// </summary> /// </summary>
Bleed, Bleed,
@ -1039,4 +1039,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
Mix, Mix,
Effect Effect
} }
public enum DamageType
{
Physical,
Magical,
True
}
} }

View File

@ -130,7 +130,7 @@ namespace Milimoe.FunGame.Core.Model
/// 原始的角色字典 /// 原始的角色字典
/// </summary> /// </summary>
protected readonly Dictionary<Guid, Character> _original = []; protected readonly Dictionary<Guid, Character> _original = [];
/// <summary> /// <summary>
/// 当前的行动顺序 /// 当前的行动顺序
/// </summary> /// </summary>
@ -226,6 +226,11 @@ namespace Milimoe.FunGame.Core.Model
/// </summary> /// </summary>
protected bool _isGameEnd = false; protected bool _isGameEnd = false;
/// <summary>
/// 是否在回合内
/// </summary>
protected bool _isInRound = false;
#endregion #endregion
#region #region
@ -537,7 +542,8 @@ namespace Milimoe.FunGame.Core.Model
TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce); TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce);
WriteLine("时间流逝:" + timeToReduce); WriteLine("时间流逝:" + timeToReduce);
foreach (Character character in _queue) Character[] characters = [.. _queue];
foreach (Character character in characters)
{ {
// 减少所有角色的硬直时间 // 减少所有角色的硬直时间
_hardnessTimes[character] = Calculation.Round2Digits(_hardnessTimes[character] - timeToReduce); _hardnessTimes[character] = Calculation.Round2Digits(_hardnessTimes[character] - timeToReduce);
@ -635,15 +641,17 @@ namespace Milimoe.FunGame.Core.Model
if (effect.Durative) if (effect.Durative)
{ {
effect.RemainDuration -= timeToReduce; if (effect.RemainDuration < timeToReduce)
if (effect.RemainDuration <= 0)
{ {
// 移除特效前也完成剩余时间内的效果
effect.OnTimeElapsed(character, effect.RemainDuration);
effect.RemainDuration = 0; effect.RemainDuration = 0;
character.Effects.Remove(effect); character.Effects.Remove(effect);
effect.OnEffectLost(character); effect.OnEffectLost(character);
} }
else else
{ {
effect.RemainDuration -= timeToReduce;
effect.OnTimeElapsed(character, timeToReduce); effect.OnTimeElapsed(character, timeToReduce);
} }
} }
@ -688,11 +696,13 @@ namespace Milimoe.FunGame.Core.Model
/// <returns>是否结束游戏</returns> /// <returns>是否结束游戏</returns>
public async Task<bool> ProcessTurnAsync(Character character) public async Task<bool> ProcessTurnAsync(Character character)
{ {
_isInRound = true;
LastRound.Actor = character; LastRound.Actor = character;
_roundDeaths.Clear(); _roundDeaths.Clear();
if (!await BeforeTurnAsync(character)) if (!await BeforeTurnAsync(character))
{ {
_isInRound = false;
return _isGameEnd; return _isGameEnd;
} }
@ -723,6 +733,7 @@ namespace Milimoe.FunGame.Core.Model
// 如果事件全程接管回合操作,需要注意触发特效 // 如果事件全程接管回合操作,需要注意触发特效
if (!await OnTurnStartAsync(character, enemys, teammates, skills, items)) if (!await OnTurnStartAsync(character, enemys, teammates, skills, items))
{ {
_isInRound = false;
return _isGameEnd; return _isGameEnd;
} }
@ -806,7 +817,9 @@ namespace Milimoe.FunGame.Core.Model
} }
else if (character.CharacterState == CharacterState.ActionRestricted) else if (character.CharacterState == CharacterState.ActionRestricted)
{ {
// 行动受限,只能使用特殊物品 // 行动受限,只能使用消耗品
items = [.. items.Where(i => i.ItemType == ItemType.Consumable)];
canUseItem = items.Count > 0;
if (canUseItem) if (canUseItem)
{ {
pCastSkill = 0; pCastSkill = 0;
@ -1131,7 +1144,7 @@ namespace Milimoe.FunGame.Core.Model
{ {
decided = true; decided = true;
LastRound.Item = item; LastRound.Item = item;
baseTime = skill.RealHardnessTime; baseTime = skill.RealHardnessTime > 0 ? skill.RealHardnessTime : 5;
effects = [.. character.Effects.Where(e => e.IsInEffect)]; effects = [.. character.Effects.Where(e => e.IsInEffect)];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
@ -1148,7 +1161,7 @@ namespace Milimoe.FunGame.Core.Model
character.CharacterState == CharacterState.BattleRestricted) character.CharacterState == CharacterState.BattleRestricted)
{ {
baseTime += 5; baseTime += 5;
WriteLine($"角色 [ {character} ] 状态为:{CharacterSet.GetCharacterState(character.CharacterState)},放弃行动将额外获得 5 {GameplayEquilibriumConstant.InGameTime}硬直时间!"); WriteLine($"[ {character} ] {CharacterSet.GetCharacterState(character.CharacterState)},放弃行动将额外获得 5 {GameplayEquilibriumConstant.InGameTime}硬直时间!");
} }
decided = true; decided = true;
WriteLine($"[ {character} ] 结束了回合!"); WriteLine($"[ {character} ] 结束了回合!");
@ -1185,6 +1198,7 @@ namespace Milimoe.FunGame.Core.Model
await AfterTurnAsync(character); await AfterTurnAsync(character);
_isInRound = false;
return _isGameEnd; return _isGameEnd;
} }
@ -1242,6 +1256,7 @@ namespace Milimoe.FunGame.Core.Model
await AfterTurnAsync(character); await AfterTurnAsync(character);
WriteLine(""); WriteLine("");
_isInRound = false;
return _isGameEnd; return _isGameEnd;
} }
@ -1348,10 +1363,10 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="enemy"></param> /// <param name="enemy"></param>
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damageResult"></param> /// <param name="damageResult"></param>
public async Task DamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal) public async Task DamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, DamageType damageType = DamageType.Physical, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal)
{ {
// 如果敌人在结算伤害之前就已经死亡,将不会继续下去 // 如果敌人在结算伤害之前就已经死亡,将不会继续下去
if (enemy.HP <= 0) if (enemy.HP <= 0)
@ -1368,169 +1383,220 @@ namespace Milimoe.FunGame.Core.Model
List<Character> characters = [actor, enemy]; List<Character> characters = [actor, enemy];
bool isEvaded = damageResult == DamageResult.Evaded; bool isEvaded = damageResult == DamageResult.Evaded;
Dictionary<Effect, double> totalDamageBonus = []; List<Effect> effects = [];
List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects) // 真实伤害跳过伤害加成区间
if (damageType != DamageType.True)
{ {
double damageBonus = effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult, ref isEvaded, totalDamageBonus); Dictionary<Effect, double> totalDamageBonus = [];
totalDamageBonus[effect] = damageBonus; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
if (isEvaded) foreach (Effect effect in effects)
{ {
damageResult = DamageResult.Evaded; double damageBonus = effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, damageType, magicType, damageResult, ref isEvaded, totalDamageBonus);
totalDamageBonus[effect] = damageBonus;
if (isEvaded)
{
damageResult = DamageResult.Evaded;
}
} }
damage += totalDamageBonus.Sum(kv => kv.Value);
} }
damage += totalDamageBonus.Sum(kv => kv.Value);
double actualDamage = damage; double actualDamage = damage;
// 闪避了就没伤害了 // 闪避了就没伤害了
if (damageResult != DamageResult.Evaded) if (damageResult != DamageResult.Evaded)
{ {
// 开始计算伤害免疫 // 开始计算伤害免疫
// 此变量为是否无视免疫 bool isImmune = false;
bool ignore = false; // 真实伤害跳过免疫
// 技能免疫无法免疫普通攻击,但是魔法免疫和物理免疫可以 if (damageType != DamageType.True)
bool isImmune = (isNormalAttack && (enemy.ImmuneType == ImmuneType.All || enemy.ImmuneType == ImmuneType.Physical || enemy.ImmuneType == ImmuneType.Magical)) ||
(!isNormalAttack && (enemy.ImmuneType == ImmuneType.All || enemy.ImmuneType == ImmuneType.Physical || enemy.ImmuneType == ImmuneType.Magical || enemy.ImmuneType == ImmuneType.Skilled));
if (isImmune)
{ {
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; // 此变量为是否无视免疫
foreach (Effect effect in effects) bool ignore = false;
// 技能免疫无法免疫普通攻击,但是魔法免疫和物理免疫可以
isImmune = (isNormalAttack && (enemy.ImmuneType == ImmuneType.All || enemy.ImmuneType == ImmuneType.Physical || enemy.ImmuneType == ImmuneType.Magical)) ||
(!isNormalAttack && (enemy.ImmuneType == ImmuneType.All || enemy.ImmuneType == ImmuneType.Physical || enemy.ImmuneType == ImmuneType.Magical || enemy.ImmuneType == ImmuneType.Skilled));
if (isImmune)
{ {
if (isNormalAttack) effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{ {
if (actor.NormalAttack.IgnoreImmune == ImmuneType.All || if (isNormalAttack)
(!isMagicDamage && actor.NormalAttack.IgnoreImmune == ImmuneType.Physical) ||
(isMagicDamage && actor.NormalAttack.IgnoreImmune == ImmuneType.Magical) ||
!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, isMagicDamage, magicType, damage))
{ {
ignore = true; if (actor.NormalAttack.IgnoreImmune == ImmuneType.All ||
(damageType == DamageType.Physical && actor.NormalAttack.IgnoreImmune == ImmuneType.Physical) ||
(damageType == DamageType.Magical && actor.NormalAttack.IgnoreImmune == ImmuneType.Magical) ||
!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, damageType, magicType, damage))
{
ignore = true;
}
} }
} else
else
{
if (!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, isMagicDamage, magicType, damage))
{ {
ignore = true; if (!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, damageType, magicType, damage))
{
ignore = true;
}
} }
} }
} }
if (ignore)
{
// 无视免疫
isImmune = false;
}
if (isImmune)
{
// 免疫
damageResult = DamageResult.Immune;
LastRound.IsImmune[enemy] = true;
WriteLine($"[ {enemy} ] 免疫了此伤害!");
actualDamage = 0;
}
} }
if (ignore) // 继续计算伤害
{ if (!isImmune)
// 无视免疫
isImmune = false;
}
if (isImmune)
{
// 免疫
damageResult = DamageResult.Immune;
LastRound.IsImmune[enemy] = true;
WriteLine($"[ {enemy} ] 免疫了此伤害!");
actualDamage = 0;
}
else
{ {
if (damage < 0) damage = 0; if (damage < 0) damage = 0;
string damageType = isMagicDamage ? CharacterSet.GetMagicDamageName(magicType) : "物理伤害"; string damageTypeString = CharacterSet.GetDamageTypeName(damageType, magicType);
// 在护盾结算前,特效可以有自己的逻辑
bool change = false;
string shieldMsg = ""; string shieldMsg = "";
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
double damageReduce = 0;
if (!effect.BeforeShieldCalculation(enemy, actor, isMagicDamage, magicType, damage, ref damageReduce, ref shieldMsg))
{
change = true;
}
if (damageReduce != 0)
{
actualDamage -= damageReduce;
if (actualDamage < 0) actualDamage = 0;
}
}
// 检查护盾 // 真实伤害跳过护盾结算
if (!change) if (damageType != DamageType.True)
{ {
double remain = actualDamage; // 在护盾结算前,特效可以有自己的逻辑
bool change = false;
// 检查特效护盾 effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
effects = [.. enemy.Shield.ShieldOfEffects.Keys];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
ShieldOfEffect soe = enemy.Shield.ShieldOfEffects[effect]; double damageReduce = 0;
if (soe.IsMagic == isMagicDamage && (!isMagicDamage || soe.MagicType == magicType) && soe.Shield > 0) if (!effect.BeforeShieldCalculation(enemy, actor, damageType, magicType, damage, ref damageReduce, ref shieldMsg))
{ {
double effectShield = soe.Shield; change = true;
// 判断护盾余额 }
if (enemy.Shield.CalculateShieldOfEffect(effect, remain) > 0) if (damageReduce != 0)
{ {
WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {remain:0.##} 点{damageType}"); actualDamage -= damageReduce;
remain = 0; if (actualDamage < 0) actualDamage = 0;
}
else
{
WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {effectShield:0.##} 点{damageType},护盾已破碎!");
remain -= effectShield;
Effect[] effects2 = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect2 in effects2)
{
if (!effect2.OnShieldBroken(enemy, actor, effect, remain))
{
WriteLine($"[ {(enemy.Effects.Contains(effect2) ? enemy : actor)} ] 因护盾破碎而发动了 [ {effect2.Skill.Name} ],化解了本次伤害!");
remain = 0;
}
}
}
if (remain <= 0)
{
Effect[] effects2 = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect2 in effects2)
{
effect2.OnShieldNeutralizeDamage(enemy, actor, isMagicDamage, magicType, damage, ShieldType.Effect);
}
break;
}
} }
} }
// 如果伤害仍然大于0继续检查护盾 // 检查护盾
if (remain > 0) if (!change)
{ {
// 检查指定类型的护盾值 double remain = actualDamage;
double shield = enemy.Shield[isMagicDamage, magicType];
if (shield > 0) // 检查特效护盾
effects = [.. enemy.Shield.ShieldOfEffects.Keys];
foreach (Effect effect in effects)
{ {
shield -= remain; ShieldOfEffect soe = enemy.Shield.ShieldOfEffects[effect];
string shieldTypeString = isMagicDamage ? "魔法" : "物理"; if (soe.IsMagic == (damageType == DamageType.Magical) && (damageType == DamageType.Physical || soe.MagicType == magicType) && soe.Shield > 0)
ShieldType shieldType = isMagicDamage ? ShieldType.Magical : ShieldType.Physical;
if (shield > 0)
{ {
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {remain:0.##} 点{damageType}"); double effectShield = soe.Shield;
enemy.Shield[isMagicDamage, magicType] -= remain; // 判断护盾余额
remain = 0; if (enemy.Shield.CalculateShieldOfEffect(effect, remain) > 0)
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{ {
effect.OnShieldNeutralizeDamage(enemy, actor, isMagicDamage, magicType, damage, shieldType); WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {remain:0.##} 点{damageTypeString}");
remain = 0;
}
else
{
WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {effectShield:0.##} 点{damageTypeString},护盾已破碎!");
remain -= effectShield;
Effect[] effects2 = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect2 in effects2)
{
if (!effect2.OnShieldBroken(enemy, actor, effect, remain))
{
WriteLine($"[ {(enemy.Effects.Contains(effect2) ? enemy : actor)} ] 因护盾破碎而发动了 [ {effect2.Skill.Name} ],化解了本次伤害!");
remain = 0;
}
}
}
if (remain <= 0)
{
Effect[] effects2 = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect2 in effects2)
{
effect2.OnShieldNeutralizeDamage(enemy, actor, damageType, magicType, damage, ShieldType.Effect);
}
break;
} }
} }
else }
// 如果伤害仍然大于0继续检查护盾
if (remain > 0)
{
// 检查指定类型的护盾值
bool isMagicDamage = damageType == DamageType.Magical;
double shield = enemy.Shield[isMagicDamage, magicType];
if (shield > 0)
{ {
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {enemy.Shield[isMagicDamage, magicType]:0.##} 点{damageType}并破碎!"); shield -= remain;
remain -= enemy.Shield[isMagicDamage, magicType]; string shieldTypeString = isMagicDamage ? "魔法" : "物理";
enemy.Shield[isMagicDamage, magicType] = 0; ShieldType shieldType = isMagicDamage ? ShieldType.Magical : ShieldType.Physical;
if (isMagicDamage && enemy.Shield.TotalMagicial <= 0 || !isMagicDamage && enemy.Shield.TotalPhysical <= 0) if (shield > 0)
{ {
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {remain:0.##} 点{damageTypeString}");
enemy.Shield[isMagicDamage, magicType] -= remain;
remain = 0;
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
if (!effect.OnShieldBroken(enemy, actor, shieldType, remain)) effect.OnShieldNeutralizeDamage(enemy, actor, damageType, magicType, damage, shieldType);
}
}
else
{
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {enemy.Shield[isMagicDamage, magicType]:0.##} 点{damageTypeString}并破碎!");
remain -= enemy.Shield[isMagicDamage, magicType];
enemy.Shield[isMagicDamage, magicType] = 0;
if (isMagicDamage && enemy.Shield.TotalMagicial <= 0 || !isMagicDamage && enemy.Shield.TotalPhysical <= 0)
{
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
if (!effect.OnShieldBroken(enemy, actor, shieldType, remain))
{
WriteLine($"[ {(enemy.Effects.Contains(effect) ? enemy : actor)} ] 因护盾破碎而发动了 [ {effect.Skill.Name} ],化解了本次伤害!");
remain = 0;
}
}
}
}
}
// 检查混合护盾
if (remain > 0 && enemy.Shield.Mix > 0)
{
shield = enemy.Shield.Mix;
shield -= remain;
if (shield > 0)
{
WriteLine($"[ {enemy} ] 的混合护盾抵消了 {remain:0.##} 点{damageTypeString}");
enemy.Shield.Mix -= remain;
remain = 0;
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
effect.OnShieldNeutralizeDamage(enemy, actor, damageType, magicType, damage, ShieldType.Mix);
}
}
else
{
WriteLine($"[ {enemy} ] 的混合护盾抵消了 {enemy.Shield.Mix:0.##} 点{damageTypeString}并破碎!");
remain -= enemy.Shield.Mix;
enemy.Shield.Mix = 0;
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
if (!effect.OnShieldBroken(enemy, actor, ShieldType.Mix, remain))
{ {
WriteLine($"[ {(enemy.Effects.Contains(effect) ? enemy : actor)} ] 因护盾破碎而发动了 [ {effect.Skill.Name} ],化解了本次伤害!"); WriteLine($"[ {(enemy.Effects.Contains(effect) ? enemy : actor)} ] 因护盾破碎而发动了 [ {effect.Skill.Name} ],化解了本次伤害!");
remain = 0; remain = 0;
@ -1540,51 +1606,18 @@ namespace Milimoe.FunGame.Core.Model
} }
} }
// 检查混合护盾 actualDamage = remain;
if (remain > 0 && enemy.Shield.Mix > 0)
{
shield = enemy.Shield.Mix;
shield -= remain;
if (shield > 0)
{
WriteLine($"[ {enemy} ] 的混合护盾抵消了 {remain:0.##} 点{damageType}");
enemy.Shield.Mix -= remain;
remain = 0;
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
effect.OnShieldNeutralizeDamage(enemy, actor, isMagicDamage, magicType, damage, ShieldType.Mix);
}
}
else
{
WriteLine($"[ {enemy} ] 的混合护盾抵消了 {enemy.Shield.Mix:0.##} 点{damageType}并破碎!");
remain -= enemy.Shield.Mix;
enemy.Shield.Mix = 0;
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
if (!effect.OnShieldBroken(enemy, actor, ShieldType.Mix, remain))
{
WriteLine($"[ {(enemy.Effects.Contains(effect) ? enemy : actor)} ] 因护盾破碎而发动了 [ {effect.Skill.Name} ],化解了本次伤害!");
remain = 0;
}
}
}
}
} }
actualDamage = remain; // 统计护盾
} if (damage > actualDamage && _stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null)
{
// 统计护盾 stats.TotalShield += damage - actualDamage;
if (damage > actualDamage && _stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null) }
{
stats.TotalShield += damage - actualDamage;
} }
enemy.HP -= actualDamage; enemy.HP -= actualDamage;
WriteLine($"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageType}{shieldMsg}"); WriteLine($"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageTypeString}{shieldMsg}");
// 生命偷取,攻击者为全额 // 生命偷取,攻击者为全额
double steal = damage * actor.Lifesteal; double steal = damage * actor.Lifesteal;
@ -1618,7 +1651,7 @@ namespace Milimoe.FunGame.Core.Model
} }
// 统计伤害 // 统计伤害
CalculateCharacterDamageStatistics(actor, enemy, damage, isMagicDamage, actualDamage); CalculateCharacterDamageStatistics(actor, enemy, damage, damageType, actualDamage);
// 计算助攻 // 计算助攻
_assistDetail[actor][enemy, TotalTime] += damage; _assistDetail[actor][enemy, TotalTime] += damage;
@ -1630,12 +1663,12 @@ namespace Milimoe.FunGame.Core.Model
actualDamage = 0; actualDamage = 0;
} }
await OnDamageToEnemyAsync(actor, enemy, damage, actualDamage, isNormalAttack, isMagicDamage, magicType, damageResult); await OnDamageToEnemyAsync(actor, enemy, damage, actualDamage, isNormalAttack, damageType, magicType, damageResult);
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
effect.AfterDamageCalculation(actor, enemy, damage, actualDamage, isNormalAttack, isMagicDamage, magicType, damageResult); effect.AfterDamageCalculation(actor, enemy, damage, actualDamage, isNormalAttack, damageType, magicType, damageResult);
} }
if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy)) if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy))
@ -1680,8 +1713,14 @@ namespace Milimoe.FunGame.Core.Model
return; return;
} }
double realHeal = heal;
if (target.HP > 0 || (isDead && canRespawn)) if (target.HP > 0 || (isDead && canRespawn))
{ {
// 用于数据统计,不能是全额,溢出的部分需要扣除
if (target.HP + heal > target.MaxHP)
{
realHeal = target.MaxHP - target.HP;
}
target.HP += heal; target.HP += heal;
if (!LastRound.Heals.TryAdd(target, heal)) if (!LastRound.Heals.TryAdd(target, heal))
{ {
@ -1717,7 +1756,7 @@ namespace Milimoe.FunGame.Core.Model
// 统计数据 // 统计数据
if (_stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null) if (_stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null)
{ {
stats.TotalHeal += heal; stats.TotalHeal += realHeal;
} }
await OnHealToTargetAsync(actor, target, heal, isRespawn); await OnHealToTargetAsync(actor, target, heal, isRespawn);
@ -1928,8 +1967,8 @@ namespace Milimoe.FunGame.Core.Model
{ {
caster.CharacterState = CharacterState.Actionable; caster.CharacterState = CharacterState.Actionable;
} }
WriteLine($"[ {caster} ] 终止了 [ {st.Skill.Name} ] 的施法" + (_hardnessTimes[caster] > 3 ? $",并获得了 3 {GameplayEquilibriumConstant.InGameTime}的硬直时间的补偿。" : "。")); WriteLine($"[ {caster} ] 终止了 [ {st.Skill.Name} ] 的施法" + (_hardnessTimes[caster] > 3 && _isInRound ? $",并获得了 3 {GameplayEquilibriumConstant.InGameTime}的硬直时间的补偿。" : "。"));
if (_hardnessTimes[caster] > 3) if (_hardnessTimes[caster] > 3 && _isInRound)
{ {
AddCharacter(caster, 3, false); AddCharacter(caster, 3, false);
} }
@ -1966,16 +2005,21 @@ namespace Milimoe.FunGame.Core.Model
{ {
LastRound.Targets = [.. targets]; LastRound.Targets = [.. targets];
WriteLine($"[ {character} ] 使用了物品 [ {item.Name} ]");
item.ReduceTimesAndRemove();
if (item.IsReduceTimesAfterUse && item.RemainUseTimes == 0)
{
character.Items.Remove(item);
}
await OnCharacterUseItemAsync(character, item, targets); await OnCharacterUseItemAsync(character, item, targets);
string line = $"[ {character} ] 使用了物品 [ {item.Name} ]\r\n[ {character} ] ";
skill.OnSkillCasting(this, character, targets); skill.OnSkillCasting(this, character, targets);
skill.BeforeSkillCasted(); skill.BeforeSkillCasted();
skill.CurrentCD = skill.RealCD; skill.CurrentCD = skill.RealCD;
skill.Enable = false; skill.Enable = false;
string line = $"[ {character} ] ";
if (costMP > 0) if (costMP > 0)
{ {
character.MP -= costMP; character.MP -= costMP;
@ -2017,7 +2061,7 @@ namespace Milimoe.FunGame.Core.Model
{ {
if (pUseItem == 0 && pCastSkill == 0 && pNormalAttack == 0) if (pUseItem == 0 && pCastSkill == 0 && pNormalAttack == 0)
{ {
return CharacterActionType.None; return CharacterActionType.EndTurn;
} }
double total = pUseItem + pCastSkill + pNormalAttack; double total = pUseItem + pCastSkill + pNormalAttack;
@ -2048,7 +2092,7 @@ namespace Milimoe.FunGame.Core.Model
return CharacterActionType.NormalAttack; return CharacterActionType.NormalAttack;
} }
return CharacterActionType.None; return CharacterActionType.EndTurn;
} }
/// <summary> /// <summary>
@ -2113,7 +2157,7 @@ namespace Milimoe.FunGame.Core.Model
if (skill.SkillType == SkillType.Magic) if (skill.SkillType == SkillType.Magic)
{ {
cost = skill.RealMPCost; cost = skill.RealMPCost;
if (cost > 0 && cost <= caster.MP) if (cost >= 0 && cost <= caster.MP)
{ {
return true; return true;
} }
@ -2125,7 +2169,7 @@ namespace Milimoe.FunGame.Core.Model
else else
{ {
cost = skill.RealEPCost; cost = skill.RealEPCost;
if (cost > 0 && cost <= caster.EP) if (cost >= 0 && cost <= caster.EP)
{ {
return true; return true;
} }
@ -2158,7 +2202,7 @@ namespace Milimoe.FunGame.Core.Model
costEP = skill.RealEPCost; costEP = skill.RealEPCost;
bool isMPOk = false; bool isMPOk = false;
bool isEPOk = false; bool isEPOk = false;
if (costMP > 0 && costMP <= caster.MP) if (costMP >= 0 && costMP <= caster.MP)
{ {
isMPOk = true; isMPOk = true;
} }
@ -2167,7 +2211,7 @@ namespace Milimoe.FunGame.Core.Model
WriteLine("[ " + caster + $" ] 魔法不足!"); WriteLine("[ " + caster + $" ] 魔法不足!");
} }
costEP = skill.RealEPCost; costEP = skill.RealEPCost;
if (costEP > 0 && costEP <= caster.EP) if (costEP >= 0 && costEP <= caster.EP)
{ {
isEPOk = true; isEPOk = true;
} }
@ -2191,16 +2235,16 @@ namespace Milimoe.FunGame.Core.Model
public DamageResult CalculatePhysicalDamage(Character actor, Character enemy, bool isNormalAttack, double expectedDamage, out double finalDamage, ref int changeCount) public DamageResult CalculatePhysicalDamage(Character actor, Character enemy, bool isNormalAttack, double expectedDamage, out double finalDamage, ref int changeCount)
{ {
List<Character> characters = [actor, enemy]; List<Character> characters = [actor, enemy];
bool isMagic = false; DamageType damageType = DamageType.Physical;
MagicType magicType = MagicType.None; MagicType magicType = MagicType.None;
List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
if (changeCount < 3) if (changeCount < 3)
{ {
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
effect.AlterDamageTypeBeforeCalculation(actor, enemy, ref isNormalAttack, ref isMagic, ref magicType); effect.AlterDamageTypeBeforeCalculation(actor, enemy, ref isNormalAttack, ref damageType, ref magicType);
} }
if (isMagic) if (damageType == DamageType.Magical)
{ {
changeCount++; changeCount++;
return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage, ref changeCount); return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage, ref changeCount);
@ -2211,7 +2255,7 @@ namespace Milimoe.FunGame.Core.Model
effects = [.. actor.Effects.Union(enemy.Effects).Distinct().Where(e => e.IsInEffect)]; effects = [.. actor.Effects.Union(enemy.Effects).Distinct().Where(e => e.IsInEffect)];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, false, MagicType.None, totalDamageBonus); double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, DamageType.Physical, MagicType.None, totalDamageBonus);
totalDamageBonus[effect] = damageBonus; totalDamageBonus[effect] = damageBonus;
} }
expectedDamage += totalDamageBonus.Sum(kv => kv.Value); expectedDamage += totalDamageBonus.Sum(kv => kv.Value);
@ -2308,15 +2352,15 @@ namespace Milimoe.FunGame.Core.Model
public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage, ref int changeCount) public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage, ref int changeCount)
{ {
List<Character> characters = [actor, enemy]; List<Character> characters = [actor, enemy];
bool isMagic = true; DamageType damageType = DamageType.Magical;
List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
if (changeCount < 3) if (changeCount < 3)
{ {
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
effect.AlterDamageTypeBeforeCalculation(actor, enemy, ref isNormalAttack, ref isMagic, ref magicType); effect.AlterDamageTypeBeforeCalculation(actor, enemy, ref isNormalAttack, ref damageType, ref magicType);
} }
if (!isMagic) if (damageType == DamageType.Physical)
{ {
changeCount++; changeCount++;
return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage, ref changeCount); return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage, ref changeCount);
@ -2327,7 +2371,7 @@ namespace Milimoe.FunGame.Core.Model
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()]; effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, true, magicType, totalDamageBonus); double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, DamageType.Magical, magicType, totalDamageBonus);
totalDamageBonus[effect] = damageBonus; totalDamageBonus[effect] = damageBonus;
} }
expectedDamage += totalDamageBonus.Sum(kv => kv.Value); expectedDamage += totalDamageBonus.Sum(kv => kv.Value);
@ -2431,7 +2475,7 @@ namespace Milimoe.FunGame.Core.Model
List<Character> teammates = GetTeammates(character); List<Character> teammates = GetTeammates(character);
return teammates.Contains(target); return teammates.Contains(target);
} }
/// <summary> /// <summary>
/// 获取目标对于某个角色是否是友方的字典 /// 获取目标对于某个角色是否是友方的字典
/// </summary> /// </summary>
@ -2812,12 +2856,16 @@ namespace Milimoe.FunGame.Core.Model
/// <summary> /// <summary>
/// 计算角色的数据 /// 计算角色的数据
/// </summary> /// </summary>
public void CalculateCharacterDamageStatistics(Character character, Character characterTaken, double damage, bool isMagic, double takenDamage = -1) public void CalculateCharacterDamageStatistics(Character character, Character characterTaken, double damage, DamageType damageType, double takenDamage = -1)
{ {
if (takenDamage == -1) takenDamage = damage; if (takenDamage == -1) takenDamage = damage;
if (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null) if (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null)
{ {
if (isMagic) if (damageType == DamageType.True)
{
stats.TotalTrueDamage += damage;
}
if (damageType == DamageType.Magical)
{ {
stats.TotalMagicDamage += damage; stats.TotalMagicDamage += damage;
} }
@ -2829,7 +2877,11 @@ namespace Milimoe.FunGame.Core.Model
} }
if (_stats.TryGetValue(characterTaken, out CharacterStatistics? statsTaken) && statsTaken != null) if (_stats.TryGetValue(characterTaken, out CharacterStatistics? statsTaken) && statsTaken != null)
{ {
if (isMagic) if (damageType == DamageType.True)
{
statsTaken.TotalTakenTrueDamage = Calculation.Round2Digits(statsTaken.TotalTakenTrueDamage + takenDamage);
}
if (damageType == DamageType.Magical)
{ {
statsTaken.TotalTakenMagicDamage = Calculation.Round2Digits(statsTaken.TotalTakenMagicDamage + takenDamage); statsTaken.TotalTakenMagicDamage = Calculation.Round2Digits(statsTaken.TotalTakenMagicDamage + takenDamage);
} }
@ -3090,7 +3142,7 @@ namespace Milimoe.FunGame.Core.Model
await (HealToTarget?.Invoke(this, actor, target, heal, isRespawn) ?? Task.CompletedTask); await (HealToTarget?.Invoke(this, actor, target, heal, isRespawn) ?? Task.CompletedTask);
} }
public delegate Task DamageToEnemyEventHandler(GamingQueue queue, Character actor, Character enemy, double damage, double actualDamage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult); public delegate Task DamageToEnemyEventHandler(GamingQueue queue, Character actor, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult);
/// <summary> /// <summary>
/// 造成伤害事件 /// 造成伤害事件
/// </summary> /// </summary>
@ -3103,13 +3155,13 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="damage"></param> /// <param name="damage"></param>
/// <param name="actualDamage"></param> /// <param name="actualDamage"></param>
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="damageType"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damageResult"></param> /// <param name="damageResult"></param>
/// <returns></returns> /// <returns></returns>
protected async Task OnDamageToEnemyAsync(Character actor, Character enemy, double damage, double actualDamage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult) protected async Task OnDamageToEnemyAsync(Character actor, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult)
{ {
await (DamageToEnemy?.Invoke(this, actor, enemy, damage, actualDamage, isNormalAttack, isMagicDamage, magicType, damageResult) ?? Task.CompletedTask); await (DamageToEnemy?.Invoke(this, actor, enemy, damage, actualDamage, isNormalAttack, damageType, magicType, damageResult) ?? Task.CompletedTask);
} }
public delegate Task CharacterNormalAttackEventHandler(GamingQueue queue, Character actor, List<Character> targets); public delegate Task CharacterNormalAttackEventHandler(GamingQueue queue, Character actor, List<Character> targets);