添加更多钩子,修复一些BUG

This commit is contained in:
milimoe 2026-01-15 01:27:27 +08:00
parent 6325e9a956
commit a93f9a274e
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
8 changed files with 168 additions and 47 deletions

View File

@ -726,7 +726,7 @@ namespace Milimoe.FunGame.Core.Entity
get
{
double value = SPD / GameplayEquilibriumConstant.SPDUpperLimit + ExActionCoefficient;
return Calculation.PercentageCheck(value);
return Math.Max(0, Math.Min(value, 0.9));
}
}
@ -2210,7 +2210,8 @@ namespace Milimoe.FunGame.Core.Entity
Skill newskill = skill.Copy();
newskill.Character = this;
newskill.Level = skill.Level;
newskill.CurrentCD = 0;
newskill.CurrentCD = skill.CurrentCD;
skill.OnCharacterRespawn(newskill);
Skills.Add(newskill);
}
foreach (Item item in items)

View File

@ -435,6 +435,20 @@ namespace Milimoe.FunGame.Core.Entity
}
/// <summary>
/// 在技能释放前触发
/// </summary>
/// <param name="caster"></param>
/// <param name="skill"></param>
/// <param name="targets"></param>
/// <param name="grids"></param>
/// <param name="others"></param>
/// <returns>返回 false 将角色从目标集合中移除</returns>
public virtual bool BeforeSkillCasted(Character caster, Skill skill, List<Character> targets, List<Grid> grids, Dictionary<string, object> others)
{
return true;
}
/// <summary>
/// 在时间流逝期间应用生命/魔法回复前修改 [ 允许取消回复 ]
/// </summary>
@ -581,9 +595,10 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary>
/// <param name="actor"></param>
/// <param name="enemy"></param>
/// <param name="isNormalAttack"></param>
/// <param name="throwingBonus"></param>
/// <returns>返回 false 表示不进行暴击检定</returns>
public virtual bool BeforeCriticalCheck(Character actor, Character enemy, ref double throwingBonus)
public virtual bool BeforeCriticalCheck(Character actor, Character enemy, bool isNormalAttack, ref double throwingBonus)
{
return true;
}
@ -880,6 +895,17 @@ namespace Milimoe.FunGame.Core.Entity
return true;
}
/// <summary>
/// 在角色开始行动时触发
/// </summary>
/// <param name="actor"></param>
/// <param name="dp"></param>
/// <param name="type"></param>
public virtual void OnCharacterActionStart(Character actor, DecisionPoints dp, CharacterActionType type)
{
}
/// <summary>
/// 在角色行动后触发
/// </summary>

View File

@ -505,7 +505,7 @@ namespace Milimoe.FunGame.Core.Entity
if (IsNonDirectional || CanSelectTargetRange < 0 || GamingQueue?.Map is not GameMap map)
{
return [];
return [.. selected];
}
foreach (Character selectedCharacter in selected)
@ -652,6 +652,19 @@ namespace Milimoe.FunGame.Core.Entity
public void OnSkillCasted(IGamingQueue queue, Character caster, List<Character> targets, List<Grid> grids)
{
GamingQueue = queue;
Character[] characters = [caster, .. targets];
foreach (Character target in characters)
{
Effect[] effects = [.. target.Effects.Where(e => e.IsInEffect)];
foreach (Effect e in effects)
{
e.GamingQueue = GamingQueue;
if (!e.BeforeSkillCasted(caster, this, targets, grids, Values))
{
targets.Remove(target);
}
}
}
foreach (Effect e in Effects)
{
e.GamingQueue = GamingQueue;
@ -691,6 +704,16 @@ namespace Milimoe.FunGame.Core.Entity
return [];
}
/// <summary>
/// 在复活时,因为复活是重新构建角色,如果需要继承死亡角色的技能数据,可以重写此方法并设置相关属性
/// </summary>
/// <param name="newSkill"></param>
/// <returns></returns>
public virtual void OnCharacterRespawn(Skill newSkill)
{
}
/// <summary>
/// 返回技能的详细说明
/// </summary>

View File

@ -55,6 +55,11 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// </summary>
public Dictionary<Character, CharacterStatistics> CharacterStatistics { get; }
/// <summary>
/// 助攻记录
/// </summary>
public Dictionary<Character, AssistDetail> AssistDetails { get; }
/// <summary>
/// 角色的决策点
/// </summary>

11
Model/DeathRelation.cs Normal file
View File

@ -0,0 +1,11 @@
using Milimoe.FunGame.Core.Entity;
namespace Milimoe.FunGame.Core.Model
{
public class DeathRelation(Character death, Character? killer, params Character[] assists)
{
public Character Death { get; set; } = death;
public Character? Killer { get; set; } = killer;
public Character[] Assists { get; set; } = assists;
}
}

View File

@ -67,6 +67,11 @@ namespace Milimoe.FunGame.Core.Model
/// </summary>
public Dictionary<Character, CharacterStatistics> CharacterStatistics => _stats;
/// <summary>
/// 助攻记录
/// </summary>
public Dictionary<Character, AssistDetail> AssistDetails => _assistDetail;
/// <summary>
/// 游戏运行的时间
/// </summary>
@ -163,6 +168,11 @@ namespace Milimoe.FunGame.Core.Model
/// </summary>
public Dictionary<Character, DecisionPoints> CharacterDecisionPoints => _decisionPoints;
/// <summary>
/// 游戏结束标识
/// </summary>
public bool GameOver => _isGameEnd;
#endregion
#region
@ -260,7 +270,7 @@ namespace Milimoe.FunGame.Core.Model
/// <summary>
/// 当前回合死亡角色和参与击杀的人
/// </summary>
protected readonly Dictionary<Character, Character[]> _roundDeaths = [];
protected readonly List<DeathRelation> _roundDeaths = [];
/// <summary>
/// 回合奖励
@ -910,7 +920,8 @@ namespace Milimoe.FunGame.Core.Model
}
// 减少复活倒计时
foreach (Character character in _respawnCountdown.Keys)
Character[] willRespawns = [.. _respawnCountdown.Keys];
foreach (Character character in willRespawns)
{
_respawnCountdown[character] = Calculation.Round2Digits(_respawnCountdown[character] - timeToReduce);
if (_respawnCountdown[character] <= 0)
@ -919,6 +930,8 @@ namespace Milimoe.FunGame.Core.Model
}
}
ProcessCharacterDeath();
WriteLine("\r\n");
return timeToReduce;
@ -1276,6 +1289,12 @@ namespace Milimoe.FunGame.Core.Model
int costDP = dp.GetActionPointCost(type);
effects = [.. character.Effects.Where(e => e.IsInEffect)];
foreach (Effect effect in effects)
{
effect.OnCharacterActionStart(character, dp, type);
}
if (type == CharacterActionType.Move)
{
if (_map != null)
@ -1800,7 +1819,7 @@ namespace Milimoe.FunGame.Core.Model
}
}
if (character.CharacterState != CharacterState.Casting)
if (character.CharacterState != CharacterState.Casting && dp.ActionsHardnessTime.Count > 0)
{
baseTime = dp.ActionsTaken > 1 ? (dp.ActionsHardnessTime.Max() + dp.ActionsTaken) : dp.ActionsHardnessTime.Max();
}
@ -1819,7 +1838,7 @@ namespace Milimoe.FunGame.Core.Model
}
// 统一在回合结束时处理角色的死亡
ProcessCharacterDeath(character);
ProcessCharacterDeath();
// 移除回合奖励
RemoveRoundRewards(character, rewards);
@ -1899,29 +1918,20 @@ namespace Milimoe.FunGame.Core.Model
/// <summary>
/// 处理角色死亡
/// </summary>
/// <param name="character"></param>
protected void ProcessCharacterDeath(Character character)
protected void ProcessCharacterDeath()
{
foreach (Character death in _roundDeaths.Keys)
foreach (DeathRelation dr in _roundDeaths)
{
Character[] assists = _roundDeaths[death];
Character death = dr.Death;
Character? killer = dr.Killer;
Character[] assists = dr.Assists;
if (!OnCharacterDeathEvent(character, death, assists))
if (!_isGameEnd)
{
continue;
AfterDeathCalculation(death, killer, assists);
}
// 给所有角色的特效广播角色死亡结算
List<Effect> effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect))];
foreach (Effect effect in effects)
{
effect.AfterDeathCalculation(death, character, _continuousKilling, _earnedMoney, assists);
}
// 将死者移出队列
_queue.Remove(death);
AfterDeathCalculation(death, character, assists);
}
_roundDeaths.Clear();
}
/// <summary>
@ -1967,16 +1977,23 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="death"></param>
/// <param name="killer"></param>
/// <param name="assists"></param>
protected virtual void AfterDeathCalculation(Character death, Character killer, Character[] assists)
protected virtual void AfterDeathCalculation(Character death, Character? killer, Character[] assists)
{
if (!_queue.Any(c => c != killer && c.Master != killer && killer.Master != c))
if (!_queue.Any(c => c != killer && c.Master != killer && killer?.Master != c))
{
// 没有其他的角色了,游戏结束
WriteLine("[ " + killer + " ] 是胜利者。");
_queue.Remove(killer);
_eliminated.Add(killer);
if (killer != null)
{
WriteLine("[ " + killer + " ] 是胜利者。");
_queue.Remove(killer);
_eliminated.Add(killer);
OnGameEndEvent(killer);
}
else
{
WriteLine("游戏结束。");
}
_isGameEnd = true;
OnGameEndEvent(killer);
}
}
@ -2389,7 +2406,6 @@ namespace Milimoe.FunGame.Core.Model
if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy))
{
LastRound.HasKill = true;
_roundDeaths.Add(enemy, []);
DeathCalculation(actor, enemy);
}
}
@ -2401,6 +2417,9 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="death"></param>
public void DeathCalculation(Character killer, Character death)
{
DeathRelation dr = new(death, killer);
_roundDeaths.Add(dr);
if (killer == death)
{
if (!OnDeathCalculationEvent(killer, death))
@ -2453,6 +2472,9 @@ namespace Milimoe.FunGame.Core.Model
Character[] assists = [.. _assistDetail.Keys.Where(c => c != death &&
((TotalTime - _assistDetail[c].GetLastTime(death) <= 30) || (TotalTime - _assistDetail[c].GetNotDamageAssistLastTime(killer) <= 20)))];
// 获取队友列表
Character[] teammates = [.. GetTeammates(killer)];
// 获取贡献百分比 以伤害为主,非伤害助攻贡献不足 10% 的按 10% 计算
double minPercentage = 0.1;
Dictionary<Character, double> assistPercentage = _assistDetail.Keys.Where(assists.Contains).ToDictionary(c => c,
@ -2509,7 +2531,10 @@ namespace Milimoe.FunGame.Core.Model
cmoney += calDiff(death, assist);
if (!_earnedMoney.TryAdd(assist, cmoney)) _earnedMoney[assist] += cmoney;
assist.User.Inventory.Credits += cmoney;
_stats[assist].Assists += 1;
if (teammates.Length == 0 || (teammates.Length > 0 && teammates.Contains(assist)))
{
_stats[assist].Assists += 1;
}
if (!LastRound.Assists.Contains(assist)) LastRound.Assists.Add(assist);
}
else
@ -2544,7 +2569,7 @@ namespace Milimoe.FunGame.Core.Model
}
WriteLine(msg);
}
_roundDeaths[death] = assists;
dr.Assists = assists;
if (FirstKiller is null)
{
@ -2586,6 +2611,17 @@ namespace Milimoe.FunGame.Core.Model
}
DealWithCharacterDied(killer, death);
// 给所有角色的特效广播角色死亡结算
List<Effect> effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Union(killer.Effects).Distinct()];
foreach (Effect effect in effects)
{
effect.AfterDeathCalculation(death, killer, _continuousKilling, _earnedMoney, assists);
}
// 将死者移出队列
_queue.Remove(death);
OnCharacterDeathEvent(death, killer, assists);
}
/// <summary>
@ -2630,6 +2666,12 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="triggerEffects"></param>
public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false, bool triggerEffects = true)
{
// 死人怎么能对自己治疗呢?
if (actor.HP <= 0)
{
return;
}
if (target.HP == target.MaxHP)
{
return;
@ -3287,7 +3329,7 @@ namespace Milimoe.FunGame.Core.Model
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
if (!effect.BeforeCriticalCheck(actor, enemy, ref throwingBonus))
if (!effect.BeforeCriticalCheck(actor, enemy, isNormalAttack, ref throwingBonus))
{
checkCritical = false;
}
@ -3414,7 +3456,7 @@ namespace Milimoe.FunGame.Core.Model
effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect effect in effects)
{
if (!effect.BeforeCriticalCheck(actor, enemy, ref throwingBonus))
if (!effect.BeforeCriticalCheck(actor, enemy, isNormalAttack, ref throwingBonus))
{
checkCritical = false;
}
@ -4189,7 +4231,7 @@ namespace Milimoe.FunGame.Core.Model
{
stats.TotalTrueDamage += damage;
}
if (damageType == DamageType.Magical)
else if (damageType == DamageType.Magical)
{
stats.TotalMagicDamage += damage;
}
@ -4205,7 +4247,7 @@ namespace Milimoe.FunGame.Core.Model
{
statsTaken.TotalTakenTrueDamage = Calculation.Round2Digits(statsTaken.TotalTakenTrueDamage + takenDamage);
}
if (damageType == DamageType.Magical)
else if (damageType == DamageType.Magical)
{
statsTaken.TotalTakenMagicDamage = Calculation.Round2Digits(statsTaken.TotalTakenMagicDamage + takenDamage);
}
@ -4513,7 +4555,7 @@ namespace Milimoe.FunGame.Core.Model
return DeathCalculationByTeammateEvent?.Invoke(this, killer, death) ?? true;
}
public delegate bool CharacterDeathEventHandler(GamingQueue queue, Character current, Character death, Character[] assists);
public delegate bool CharacterDeathEventHandler(GamingQueue queue, Character death, Character? killer, Character[] assists);
/// <summary>
/// 角色死亡事件,此事件位于 <see cref="DeathCalculation"/> 之后
/// </summary>
@ -4521,13 +4563,13 @@ namespace Milimoe.FunGame.Core.Model
/// <summary>
/// 角色死亡事件,此事件位于 <see cref="DeathCalculation"/> 之后
/// </summary>
/// <param name="current"></param>
/// <param name="death"></param>
/// <param name="killer"></param>
/// <param name="assists"></param>
/// <returns></returns>
protected bool OnCharacterDeathEvent(Character current, Character death, Character[] assists)
protected bool OnCharacterDeathEvent(Character death, Character? killer, Character[] assists)
{
return CharacterDeathEvent?.Invoke(this, current, death, assists) ?? true;
return CharacterDeathEvent?.Invoke(this, death, killer, assists) ?? true;
}
public delegate void HealToTargetEventHandler(GamingQueue queue, Character actor, Character target, double heal, bool isRespawn);

View File

@ -15,7 +15,7 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="killer"></param>
/// <param name="assists"></param>
/// <returns></returns>
protected override void AfterDeathCalculation(Character death, Character killer, Character[] assists)
protected override void AfterDeathCalculation(Character death, Character? killer, Character[] assists)
{
if (MaxRespawnTimes != 0 && MaxScoreToWin > 0)
{
@ -23,13 +23,13 @@ namespace Milimoe.FunGame.Core.Model
.Select(kv => $"[ {kv.Key} ] {kv.Value.Kills} 分"))}\r\n剩余存活人数{_queue.Count}");
}
if (!_queue.Any(c => c != killer && c.Master != killer && killer.Master != c))
if (!_queue.Any(c => c != killer && c.Master != killer && killer?.Master != c))
{
// 没有其他的角色了,游戏结束
EndGameInfo(killer);
}
if (MaxScoreToWin > 0 && _stats[killer].Kills >= MaxScoreToWin)
if (MaxScoreToWin > 0 && killer != null && _stats[killer].Kills >= MaxScoreToWin)
{
EndGameInfo(killer);
return;
@ -58,8 +58,14 @@ namespace Milimoe.FunGame.Core.Model
/// <summary>
/// 游戏结束信息
/// </summary>
public void EndGameInfo(Character winner)
public void EndGameInfo(Character? winner)
{
winner ??= _queue.FirstOrDefault();
if (winner is null)
{
WriteLine("游戏结束。");
return;
}
WriteLine("[ " + winner + " ] 是胜利者。");
foreach (Character character in _stats.OrderBy(kv => kv.Value.Kills)
.ThenByDescending(kv => kv.Value.Deaths)

View File

@ -159,8 +159,15 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="killer"></param>
/// <param name="assists"></param>
/// <returns></returns>
protected override void AfterDeathCalculation(Character death, Character killer, Character[] assists)
protected override void AfterDeathCalculation(Character death, Character? killer, Character[] assists)
{
killer ??= _queue.FirstOrDefault();
if (killer is null)
{
WriteLine("游戏结束。");
return;
}
Team? killTeam = GetTeam(killer);
Team? deathTeam = GetTeam(death);