底层支持真实伤害 (#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,11 +129,15 @@ namespace Milimoe.FunGame.Core.Entity
}
set
{
int past = _Level;
_Level = Math.Min(Math.Max(1, value), GameplayEquilibriumConstant.MaxLevel);
if (past != _Level)
{
OnAttributeChanged();
Recovery();
}
}
}
/// <summary>
/// 经验值
@ -900,8 +904,8 @@ namespace Milimoe.FunGame.Core.Entity
{
if (time > 0)
{
HP = Math.Min(MaxHP, HP + HR * time);
MP = Math.Min(MaxMP, MP + MR * time);
HP += HR * time;
MP += MR * time;
if (EP != -1) this.EP = EP;
}
}
@ -914,12 +918,18 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="pastMaxHP"></param>
/// <param name="pastMaxMP"></param>
public void Recovery(double pastHP, double pastMP, double pastMaxHP, double pastMaxMP)
{
if (pastHP > 0 && pastMaxHP > 0)
{
double pHP = pastHP / pastMaxHP;
double pMP = pastMP / pastMaxMP;
HP = MaxHP * pHP;
}
if (pastMP > 0 && pastMaxMP > 0)
{
double pMP = pastMP / pastMaxMP;
MP = MaxMP * pMP;
}
}
/// <summary>
/// 为角色装备物品必须使用此方法而不是自己去给EquipSlot里的物品赋值<para/>
@ -1147,6 +1157,32 @@ namespace Milimoe.FunGame.Core.Entity
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>

View File

@ -218,9 +218,9 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="character"></param>
/// <param name="enemy"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></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="damage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="totalDamageBonus"></param>
/// <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;
}
@ -248,13 +248,13 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="enemy"></param>
/// <param name="damage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="damageResult"></param>
/// <param name="isEvaded"></param>
/// <param name="totalDamageBonus"></param>
/// <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;
}
@ -335,7 +335,7 @@ namespace Milimoe.FunGame.Core.Entity
}
/// <summary>
/// 对目标触发技能效果
/// 对目标触发技能效果(局外)
/// </summary>
/// <param name="user"></param>
/// <param name="targets"></param>
@ -363,10 +363,10 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="damage"></param>
/// <param name="actualDamage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></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>
/// <param name="character"></param>
/// <param name="attacker"></param>
/// <param name="isMagic"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="damage"></param>
/// <param name="damageReduce"></param>
/// <param name="message"></param>
/// <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;
}
@ -672,11 +672,11 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary>
/// <param name="character"></param>
/// <param name="attacker"></param>
/// <param name="isMagic"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="damage"></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="enemy"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagic"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="damage"></param>
/// <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;
}
@ -740,16 +740,21 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary>
/// <param name="actor"></param>
/// <param name="enemy"></param>
/// <param name="isMagic"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="expectedDamage"></param>
/// <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;
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);
GamingQueue.DamageToEnemyAsync(actor, enemy, damage, false, isMagic, magicType, result);
DamageResult result = DamageResult.Normal;
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;
}

View File

@ -181,7 +181,7 @@ namespace Milimoe.FunGame.Core.Entity
double expected = Damage;
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);
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 TotalPhysicalDamage { 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 TotalTakenPhysicalDamage { 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 AvgPhysicalDamage { 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 AvgTakenPhysicalDamage { 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 AvgHeal { get; set; } = 0;
public double TotalShield { get; set; } = 0;

View File

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

View File

@ -11,7 +11,7 @@ namespace Milimoe.FunGame.Core.Entity
public Dictionary<long, double> DamageStats { get; } = [];
public Dictionary<long, double> PhysicalDamageStats { get; } = [];
public Dictionary<long, double> MagicDamageStats { get; } = [];
public Dictionary<long, double> RealDamageStats { get; } = [];
public Dictionary<long, double> TrueDamageStats { get; } = [];
public Dictionary<long, double> AvgDamageStats
{
get
@ -66,7 +66,7 @@ namespace Milimoe.FunGame.Core.Entity
return avgdamage;
}
}
public Dictionary<long, double> AvgRealDamageStats
public Dictionary<long, double> AvgTrueDamageStats
{
get
{
@ -75,9 +75,9 @@ namespace Milimoe.FunGame.Core.Entity
{
long plays = Plays[key];
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));
}

View File

@ -79,10 +79,10 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="enemy"></param>
/// <param name="damage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></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>
/// 治疗一个目标
@ -198,5 +198,15 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="isPercentage">是否是百分比</param>
/// <param name="isCheckProtected">是否使用插队保护机制</param>
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

View File

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

View File

@ -226,6 +226,11 @@ namespace Milimoe.FunGame.Core.Model
/// </summary>
protected bool _isGameEnd = false;
/// <summary>
/// 是否在回合内
/// </summary>
protected bool _isInRound = false;
#endregion
#region
@ -537,7 +542,8 @@ namespace Milimoe.FunGame.Core.Model
TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce);
WriteLine("时间流逝:" + timeToReduce);
foreach (Character character in _queue)
Character[] characters = [.. _queue];
foreach (Character character in characters)
{
// 减少所有角色的硬直时间
_hardnessTimes[character] = Calculation.Round2Digits(_hardnessTimes[character] - timeToReduce);
@ -635,15 +641,17 @@ namespace Milimoe.FunGame.Core.Model
if (effect.Durative)
{
effect.RemainDuration -= timeToReduce;
if (effect.RemainDuration <= 0)
if (effect.RemainDuration < timeToReduce)
{
// 移除特效前也完成剩余时间内的效果
effect.OnTimeElapsed(character, effect.RemainDuration);
effect.RemainDuration = 0;
character.Effects.Remove(effect);
effect.OnEffectLost(character);
}
else
{
effect.RemainDuration -= timeToReduce;
effect.OnTimeElapsed(character, timeToReduce);
}
}
@ -688,11 +696,13 @@ namespace Milimoe.FunGame.Core.Model
/// <returns>是否结束游戏</returns>
public async Task<bool> ProcessTurnAsync(Character character)
{
_isInRound = true;
LastRound.Actor = character;
_roundDeaths.Clear();
if (!await BeforeTurnAsync(character))
{
_isInRound = false;
return _isGameEnd;
}
@ -723,6 +733,7 @@ namespace Milimoe.FunGame.Core.Model
// 如果事件全程接管回合操作,需要注意触发特效
if (!await OnTurnStartAsync(character, enemys, teammates, skills, items))
{
_isInRound = false;
return _isGameEnd;
}
@ -806,7 +817,9 @@ namespace Milimoe.FunGame.Core.Model
}
else if (character.CharacterState == CharacterState.ActionRestricted)
{
// 行动受限,只能使用特殊物品
// 行动受限,只能使用消耗品
items = [.. items.Where(i => i.ItemType == ItemType.Consumable)];
canUseItem = items.Count > 0;
if (canUseItem)
{
pCastSkill = 0;
@ -1131,7 +1144,7 @@ namespace Milimoe.FunGame.Core.Model
{
decided = true;
LastRound.Item = item;
baseTime = skill.RealHardnessTime;
baseTime = skill.RealHardnessTime > 0 ? skill.RealHardnessTime : 5;
effects = [.. character.Effects.Where(e => e.IsInEffect)];
foreach (Effect effect in effects)
{
@ -1148,7 +1161,7 @@ namespace Milimoe.FunGame.Core.Model
character.CharacterState == CharacterState.BattleRestricted)
{
baseTime += 5;
WriteLine($"角色 [ {character} ] 状态为:{CharacterSet.GetCharacterState(character.CharacterState)},放弃行动将额外获得 5 {GameplayEquilibriumConstant.InGameTime}硬直时间!");
WriteLine($"[ {character} ] {CharacterSet.GetCharacterState(character.CharacterState)},放弃行动将额外获得 5 {GameplayEquilibriumConstant.InGameTime}硬直时间!");
}
decided = true;
WriteLine($"[ {character} ] 结束了回合!");
@ -1185,6 +1198,7 @@ namespace Milimoe.FunGame.Core.Model
await AfterTurnAsync(character);
_isInRound = false;
return _isGameEnd;
}
@ -1242,6 +1256,7 @@ namespace Milimoe.FunGame.Core.Model
await AfterTurnAsync(character);
WriteLine("");
_isInRound = false;
return _isGameEnd;
}
@ -1348,10 +1363,10 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="enemy"></param>
/// <param name="damage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></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)
@ -1368,11 +1383,16 @@ namespace Milimoe.FunGame.Core.Model
List<Character> characters = [actor, enemy];
bool isEvaded = damageResult == DamageResult.Evaded;
List<Effect> effects = [];
// 真实伤害跳过伤害加成区间
if (damageType != DamageType.True)
{
Dictionary<Effect, double> totalDamageBonus = [];
List<Effect> 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)
{
double damageBonus = effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult, ref isEvaded, totalDamageBonus);
double damageBonus = effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, damageType, magicType, damageResult, ref isEvaded, totalDamageBonus);
totalDamageBonus[effect] = damageBonus;
if (isEvaded)
{
@ -1380,16 +1400,21 @@ namespace Milimoe.FunGame.Core.Model
}
}
damage += totalDamageBonus.Sum(kv => kv.Value);
}
double actualDamage = damage;
// 闪避了就没伤害了
if (damageResult != DamageResult.Evaded)
{
// 开始计算伤害免疫
bool isImmune = false;
// 真实伤害跳过免疫
if (damageType != DamageType.True)
{
// 此变量为是否无视免疫
bool ignore = false;
// 技能免疫无法免疫普通攻击,但是魔法免疫和物理免疫可以
bool isImmune = (isNormalAttack && (enemy.ImmuneType == ImmuneType.All || enemy.ImmuneType == ImmuneType.Physical || enemy.ImmuneType == ImmuneType.Magical)) ||
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)
{
@ -1399,16 +1424,16 @@ namespace Milimoe.FunGame.Core.Model
if (isNormalAttack)
{
if (actor.NormalAttack.IgnoreImmune == ImmuneType.All ||
(!isMagicDamage && actor.NormalAttack.IgnoreImmune == ImmuneType.Physical) ||
(isMagicDamage && actor.NormalAttack.IgnoreImmune == ImmuneType.Magical) ||
!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, isMagicDamage, magicType, damage))
(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
{
if (!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, isMagicDamage, magicType, damage))
if (!effect.OnDamageImmuneCheck(actor, enemy, isNormalAttack, damageType, magicType, damage))
{
ignore = true;
}
@ -1430,20 +1455,26 @@ namespace Milimoe.FunGame.Core.Model
WriteLine($"[ {enemy} ] 免疫了此伤害!");
actualDamage = 0;
}
else
}
// 继续计算伤害
if (!isImmune)
{
if (damage < 0) damage = 0;
string damageType = isMagicDamage ? CharacterSet.GetMagicDamageName(magicType) : "物理伤害";
string damageTypeString = CharacterSet.GetDamageTypeName(damageType, magicType);
string shieldMsg = "";
// 真实伤害跳过护盾结算
if (damageType != DamageType.True)
{
// 在护盾结算前,特效可以有自己的逻辑
bool change = false;
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))
if (!effect.BeforeShieldCalculation(enemy, actor, damageType, magicType, damage, ref damageReduce, ref shieldMsg))
{
change = true;
}
@ -1464,18 +1495,18 @@ namespace Milimoe.FunGame.Core.Model
foreach (Effect effect in effects)
{
ShieldOfEffect soe = enemy.Shield.ShieldOfEffects[effect];
if (soe.IsMagic == isMagicDamage && (!isMagicDamage || soe.MagicType == magicType) && soe.Shield > 0)
if (soe.IsMagic == (damageType == DamageType.Magical) && (damageType == DamageType.Physical || soe.MagicType == magicType) && soe.Shield > 0)
{
double effectShield = soe.Shield;
// 判断护盾余额
if (enemy.Shield.CalculateShieldOfEffect(effect, remain) > 0)
{
WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {remain:0.##} 点{damageType}");
WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {remain:0.##} 点{damageTypeString}");
remain = 0;
}
else
{
WriteLine($"[ {enemy} ] 发动了 [ {effect.Skill.Name} ] 的护盾效果,抵消了 {effectShield:0.##} 点{damageType},护盾已破碎!");
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)
@ -1492,7 +1523,7 @@ namespace Milimoe.FunGame.Core.Model
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);
effect2.OnShieldNeutralizeDamage(enemy, actor, damageType, magicType, damage, ShieldType.Effect);
}
break;
}
@ -1503,6 +1534,7 @@ namespace Milimoe.FunGame.Core.Model
if (remain > 0)
{
// 检查指定类型的护盾值
bool isMagicDamage = damageType == DamageType.Magical;
double shield = enemy.Shield[isMagicDamage, magicType];
if (shield > 0)
{
@ -1511,18 +1543,18 @@ namespace Milimoe.FunGame.Core.Model
ShieldType shieldType = isMagicDamage ? ShieldType.Magical : ShieldType.Physical;
if (shield > 0)
{
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {remain:0.##} 点{damageType}");
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {remain:0.##} 点{damageTypeString}");
enemy.Shield[isMagicDamage, magicType] -= 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);
effect.OnShieldNeutralizeDamage(enemy, actor, damageType, magicType, damage, shieldType);
}
}
else
{
WriteLine($"[ {enemy} ] 的{shieldTypeString}护盾抵消了 {enemy.Shield[isMagicDamage, magicType]:0.##} 点{damageType}并破碎!");
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)
@ -1547,18 +1579,18 @@ namespace Milimoe.FunGame.Core.Model
shield -= remain;
if (shield > 0)
{
WriteLine($"[ {enemy} ] 的混合护盾抵消了 {remain:0.##} 点{damageType}");
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, isMagicDamage, magicType, damage, ShieldType.Mix);
effect.OnShieldNeutralizeDamage(enemy, actor, damageType, magicType, damage, ShieldType.Mix);
}
}
else
{
WriteLine($"[ {enemy} ] 的混合护盾抵消了 {enemy.Shield.Mix:0.##} 点{damageType}并破碎!");
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()];
@ -1582,9 +1614,10 @@ namespace Milimoe.FunGame.Core.Model
{
stats.TotalShield += damage - actualDamage;
}
}
enemy.HP -= actualDamage;
WriteLine($"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageType}{shieldMsg}");
WriteLine($"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageTypeString}{shieldMsg}");
// 生命偷取,攻击者为全额
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;
@ -1630,12 +1663,12 @@ namespace Milimoe.FunGame.Core.Model
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()];
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))
@ -1680,8 +1713,14 @@ namespace Milimoe.FunGame.Core.Model
return;
}
double realHeal = heal;
if (target.HP > 0 || (isDead && canRespawn))
{
// 用于数据统计,不能是全额,溢出的部分需要扣除
if (target.HP + heal > target.MaxHP)
{
realHeal = target.MaxHP - target.HP;
}
target.HP += 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)
{
stats.TotalHeal += heal;
stats.TotalHeal += realHeal;
}
await OnHealToTargetAsync(actor, target, heal, isRespawn);
@ -1928,8 +1967,8 @@ namespace Milimoe.FunGame.Core.Model
{
caster.CharacterState = CharacterState.Actionable;
}
WriteLine($"[ {caster} ] 终止了 [ {st.Skill.Name} ] 的施法" + (_hardnessTimes[caster] > 3 ? $",并获得了 3 {GameplayEquilibriumConstant.InGameTime}的硬直时间的补偿。" : "。"));
if (_hardnessTimes[caster] > 3)
WriteLine($"[ {caster} ] 终止了 [ {st.Skill.Name} ] 的施法" + (_hardnessTimes[caster] > 3 && _isInRound ? $",并获得了 3 {GameplayEquilibriumConstant.InGameTime}的硬直时间的补偿。" : "。"));
if (_hardnessTimes[caster] > 3 && _isInRound)
{
AddCharacter(caster, 3, false);
}
@ -1966,16 +2005,21 @@ namespace Milimoe.FunGame.Core.Model
{
LastRound.Targets = [.. targets];
WriteLine($"[ {character} ] 使用了物品 [ {item.Name} ]");
item.ReduceTimesAndRemove();
if (item.IsReduceTimesAfterUse && item.RemainUseTimes == 0)
{
character.Items.Remove(item);
}
await OnCharacterUseItemAsync(character, item, targets);
string line = $"[ {character} ] 使用了物品 [ {item.Name} ]\r\n[ {character} ] ";
skill.OnSkillCasting(this, character, targets);
skill.BeforeSkillCasted();
skill.CurrentCD = skill.RealCD;
skill.Enable = false;
string line = $"[ {character} ] ";
if (costMP > 0)
{
character.MP -= costMP;
@ -2017,7 +2061,7 @@ namespace Milimoe.FunGame.Core.Model
{
if (pUseItem == 0 && pCastSkill == 0 && pNormalAttack == 0)
{
return CharacterActionType.None;
return CharacterActionType.EndTurn;
}
double total = pUseItem + pCastSkill + pNormalAttack;
@ -2048,7 +2092,7 @@ namespace Milimoe.FunGame.Core.Model
return CharacterActionType.NormalAttack;
}
return CharacterActionType.None;
return CharacterActionType.EndTurn;
}
/// <summary>
@ -2113,7 +2157,7 @@ namespace Milimoe.FunGame.Core.Model
if (skill.SkillType == SkillType.Magic)
{
cost = skill.RealMPCost;
if (cost > 0 && cost <= caster.MP)
if (cost >= 0 && cost <= caster.MP)
{
return true;
}
@ -2125,7 +2169,7 @@ namespace Milimoe.FunGame.Core.Model
else
{
cost = skill.RealEPCost;
if (cost > 0 && cost <= caster.EP)
if (cost >= 0 && cost <= caster.EP)
{
return true;
}
@ -2158,7 +2202,7 @@ namespace Milimoe.FunGame.Core.Model
costEP = skill.RealEPCost;
bool isMPOk = false;
bool isEPOk = false;
if (costMP > 0 && costMP <= caster.MP)
if (costMP >= 0 && costMP <= caster.MP)
{
isMPOk = true;
}
@ -2167,7 +2211,7 @@ namespace Milimoe.FunGame.Core.Model
WriteLine("[ " + caster + $" ] 魔法不足!");
}
costEP = skill.RealEPCost;
if (costEP > 0 && costEP <= caster.EP)
if (costEP >= 0 && costEP <= caster.EP)
{
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)
{
List<Character> characters = [actor, enemy];
bool isMagic = false;
DamageType damageType = DamageType.Physical;
MagicType magicType = MagicType.None;
List<Effect> effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
if (changeCount < 3)
{
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++;
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)];
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;
}
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)
{
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()];
if (changeCount < 3)
{
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++;
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()];
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;
}
expectedDamage += totalDamageBonus.Sum(kv => kv.Value);
@ -2812,12 +2856,16 @@ namespace Milimoe.FunGame.Core.Model
/// <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 (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null)
{
if (isMagic)
if (damageType == DamageType.True)
{
stats.TotalTrueDamage += damage;
}
if (damageType == DamageType.Magical)
{
stats.TotalMagicDamage += damage;
}
@ -2829,7 +2877,11 @@ namespace Milimoe.FunGame.Core.Model
}
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);
}
@ -3090,7 +3142,7 @@ namespace Milimoe.FunGame.Core.Model
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>
@ -3103,13 +3155,13 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="damage"></param>
/// <param name="actualDamage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="damageType"></param>
/// <param name="magicType"></param>
/// <param name="damageResult"></param>
/// <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);