FunGame-Core/Api/Utility/ActionQueue.cs
milimoe 3d02cb3db3
添加行动顺序表和角色技能操作 (#87)
* 添加行动顺序表和角色技能操作

* 添加更多内容(特效的设计接口等)

* 添加爆发技插队和插队保护机制
2024-09-09 01:45:46 +08:00

997 lines
40 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Api.Utility
{
/// <summary>
/// 行动顺序表
/// </summary>
public class ActionQueue
{
/// <summary>
/// 用于文本输出
/// </summary>
public Action<string> WriteLine { get; }
/// <summary>
/// 当前的行动顺序
/// </summary>
protected readonly List<Character> _queue = [];
/// <summary>
/// 当前已死亡的角色顺序(第一个是最早死的)
/// </summary>
protected readonly List<Character> _eliminated = [];
/// <summary>
/// 硬直时间表
/// </summary>
protected readonly Dictionary<Character, double> _hardnessTimes = [];
/// <summary>
/// 角色正在吟唱的魔法
/// </summary>
protected readonly Dictionary<Character, Skill> _castingSkills = [];
/// <summary>
/// 角色预释放的爆发技
/// </summary>
protected readonly Dictionary<Character, Skill> _castingSuperSkills = [];
/// <summary>
/// 角色目前赚取的金钱
/// </summary>
protected readonly Dictionary<Character, double> _earnedMoney = [];
/// <summary>
/// 角色目前的连杀数
/// </summary>
protected readonly Dictionary<Character, int> _continuousKilling = [];
/// <summary>
/// 角色被插队次数
/// </summary>
protected readonly Dictionary<Character, int> _cutCount = [];
/// <summary>
/// 游戏是否结束
/// </summary>
protected bool _isGameEnd = false;
/// <summary>
/// 新建一个行动顺序表
/// </summary>
/// <param name="characters">参与本次游戏的角色列表</param>
/// <param name="writer">用于文本输出</param>
public ActionQueue(List<Character> characters, Action<string>? writer = null)
{
if (writer != null)
{
WriteLine = writer;
}
WriteLine ??= new Action<string>(Console.WriteLine);
// 初始排序:按速度排序
List<IGrouping<double, Character>> groupedBySpeed = [.. characters
.GroupBy(c => c.SPD)
.OrderByDescending(g => g.Key)];
Random random = new();
foreach (IGrouping<double, Character> group in groupedBySpeed)
{
if (group.Count() == 1)
{
// 如果只有一个角色,直接加入队列
AddCharacter(group.First(), Calculation.Round2Digits(_queue.Count * 0.1), false);
}
else
{
// 如果有多个角色,进行先行决定
List<Character> sortedList = [.. group];
while (sortedList.Count > 0)
{
Character? selectedCharacter = null;
bool decided = false;
if (sortedList.Count == 1)
{
selectedCharacter = sortedList[0];
decided = true;
}
while (!decided)
{
// 每个角色进行两次随机数抽取
var randomNumbers = sortedList.Select(c => new
{
Character = c,
FirstRoll = random.Next(1, 21),
SecondRoll = random.Next(1, 21)
}).ToList();
randomNumbers.ForEach(a => WriteLine(a.Character.Name + ": " + a.FirstRoll + " / " + a.SecondRoll));
// 找到两次都大于其他角色的角色
int maxFirstRoll = randomNumbers.Max(r => r.FirstRoll);
int maxSecondRoll = randomNumbers.Max(r => r.SecondRoll);
var candidates = randomNumbers
.Where(r => r.FirstRoll == maxFirstRoll && r.SecondRoll == maxSecondRoll)
.ToList();
if (candidates.Count == 1)
{
selectedCharacter = candidates.First().Character;
decided = true;
}
}
// 将决定好的角色加入顺序表
if (selectedCharacter != null)
{
AddCharacter(selectedCharacter, Calculation.Round2Digits(_queue.Count * 0.1), false);
WriteLine("decided: " + selectedCharacter.Name + "\r\n");
sortedList.Remove(selectedCharacter);
}
}
}
}
}
/// <summary>
/// 将角色加入行动顺序表
/// </summary>
/// <param name="character"></param>
/// <param name="hardnessTime"></param>
/// <param name="isCheckProtected"></param>
public void AddCharacter(Character character, double hardnessTime, bool isCheckProtected = true)
{
// 插队机制:按硬直时间排序
int insertIndex = _queue.FindIndex(c => _hardnessTimes[c] > hardnessTime);
if (isCheckProtected)
{
// 查找保护条件 5人局为3次9人局为4次
int countProtected = _queue.Count >= 9 ? 4 : ((_queue.Count >= 5) ? 3 : 2);
// 查找队列中是否有满足插队补偿条件的角色(最后一个)
var list = _queue
.Select((c, index) => new { Character = c, Index = index })
.Where(x => _cutCount.ContainsKey(x.Character) && _cutCount[x.Character] >= countProtected);
// 如果没有找到满足条件的角色,返回 -1
int protectIndex = list.Select(x => x.Index).LastOrDefault(-1);
// 判断是否需要插入到受保护角色的后面
if (protectIndex != -1 && (insertIndex != -1 && insertIndex <= protectIndex))
{
// 如果按硬直时间插入的位置在受保护角色之前或相同,则插入到受保护角色的后面一位
insertIndex = protectIndex + 1;
hardnessTime = _hardnessTimes[list.Select(x => x.Character).Last()];
// 列出受保护角色的名单
WriteLine($"由于 [ {string.Join(" ][ ", list.Select(x => x.Character))} ] 受到行动保护,因此角色 [ {character} ] 将插入至顺序表第 {insertIndex + 1} 位。");
}
}
// 如果插入索引无效(为-1 或 大于等于队列长度),则添加到队列尾部
if (insertIndex == -1 || insertIndex >= _queue.Count)
{
_queue.Add(character);
}
else
{
_queue.Insert(insertIndex, character);
}
_hardnessTimes[character] = hardnessTime;
// 为所有被插队的角色增加 _cutCount
if (isCheckProtected && insertIndex != -1 && insertIndex < _queue.Count)
{
for (int i = insertIndex + 1; i < _queue.Count; i++)
{
Character queuedCharacter = _queue[i];
if (!_cutCount.TryAdd(queuedCharacter, 1))
{
_cutCount[queuedCharacter] += 1;
}
}
}
}
/// <summary>
/// 从行动顺序表取出第一个角色
/// </summary>
/// <returns></returns>
public Character? NextCharacter()
{
if (_queue.Count == 0) return null;
// 硬直时间为0的角色将执行行动
Character? character = _queue.FirstOrDefault(c => _hardnessTimes[c] == 0);
if (character != null)
{
_queue.Remove(character);
_cutCount.Remove(character);
return character;
}
return null;
}
/// <summary>
/// 显示当前所有角色的状态和硬直时间
/// </summary>
public void DisplayQueue()
{
StringBuilder text = new();
text.AppendLine("==== 角色状态 ====");
foreach (Character c in _queue)
{
text.AppendLine("角色 [ " + c + " ]");
text.AppendLine($"生命值:{c.HP} / {c.MaxHP}" + (c.ExHP + c.ExHP2 > 0 ? $" [{c.BaseHP} + {c.ExHP + c.ExHP2}]" : ""));
text.AppendLine($"魔法值:{c.MP} / {c.MaxMP}" + (c.ExMP + c.ExMP2 > 0 ? $" [{c.BaseMP} + {c.ExMP + c.ExMP2}]" : ""));
text.AppendLine($"能量值:{c.EP} / 200");
if (c.CharacterState != CharacterState.Actionable)
{
text.AppendLine(CharacterSet.GetCharacterState(c.CharacterState));
}
text.AppendLine($"硬直时间:{_hardnessTimes[c]}");
}
WriteLine(text.ToString());
}
/// <summary>
/// 回合开始前触发
/// </summary>
/// <returns></returns>
public virtual bool BeforeTurn(Character character)
{
return true;
}
/// <summary>
/// 角色 <paramref name="character"/> 的回合进行中
/// </summary>
/// <param name="character"></param>
/// <returns>是否结束游戏</returns>
public bool ProcessTurn(Character character)
{
if (!BeforeTurn(character))
{
return false;
}
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
effect.OnTurnStart(character);
}
// 假设基础硬直时间
double baseTime = new Random().Next(10, 30);
// 敌人列表
List<Character> enemys = [.. _queue.Where(c => c != character && c.CharacterState != CharacterState.Neutral)];
// 队友列表
List<Character> teammates = [];
// 技能列表
List<Skill> skills = [.. character.Skills.Where(s => s.Level > 0 && s.IsActive && s.Enable && s.CurrentCD == 0 &&
((s.IsSuperSkill && s.EPCost <= character.EP) || (!s.IsSuperSkill && s.IsMagic && s.MPCost <= character.MP) || (!s.IsSuperSkill && !s.IsMagic && s.EPCost <= character.EP)))];
// 作出了什么行动
CharacterActionType type = CharacterActionType.None;
if (character.CharacterState == CharacterState.Actionable)
{
if (character.CharacterState == CharacterState.ActionRestricted)
{
// 行动受限,只能使用特殊物品
if (character.Items.Count > 0)
{
type = CharacterActionType.UseItem;
}
}
else if (character.CharacterState == CharacterState.BattleRestricted)
{
// 战斗不能,只能使用物品
if (character.Items.Count > 0)
{
type = CharacterActionType.UseItem;
}
}
else if (character.CharacterState == CharacterState.SkillRestricted)
{
// 技能受限,无法使用技能,可以使用物品
if (character.Items.Count > 0)
{
if (new Random().NextDouble() > 0.5)
{
type = CharacterActionType.UseItem;
}
else
{
type = CharacterActionType.NormalAttack;
}
}
else
{
type = CharacterActionType.NormalAttack;
}
}
else
{
// 可以任意行动
bool canUseItem = character.Items.Count > 0;
bool canCastSkill = skills.Count > 0;
if (canUseItem && canCastSkill)
{
double dowhat = new Random().NextDouble();
if (dowhat < 0.33)
{
type = CharacterActionType.UseItem;
}
else if (dowhat < 0.66)
{
type = CharacterActionType.PreCastSkill;
}
else
{
type = CharacterActionType.NormalAttack;
}
}
else if (canUseItem && !canCastSkill)
{
if (new Random().NextDouble() > 0.5)
{
type = CharacterActionType.UseItem;
}
else
{
type = CharacterActionType.NormalAttack;
}
}
else if (!canUseItem && canCastSkill)
{
if (new Random().NextDouble() > 0.5)
{
type = CharacterActionType.PreCastSkill;
}
else
{
type = CharacterActionType.NormalAttack;
}
}
else type = CharacterActionType.NormalAttack;
}
}
else if (character.CharacterState == CharacterState.Casting)
{
// 如果角色上一次吟唱了魔法,这次的行动则是结算这个魔法
type = CharacterActionType.CastSkill;
}
else if (character.CharacterState == CharacterState.PreCastSuperSkill)
{
// 角色使用回合外爆发技插队
type = CharacterActionType.CastSuperSkill;
}
else
{
// 完全行动不能会获得10时间硬直
baseTime = 10;
WriteLine("[ " + character + $" ] 完全行动不能!");
}
if (type == CharacterActionType.NormalAttack)
{
// 使用普通攻击逻辑
// 获取随机敌人
if (enemys.Count > 0)
{
Character enemy = enemys[new Random().Next(enemys.Count)];
character.NormalAttack.Attack(this, character, enemy);
// 普通攻击的默认硬直时间为7
baseTime = 7;
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
if (effect.AlterHardnessTimeAfterNormalAttack(character, baseTime, out double newTime))
{
baseTime = newTime;
}
}
}
}
else if (type == CharacterActionType.PreCastSkill)
{
// 预使用技能,即开始吟唱逻辑
// 注意FastAuto 模式下,此吟唱逻辑删减了选取目标的逻辑,将选取逻辑放在了实际释放的环节
// 在正常交互式模式下,吟唱前需要先选取目标
Skill skill = skills[new Random().Next(skills.Count)];
if (skill.IsMagic && !skill.IsSuperSkill)
{
character.CharacterState = CharacterState.Casting;
_castingSkills.Add(character, skill);
baseTime = skill.CastTime;
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
effect.OnSkillCasting(character);
}
}
else
{
if (CheckCanCast(character, skill, out double cost))
{
character.EP = Calculation.Round2Digits(character.EP - cost);
baseTime = skill.HardnessTime;
skill.CurrentCD = Calculation.Round2Digits(Math.Max(1, skill.CD * (1 - character.CDR)));
skill.Enable = false;
WriteLine("[ " + character + $" ] 消耗了 {cost:f2} 点能量,释放了{(skill.IsSuperSkill ? "" : "")} {skill.Name}");
skill.Trigger(this, character, enemys, teammates);
foreach (Effect effect in character.Effects)
{
if (effect.AlterHardnessTimeAfterCastSkill(character, baseTime, out double newTime))
{
baseTime = newTime;
}
}
}
}
}
else if (type == CharacterActionType.CastSkill)
{
// 使用技能逻辑,结束吟唱状态
character.CharacterState = CharacterState.Actionable;
Skill skill = _castingSkills[character];
_castingSkills.Remove(character);
// 判断是否能够释放技能
if (CheckCanCast(character, skill, out double cost))
{
character.MP = Calculation.Round2Digits(character.MP - cost);
baseTime = skill.HardnessTime;
skill.CurrentCD = Calculation.Round2Digits(Math.Max(1, skill.CD * (1 - character.CDR)));
skill.Enable = false;
WriteLine("[ " + character + $" ] 消耗了 {cost:f2} 点魔法值,释放了技能 {skill.Name}");
skill.Trigger(this, character, enemys, teammates);
}
else
{
WriteLine("[ " + character + $" ] 放弃释放技能!");
// 放弃释放技能会获得3的硬直时间
baseTime = 3;
}
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
if (effect.AlterHardnessTimeAfterCastSkill(character, baseTime, out double newTime))
{
baseTime = newTime;
}
}
}
else if (type == CharacterActionType.CastSuperSkill)
{
// 结束预释放爆发技的状态
character.CharacterState = CharacterState.Actionable;
Skill skill = _castingSuperSkills[character];
_castingSuperSkills.Remove(character);
// 判断是否能够释放技能
if (CheckCanCast(character, skill, out double cost))
{
character.EP = Calculation.Round2Digits(character.EP - cost);
baseTime = skill.HardnessTime;
skill.CurrentCD = Calculation.Round2Digits(Math.Max(1, skill.CD * (1 - character.CDR)));
skill.Enable = false;
WriteLine("[ " + character + $" ] 消耗了 {cost:f2} 点能量值,释放了爆发技 {skill.Name}");
skill.Trigger(this, character, enemys, teammates);
}
else
{
WriteLine("[ " + character + $" ] 放弃释放技能!");
// 放弃释放技能会获得3的硬直时间
baseTime = 3;
}
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
if (effect.AlterHardnessTimeAfterCastSkill(character, baseTime, out double newTime))
{
baseTime = newTime;
}
}
}
else if (type == CharacterActionType.UseItem)
{
// 使用物品逻辑
}
else
{
WriteLine("[ " + character + $" ] 放弃了行动!");
}
if (_isGameEnd)
{
return true;
}
// 减少硬直时间
double newHardnessTime = baseTime;
if (character.CharacterState != CharacterState.Casting)
{
newHardnessTime = Math.Max(1, Calculation.Round2Digits(baseTime * (1 - character.ActionCoefficient)));
WriteLine("[ " + character + " ] 回合结束,获得硬直时间: " + newHardnessTime);
}
else
{
newHardnessTime = Math.Max(1, Calculation.Round2Digits(baseTime * (1 - character.AccelerationCoefficient)));
WriteLine("[ " + character + " ] 进行吟唱,持续时间: " + newHardnessTime);
}
AddCharacter(character, newHardnessTime);
// 有人想要插队吗?
WillPreCastSuperSkill(character);
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
effect.OnTurnEnd(character);
}
AfterTurn(character);
WriteLine("");
return false;
}
/// <summary>
/// 回合结束后触发
/// </summary>
/// <returns></returns>
public virtual void AfterTurn(Character character)
{
}
/// <summary>
/// 时间进行流逝,减少硬直时间,减少技能冷却时间,角色也会因此回复状态
/// </summary>
/// <returns>流逝的时间</returns>
public double TimeLapse()
{
if (_queue.Count == 0) return 0;
// 获取第一个角色的硬直时间
double timeToReduce = _hardnessTimes[_queue[0]];
WriteLine("时间流逝:" + timeToReduce);
// 减少所有角色的硬直时间
foreach (Character character in _queue)
{
_hardnessTimes[character] = Calculation.Round2Digits(_hardnessTimes[character] - timeToReduce);
// 回血回蓝
double recoveryHP = Calculation.Round2Digits(character.HR * timeToReduce);
double recoveryMP = Calculation.Round2Digits(character.MR * timeToReduce);
double needHP = Calculation.Round2Digits(character.MaxHP - character.HP);
double needMP = Calculation.Round2Digits(character.MaxMP - character.MP);
double reallyReHP = needHP >= recoveryHP ? recoveryHP : needHP;
double reallyReMP = needMP >= recoveryMP ? recoveryMP : needMP;
if (reallyReHP > 0 && reallyReMP > 0)
{
character.HP = Calculation.Round2Digits(character.HP + reallyReHP);
character.MP = Calculation.Round2Digits(character.MP + reallyReMP);
WriteLine("角色 " + character.Name + " 回血:" + recoveryHP + " / " + "回蓝:" + recoveryMP);
}
else
{
if (reallyReHP > 0)
{
character.HP = Calculation.Round2Digits(character.HP + reallyReHP);
WriteLine("角色 " + character.Name + " 回血:" + recoveryHP);
}
if (reallyReMP > 0)
{
character.MP = Calculation.Round2Digits(character.MP + reallyReMP);
WriteLine("角色 " + character.Name + " 回蓝:" + recoveryMP);
}
}
// 减少所有技能的冷却时间
foreach (Skill skill in character.Skills)
{
skill.CurrentCD = Calculation.Round2Digits(skill.CurrentCD - timeToReduce);
if (skill.CurrentCD <= 0)
{
skill.CurrentCD = 0;
skill.Enable = true;
}
}
// 移除到时间的特效
foreach (Effect effect in character.Effects.ToList())
{
if (!effect.Durative || effect.Level == 0)
{
character.Effects.Remove(effect);
continue;
}
effect.OnTimeElapsed(character, timeToReduce);
effect.RemainDuration = Calculation.Round2Digits(effect.RemainDuration - timeToReduce);
if (effect.RemainDuration <= 0)
{
effect.RemainDuration = 0;
character.Effects.Remove(effect);
effect.OnEffectLost(character);
}
}
}
WriteLine("\r\n");
return timeToReduce;
}
/// <summary>
/// 对敌人造成伤害
/// </summary>
/// <param name="actor"></param>
/// <param name="enemy"></param>
/// <param name="damage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="magicType"></param>
/// <param name="isCritical"></param>
public void DamageToEnemy(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, bool isCritical = false)
{
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
if (effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, isCritical, out double newDamage))
{
damage = newDamage;
}
}
if (damage < 0) damage = 0;
if (isMagicDamage)
{
string dmgType = CharacterSet.GetMagicName(magicType);
WriteLine("[ " + enemy + $" ] 受到了 {damage} 点{dmgType}");
}
else WriteLine("[ " + enemy + $" ] 受到了 {damage} 点物理伤害!");
enemy.HP = Calculation.Round2Digits(enemy.HP - damage);
// 造成伤害和受伤都可以获得能量
double ep = GetEP(damage, 0.2, 40);
foreach (Effect effect in actor.Effects)
{
if (effect.AlterEPAfterDamage(actor, ep, out double newep))
{
ep = newep;
}
}
actor.EP += ep;
ep = GetEP(damage, 0.1, 20);
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
if (effect.AlterEPAfterGetDamage(enemy, ep, out double newep))
{
ep = newep;
}
}
enemy.EP += ep;
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
effect.AfterDamageCalculation(actor, enemy, damage, isMagicDamage, magicType);
}
if (enemy.HP < 0)
{
DeathCalculation(actor, enemy);
// 给所有角色的特效广播角色死亡结算
foreach (Effect effect in _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0)))
{
effect.AfterDeathCalculation(enemy, actor, _continuousKilling, _earnedMoney);
}
if (_queue.Remove(enemy) && (!_queue.Where(c => c != actor && c.CharacterState != CharacterState.Neutral).Any()))
{
// 没有其他的角色了,游戏结束
EndGameInfo(actor);
}
}
}
/// <summary>
/// 获取EP
/// </summary>
/// <param name="a">参数1</param>
/// <param name="b">参数2</param>
/// <param name="max">最大获取量</param>
public static double GetEP(double a, double b, double max)
{
return Calculation.Round2Digits(Math.Min((a + new Random().Next(30)) * b, max));
}
/// <summary>
/// 计算物理伤害
/// </summary>
/// <param name="actor"></param>
/// <param name="target"></param>
/// <param name="isNormalAttack"></param>
/// <param name="expectedDamage"></param>
/// <param name="finalDamage"></param>
/// <returns></returns>
public DamageResult CalculatePhysicalDamage(Character actor, Character target, bool isNormalAttack, double expectedDamage, out double finalDamage)
{
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
if (effect.AlterExpectedDamageBeforeCalculation(actor, target, expectedDamage, isNormalAttack, false, MagicType.None, out double newDamage))
{
expectedDamage = newDamage;
}
}
double dice = new Random().NextDouble();
// 闪避判定
if (dice < target.EvadeRate)
{
finalDamage = 0;
List<Character> characters = [actor, target];
bool isAlterEvaded = false;
foreach (Effect effect in characters.SelectMany(c => c.Effects.Where(e => e.Level > 0)))
{
isAlterEvaded = effect.OnEvadedTriggered(actor, target, dice);
}
if (!isAlterEvaded)
{
WriteLine("此物理攻击被完美闪避了!");
return DamageResult.Evaded;
}
}
// 物理穿透后的护甲
double penetratedDEF = Calculation.Round2Digits((1 - actor.PhysicalPenetration) * target.DEF);
// 物理伤害减免
double physicalDamageReduction = Calculation.Round4Digits(penetratedDEF / (penetratedDEF + 120));
// 最终的物理伤害
finalDamage = Calculation.Round2Digits(expectedDamage * (1 - physicalDamageReduction));
// 暴击判定
dice = new Random().NextDouble();
if (dice < actor.CritRate)
{
finalDamage = Calculation.Round2Digits(finalDamage * actor.CritDMG); // 暴击伤害倍率加成
WriteLine("暴击生效!!");
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
effect.OnCriticalDamageTriggered(actor, dice);
}
return DamageResult.Critical;
}
// 是否有效伤害
return DamageResult.Normal;
}
/// <summary>
/// 计算魔法伤害
/// </summary>
/// <param name="actor"></param>
/// <param name="target"></param>
/// <param name="isNormalAttack"></param>
/// <param name="magicType"></param>
/// <param name="expectedDamage"></param>
/// <param name="finalDamage"></param>
/// <returns></returns>
public DamageResult CalculateMagicalDamage(Character actor, Character target, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage)
{
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
if (effect.AlterExpectedDamageBeforeCalculation(actor, target, expectedDamage, isNormalAttack, true, magicType, out double newDamage))
{
expectedDamage = newDamage;
}
}
MagicResistance magicResistance = magicType switch
{
MagicType.Starmark => target.MDF.Starmark,
MagicType.PurityNatural => target.MDF.PurityNatural,
MagicType.PurityContemporary => target.MDF.PurityContemporary,
MagicType.Bright => target.MDF.Bright,
MagicType.Shadow => target.MDF.Shadow,
MagicType.Element => target.MDF.Element,
MagicType.Fleabane => target.MDF.Fleabane,
MagicType.Particle => target.MDF.Particle,
_ => target.MDF.None
};
// 魔法穿透后的魔法抗性
double MDF = Calculation.Round2Digits((1 - actor.MagicalPenetration) * magicResistance.Value);
// 最终的魔法伤害
finalDamage = Calculation.Round2Digits(expectedDamage * (1 - MDF));
// 暴击判定
double dice = new Random().NextDouble();
if (dice < actor.CritRate)
{
finalDamage = Calculation.Round2Digits(finalDamage * actor.CritDMG); // 暴击伤害倍率加成
WriteLine("暴击生效!!");
foreach (Effect effect in actor.Effects.Where(e => e.Level > 0))
{
effect.OnCriticalDamageTriggered(actor, dice);
}
return DamageResult.Critical;
}
// 是否有效伤害
return DamageResult.Normal;
}
/// <summary>
/// 死亡结算
/// </summary>
/// <param name="killer"></param>
/// <param name="death"></param>
public void DeathCalculation(Character killer, Character death)
{
if (!_continuousKilling.TryAdd(killer, 1)) _continuousKilling[killer] += 1;
double money = new Random().Next(200, 400);
if (_continuousKilling.TryGetValue(death, out int coefficient) && coefficient > 1)
{
money = Calculation.Round(money + ((coefficient + 1) * new Random().Next(100, 200)), 0);
string termination = CharacterSet.GetContinuousKilling(coefficient);
WriteLine("[ " + killer + " ] 终结了 [ " + death + " ]" + (termination != "" ? " 的" + termination : "") + $",获得 {money} 金钱!");
}
else
{
WriteLine("[ " + killer + " ] 杀死了 [ " + death + $" ],获得 {money} 金钱!");
}
int kills = _continuousKilling[killer];
string continuousKilling = CharacterSet.GetContinuousKilling(kills);
if (kills == 2 || kills == 3)
{
WriteLine("[ " + killer + " ] 完成了" + continuousKilling + "");
}
else if (kills == 4)
{
WriteLine("[ " + killer + " ] 正在" + continuousKilling + "");
}
else if (kills > 4 && kills < 10)
{
WriteLine("[ " + killer + " ] 已经" + continuousKilling + "");
}
else if (kills >= 10)
{
WriteLine("[ " + killer + " ] 已经" + continuousKilling + "!拜托谁去杀了他吧!!!");
}
if (!_earnedMoney.TryAdd(killer, money)) _earnedMoney[killer] += money;
_eliminated.Add(death);
}
/// <summary>
/// 游戏结束信息
/// </summary>
public void EndGameInfo(Character winner)
{
WriteLine("[ " + winner + " ] 是胜利者。");
_queue.Remove(winner);
_eliminated.Add(winner);
int top = 1;
WriteLine("");
WriteLine("=== 排名 ===");
for (int i = _eliminated.Count - 1; i >= 0; i--)
{
string topCharacter = _eliminated[i].ToString() + (_earnedMoney.TryGetValue(_eliminated[i], out double earned) ? $" [ 已赚取 {earned} 金钱 ]" : "");
if (top == 1)
{
WriteLine("冠军:" + topCharacter);
}
else if (top == 2)
{
WriteLine("亚军:" + topCharacter);
}
else if (top == 3)
{
WriteLine("季军:" + topCharacter);
}
else
{
WriteLine($"第 {top} 名:" + topCharacter);
}
top++;
}
WriteLine("");
_isGameEnd = true;
}
/// <summary>
/// 检查是否可以释放技能
/// </summary>
/// <param name="caster"></param>
/// <param name="skill"></param>
/// <param name="cost"></param>
/// <returns></returns>
public bool CheckCanCast(Character caster, Skill skill, out double cost)
{
if (skill.IsMagic && !skill.IsSuperSkill)
{
cost = skill.MPCost;
if (cost > 0 && cost <= caster.MP)
{
return true;
}
else
{
WriteLine("[ " + caster + $" ] 魔法不足!");
}
}
else
{
cost = skill.EPCost;
if (cost > 0 && cost <= caster.EP)
{
return true;
}
else
{
WriteLine("[ " + caster + $" ] 能量不足!");
}
}
return false;
}
/// <summary>
/// 是否在回合外释放爆发技插队
/// </summary>
/// <param name="character">当前正在行动的角色</param>
/// <returns></returns>
public void WillPreCastSuperSkill(Character character)
{
CharacterState[] checkStates = [CharacterState.Actionable, CharacterState.Neutral];
// 选取在顺序表一半之后的角色
foreach (Character other in _queue.Where(c => c != character && checkStates.Contains(c.CharacterState) && _queue.IndexOf(c) >= _queue.Count / 2).ToList())
{
// 有 65% 欲望插队
if (new Random().NextDouble() < 0.65)
{
List<Skill> skills = other.Skills.Where(s => s.IsSuperSkill && s.Level > 0 && s.IsActive && s.Enable && s.CurrentCD == 0 && other.EP >= s.EPCost).ToList();
if (skills.Count > 0)
{
Skill skill = skills[new Random().Next(skills.Count)];
_castingSuperSkills.Add(other, skill);
other.CharacterState = CharacterState.PreCastSuperSkill;
_queue.Remove(other);
_cutCount.Remove(character);
AddCharacter(other, 0, false);
WriteLine("[ " + other + " ] 预释放了爆发技!!");
foreach (Character c in _hardnessTimes.Keys)
{
if (c != other)
{
_hardnessTimes[c] += 0.01;
}
}
foreach (Effect effect in character.Effects.Where(e => e.Level > 0))
{
effect.OnSkillCasting(character);
}
}
}
}
}
}
}