diff --git a/Api/Factory/SkillFactory.cs b/Api/Factory/SkillFactory.cs index 53cdbb3..5074cea 100644 --- a/Api/Factory/SkillFactory.cs +++ b/Api/Factory/SkillFactory.cs @@ -1,29 +1,15 @@ -using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; -using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Api.Factory { internal class SkillFactory : IFactory { - public Type EntityType => _EntityType; - - private Type _EntityType = typeof(Skill); - - internal Skill Create(SkillType type = SkillType.Passive) - { - _EntityType = typeof(Skill); - return type switch - { - SkillType.Passive => new(false), - SkillType.Active => new(true), - _ => new(false) - }; - } + public Type EntityType => typeof(Skill); public Skill Create() { - return Create(SkillType.Passive); + return new(); } } } diff --git a/Api/Utility/ActionQueue.cs b/Api/Utility/ActionQueue.cs new file mode 100644 index 0000000..a271e63 --- /dev/null +++ b/Api/Utility/ActionQueue.cs @@ -0,0 +1,996 @@ +using System.Text; +using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Api.Utility +{ + /// + /// 行动顺序表 + /// + public class ActionQueue + { + /// + /// 用于文本输出 + /// + public Action WriteLine { get; } + + /// + /// 当前的行动顺序 + /// + protected readonly List _queue = []; + + /// + /// 当前已死亡的角色顺序(第一个是最早死的) + /// + protected readonly List _eliminated = []; + + /// + /// 硬直时间表 + /// + protected readonly Dictionary _hardnessTimes = []; + + /// + /// 角色正在吟唱的魔法 + /// + protected readonly Dictionary _castingSkills = []; + + /// + /// 角色预释放的爆发技 + /// + protected readonly Dictionary _castingSuperSkills = []; + + /// + /// 角色目前赚取的金钱 + /// + protected readonly Dictionary _earnedMoney = []; + + /// + /// 角色目前的连杀数 + /// + protected readonly Dictionary _continuousKilling = []; + + /// + /// 角色被插队次数 + /// + protected readonly Dictionary _cutCount = []; + + /// + /// 游戏是否结束 + /// + protected bool _isGameEnd = false; + + /// + /// 新建一个行动顺序表 + /// + /// 参与本次游戏的角色列表 + /// 用于文本输出 + public ActionQueue(List characters, Action? writer = null) + { + if (writer != null) + { + WriteLine = writer; + } + WriteLine ??= new Action(Console.WriteLine); + + // 初始排序:按速度排序 + List> groupedBySpeed = [.. characters + .GroupBy(c => c.SPD) + .OrderByDescending(g => g.Key)]; + + Random random = new(); + + foreach (IGrouping group in groupedBySpeed) + { + if (group.Count() == 1) + { + // 如果只有一个角色,直接加入队列 + AddCharacter(group.First(), Calculation.Round2Digits(_queue.Count * 0.1), false); + } + else + { + // 如果有多个角色,进行先行决定 + List 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); + } + } + } + } + } + + /// + /// 将角色加入行动顺序表 + /// + /// + /// + /// + 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; + } + } + } + } + + /// + /// 从行动顺序表取出第一个角色 + /// + /// + 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; + } + + /// + /// 显示当前所有角色的状态和硬直时间 + /// + 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()); + } + + /// + /// 回合开始前触发 + /// + /// + public virtual bool BeforeTurn(Character character) + { + return true; + } + + /// + /// 角色 的回合进行中 + /// + /// + /// 是否结束游戏 + 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 enemys = [.. _queue.Where(c => c != character && c.CharacterState != CharacterState.Neutral)]; + + // 队友列表 + List teammates = []; + + // 技能列表 + List 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; + } + + /// + /// 回合结束后触发 + /// + /// + public virtual void AfterTurn(Character character) + { + + } + + /// + /// 时间进行流逝,减少硬直时间,减少技能冷却时间,角色也会因此回复状态 + /// + /// 流逝的时间 + 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; + } + + /// + /// 对敌人造成伤害 + /// + /// + /// + /// + /// + /// + /// + /// + 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); + } + } + } + + /// + /// 获取EP + /// + /// 参数1 + /// 参数2 + /// 最大获取量 + public static double GetEP(double a, double b, double max) + { + return Calculation.Round2Digits(Math.Min((a + new Random().Next(30)) * b, max)); + } + + /// + /// 计算物理伤害 + /// + /// + /// + /// + /// + /// + /// + 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 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; + } + + /// + /// 计算魔法伤害 + /// + /// + /// + /// + /// + /// + /// + /// + 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; + } + + /// + /// 死亡结算 + /// + /// + /// + 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); + } + + /// + /// 游戏结束信息 + /// + 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; + } + + /// + /// 检查是否可以释放技能 + /// + /// + /// + /// + /// + 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; + } + + /// + /// 是否在回合外释放爆发技插队 + /// + /// 当前正在行动的角色 + /// + 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 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); + } + } + } + } + } + } +} diff --git a/Api/Utility/Factory.cs b/Api/Utility/Factory.cs index bc63c45..4008680 100644 --- a/Api/Utility/Factory.cs +++ b/Api/Utility/Factory.cs @@ -140,11 +140,10 @@ namespace Milimoe.FunGame.Core.Api.Utility /// /// 获取技能实例 /// - /// Skill类型 主动 或 被动 /// - public static Skill GetSkill(SkillType type = SkillType.Passive) + public static Skill GetSkill() { - return SkillFactory.Create(type); + return SkillFactory.Create(); } /// diff --git a/Api/Utility/General.cs b/Api/Utility/General.cs index 475bd7d..3d440a4 100644 --- a/Api/Utility/General.cs +++ b/Api/Utility/General.cs @@ -190,6 +190,15 @@ namespace Milimoe.FunGame.Core.Api.Utility /// public static T? JsonDeserialize(string json) => Service.JsonManager.GetObject(json); + /// + /// 反序列化Json对象 使用 可指定反序列化选项 + /// + /// + /// + /// + /// + public static T? JsonDeserialize(ref Utf8JsonReader reader, JsonSerializerOptions options) => Service.JsonManager.GetObject(ref reader, options); + /// /// 反序列化Json对象 可指定反序列化选项 /// diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs index 6ffd318..3632fa3 100644 --- a/Entity/Character/Character.cs +++ b/Entity/Character/Character.cs @@ -1,9 +1,16 @@ -using Milimoe.FunGame.Core.Api.Utility; +using System.Collections.Generic; +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Interface.Entity; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Entity { + /// + /// 角色需要使用 Factory.Get 的方式来构造,并赋值 标记的属性 + /// 在使用时仅需要调用 方法即可获得相同对象 + /// 不建议继承 + /// public class Character : BaseEntity, ICopyable { /// @@ -27,9 +34,9 @@ namespace Milimoe.FunGame.Core.Entity public User User { get; set; } /// - /// 角色统计数据 + /// 角色的详细资料 /// - public CharacterStatistics Statistics { get; set; } + public CharacterProfile Profile { get; set; } /// /// 魔法属性 @@ -114,7 +121,8 @@ namespace Milimoe.FunGame.Core.Entity } set { - if (_Level > 0) _Level = value; + _Level = Math.Min(Math.Max(1, value), 60); + Recovery(); } } @@ -123,6 +131,11 @@ namespace Milimoe.FunGame.Core.Entity /// public double EXP { get; set; } = 0; + /// + /// 角色目前所处的状态 [ 战斗相关 ] + /// + public CharacterState CharacterState { get; set; } = CharacterState.Actionable; + /// /// 初始生命值 [ 初始设定 ] /// @@ -147,7 +160,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 最大生命值 = 基础生命值 + 额外生命值 + 额外生命值2 /// - public double MaxHP => BaseHP + ExHP + ExHP2; + public double MaxHP => Calculation.Round2Digits(BaseHP + ExHP + ExHP2); /// /// 当前生命值 [ 战斗相关 ] @@ -178,7 +191,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 最大魔法值 = 基础魔法值 + 额外魔法值 + 额外魔法值2 /// - public double MaxMP => BaseMP + ExMP + ExMP2; + public double MaxMP => Calculation.Round2Digits(BaseMP + ExMP + ExMP2); /// /// 当前魔法值 [ 战斗相关 ] @@ -188,7 +201,19 @@ namespace Milimoe.FunGame.Core.Entity /// /// 当前爆发能量 [ 战斗相关 ] /// - public double EP { get; set; } = 0; + public double EP + { + get + { + return _EP < 0 ? 0 : (_EP > 200 ? 200 : _EP); + } + set + { + _EP = Calculation.Round2Digits(value); + if (_EP > 200) _EP = 200; + else if (_EP < 0) _EP = 0; + } + } /// /// 初始攻击力 [ 初始设定 ] @@ -206,15 +231,15 @@ namespace Milimoe.FunGame.Core.Entity double atk = Calculation.Round2Digits(InitialATK + (Level - 1) * (0.95 + 0.045 * InitialATK)); if (PrimaryAttribute == PrimaryAttribute.AGI) { - return atk + BaseAGI; + return Calculation.Round2Digits(atk + BaseAGI); } else if (PrimaryAttribute == PrimaryAttribute.INT) { - return atk + BaseINT; + return Calculation.Round2Digits(atk + BaseINT); } else // 默认STR { - return atk + BaseSTR; + return Calculation.Round2Digits(atk + BaseSTR); } } } @@ -249,7 +274,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 攻击力 = 基础攻击力 + 额外攻击力 + 额外攻击力2 /// - public double ATK => BaseATK + ExATK + ExATK2; + public double ATK => Calculation.Round2Digits(BaseATK + ExATK + ExATK2); /// /// 初始物理护甲 [ 初始设定 ] @@ -275,7 +300,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 物理护甲 = 基础物理护甲 + 额外物理护甲 + 额外物理护甲2 /// - public double DEF => BaseDEF + ExDEF + ExDEF2; + public double DEF => Calculation.Round2Digits(BaseDEF + ExDEF + ExDEF2); /// /// 物理伤害减免(%) = [ 与物理护甲相关 ] + 额外物理伤害减免(%) @@ -297,7 +322,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 魔法抗性(%) [ 与技能和物品相关 ] /// - public double MDF { get; set; } = 0; + public MDF MDF { get; set; } /// /// 物理穿透(%) [ 与技能和物品相关 ] @@ -366,6 +391,72 @@ namespace Milimoe.FunGame.Core.Entity /// public double ER { get; set; } = 0; + /// + /// 核心属性的值 [ 核心属性相关 ] + /// + public double PrimaryAttributeValue + { + get + { + if (PrimaryAttribute == PrimaryAttribute.AGI) + { + return AGI; + } + else if (PrimaryAttribute == PrimaryAttribute.INT) + { + return INT; + } + else + { + return STR; + } + } + } + + /// + /// 基础核心属性的值 [ 核心属性相关 ] + /// + public double BasePrimaryAttributeValue + { + get + { + if (PrimaryAttribute == PrimaryAttribute.AGI) + { + return BaseAGI; + } + else if (PrimaryAttribute == PrimaryAttribute.INT) + { + return BaseINT; + } + else + { + return BaseSTR; + } + } + } + + /// + /// 额外核心属性的值 [ 核心属性相关 ] + /// + public double ExPrimaryAttributeValue + { + get + { + if (PrimaryAttribute == PrimaryAttribute.AGI) + { + return ExAGI; + } + else if (PrimaryAttribute == PrimaryAttribute.INT) + { + return ExINT; + } + else + { + return ExSTR; + } + } + } + /// /// 初始力量 [ 初始设定 ] /// @@ -512,7 +603,7 @@ namespace Milimoe.FunGame.Core.Entity { get { - double value = Calculation.Round4Digits(0.05 + INT * 0.0025 + ExCDR); + double value = Calculation.Round4Digits(0.05 + AGI * 0.0025 + ExCritRate); return Calculation.PercentageCheck(value); } } @@ -556,14 +647,24 @@ namespace Milimoe.FunGame.Core.Entity public double ExEvadeRate { get; set; } = 0; /// - /// 角色的技能组 + /// 普通攻击对象 /// - public Dictionary Skills { get; set; } = []; + public NormalAttack NormalAttack { get; } + + /// + /// 角色的技能列表 + /// + public HashSet Skills { get; } = []; + + /// + /// 角色的持续性特效列表 + /// + public HashSet Effects { get; } = []; /// /// 角色携带的物品 /// - public Dictionary Items { get; set; } = []; + public HashSet Items { get; } = []; /** * ===== 私有变量 ===== @@ -573,6 +674,11 @@ namespace Milimoe.FunGame.Core.Entity /// 等级 /// private int _Level = 1; + + /// + /// 能量值 + /// + private double _EP = 0; /// /// 物理穿透 @@ -587,7 +693,9 @@ namespace Milimoe.FunGame.Core.Entity protected Character() { User = General.UnknownUserInstance; - Statistics = new(); + Profile = new(Name, FirstName, NickName); + MDF = new(); + NormalAttack = new(this); } internal static Character GetInstance() @@ -605,7 +713,7 @@ namespace Milimoe.FunGame.Core.Entity MP = MaxMP; if (EP != -1) this.EP = EP; } - + /// /// 按时间回复状态 /// @@ -621,6 +729,20 @@ namespace Milimoe.FunGame.Core.Entity } } + /// + /// 提升角色的等级 + /// + /// 可为负数 + /// + public int LevelUp(int up = 1) + { + if (up != 0) + { + Level += up; + } + return Level; + } + /// /// 比较一个角色(只比较 ) /// @@ -637,8 +759,7 @@ namespace Milimoe.FunGame.Core.Entity /// public override string ToString() { - bool isChineseName = NetworkUtility.IsChineseName(Name + FirstName); - string str = isChineseName ? (Name + FirstName).Trim() : (Name + " " + FirstName).Trim(); + string str = GetName(); if (NickName != "") { if (str != "") str += ", "; @@ -651,6 +772,106 @@ namespace Milimoe.FunGame.Core.Entity return str; } + /// + /// 获取角色实例的名字、昵称以及所属玩家,包含等级 + /// + /// + public string ToStringWithLevel() + { + string str = GetName(); + if (NickName != "") + { + if (str != "") str += ", "; + str += NickName; + } + if (User != null && User.Username != "") + { + str += "(" + User.Username + ")"; + } + str += " - 等级 " + Level; + return str; + } + + /// + /// 获取角色的名字 + /// + /// + /// 如果 = false,返回 ;反之,返回 + + public string GetName(bool full = true) + { + if (full) + { + bool isChineseName = NetworkUtility.IsChineseName(Name + FirstName); + string str = isChineseName ? (Name + FirstName).Trim() : (Name + " " + FirstName).Trim(); + return str; + } + else + { + return FirstName; + } + } + + /// + /// 获取角色的详细信息 + /// + /// + public string GetInfo() + { + StringBuilder builder = new(); + + builder.AppendLine(ToStringWithLevel()); + builder.AppendLine($"生命值:{HP} / {MaxHP}" + (ExHP + ExHP2 > 0 ? $" [{BaseHP} + {ExHP + ExHP2}]" : "")); + builder.AppendLine($"魔法值:{MP} / {MaxMP}" + (ExMP + ExMP2 > 0 ? $" [{BaseMP} + {ExMP + ExMP2}]" : "")); + builder.AppendLine($"能量值:{EP} / 200"); + builder.AppendLine($"攻击力:{ATK}" + (ExATK + ExATK2 > 0 ? $" [{BaseATK} + {ExATK + ExATK2}]" : "")); + builder.AppendLine($"物理护甲:{DEF}" + (ExDEF + ExDEF2 > 0 ? $" [{BaseDEF} + {ExDEF + ExDEF2}]" : "") + $" ({PDR * 100:f2}%)"); + double mdf = Calculation.Round4Digits((MDF.None.Value + MDF.Starmark.Value + MDF.PurityNatural.Value + MDF.PurityContemporary.Value + + MDF.Bright.Value + MDF.Shadow.Value + MDF.Element.Value + MDF.Fleabane.Value + MDF.Particle.Value) / 9); + builder.AppendLine($"魔法抗性:{mdf * 100:f2}%(平均)"); + double exSPD = Calculation.Round2Digits(AGI * 0.65 + ExSPD); + builder.AppendLine($"行动速度:{SPD}" + (exSPD > 0 ? $" [{InitialSPD} + {exSPD}]" : "") + $" ({ActionCoefficient * 100:f2}%)"); + builder.AppendLine($"核心属性:{CharacterSet.GetPrimaryAttributeName(PrimaryAttribute)}"); + builder.AppendLine($"力量:{STR}" + (ExSTR > 0 ? $" [{BaseSTR} + {ExSTR}]" : "")); + builder.AppendLine($"敏捷:{AGI}" + (ExAGI > 0 ? $" [{BaseAGI} + {ExAGI}]" : "")); + builder.AppendLine($"智力:{INT}" + (ExINT > 0 ? $" [{BaseINT} + {ExINT}]" : "")); + builder.AppendLine($"生命回复:{HR}" + (ExHR > 0 ? $" [{Calculation.Round2Digits(InitialHR + STR * 0.25)} + {ExHR}]" : "")); + builder.AppendLine($"魔法回复:{MR}" + (ExMR > 0 ? $" [{Calculation.Round2Digits(InitialMR + INT * 0.1)} + {ExMR}]" : "")); + builder.AppendLine($"暴击率:{CritRate * 100:f2}%"); + builder.AppendLine($"暴击伤害:{CritDMG * 100:f2}%"); + builder.AppendLine($"闪避率:{EvadeRate * 100:f2}%"); + builder.AppendLine($"冷却缩减:{CDR * 100:f2}%"); + builder.AppendLine($"物理穿透:{PhysicalPenetration * 100:f2}%"); + builder.AppendLine($"魔法穿透:{MagicalPenetration * 100:f2}%"); + + if (CharacterState != CharacterState.Actionable) + { + builder.AppendLine(CharacterSet.GetCharacterState(CharacterState)); + } + + builder.AppendLine("== 普通攻击 =="); + builder.Append(NormalAttack.ToString()); + + if (Skills.Count > 0) + { + builder.AppendLine("== 角色技能 =="); + foreach (Skill skill in Skills) + { + builder.Append(skill.ToString()); + } + } + + if (Items.Count > 0) + { + builder.AppendLine("== 角色物品 =="); + foreach (Item item in Items) + { + builder.Append(item.ToString()); + } + } + + return builder.ToString(); + } + /// /// 复制一个角色 /// [ 推荐从模组中复制后使用对象 ] @@ -663,7 +884,7 @@ namespace Milimoe.FunGame.Core.Entity Name = Name, FirstName = FirstName, NickName = NickName, - Statistics = Statistics, + Profile = Profile, MagicType = MagicType, FirstRoleType = FirstRoleType, SecondRoleType = SecondRoleType, @@ -672,6 +893,7 @@ namespace Milimoe.FunGame.Core.Entity PrimaryAttribute = PrimaryAttribute, Level = Level, EXP = EXP, + CharacterState = CharacterState, InitialHP = InitialHP, ExHP2 = ExHP2, InitialMP = InitialMP, @@ -706,12 +928,18 @@ namespace Milimoe.FunGame.Core.Entity ATR = ATR, ExCritRate = ExCritRate, ExCritDMG = ExCritDMG, - ExEvadeRate = ExEvadeRate, - Skills = new(Skills), - Items = new(Items), + ExEvadeRate = ExEvadeRate }; + foreach (Skill skill in Skills) + { + c.Skills.Add(skill); + } + foreach (Item item in Items) + { + c.Items.Add(item); + } c.Recovery(); return c; } } -} +} \ No newline at end of file diff --git a/Entity/Character/CharacterProfile.cs b/Entity/Character/CharacterProfile.cs new file mode 100644 index 0000000..2373b9f --- /dev/null +++ b/Entity/Character/CharacterProfile.cs @@ -0,0 +1,62 @@ +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + public class CharacterProfile(string name, string firstname, string nickname) + { + /// + /// 角色的姓 + /// + public string Name { get; set; } = name; + + /// + /// 角色的名字 + /// + public string FirstName { get; set; } = firstname; + + /// + /// 角色的昵称 + /// + public string NickName { get; set; } = nickname; + + /// + /// 角色的出生地 + /// + public string Birthplace { get; set; } = ""; + + /// + /// 角色的出生日期 + /// + public DateTime Birthday { get; set; } = General.DefaultTime; + + /// + /// 角色的身份 + /// + public string Status { get; set; } = ""; + + /// + /// 角色的隶属 + /// + public string Affiliation { get; set; } = ""; + + /// + /// 角色的性别 + /// + public string Sex { get; set; } = ""; + + /// + /// 角色的身高 + /// + public string Height { get; set; } = ""; + + /// + /// 角色的体重 + /// + public string Weight { get; set; } = ""; + + /// + /// 角色的故事 + /// + public Dictionary Stories { get; set; } = []; + } +} \ No newline at end of file diff --git a/Entity/Character/MagicResistance.cs b/Entity/Character/MagicResistance.cs new file mode 100644 index 0000000..f36c64b --- /dev/null +++ b/Entity/Character/MagicResistance.cs @@ -0,0 +1,23 @@ +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + public class MDF() + { + public MagicResistance None { get; } = new(MagicType.None); + public MagicResistance Starmark { get; } = new(MagicType.Starmark); + public MagicResistance PurityNatural { get; } = new(MagicType.PurityNatural); + public MagicResistance PurityContemporary { get; } = new(MagicType.PurityContemporary); + public MagicResistance Bright { get; } = new(MagicType.Bright); + public MagicResistance Shadow { get; } = new(MagicType.Shadow); + public MagicResistance Element { get; } = new(MagicType.Element); + public MagicResistance Fleabane { get; } = new(MagicType.Fleabane); + public MagicResistance Particle { get; } = new(MagicType.Particle); + } + + public class MagicResistance(MagicType type = MagicType.None, double value = 0) + { + public MagicType MagicType { get; } = type; + public double Value { get; set; } = value; + } +} diff --git a/Entity/Item/Item.cs b/Entity/Item/Item.cs index 4e98dd1..57f6c96 100644 --- a/Entity/Item/Item.cs +++ b/Entity/Item/Item.cs @@ -7,14 +7,14 @@ namespace Milimoe.FunGame.Core.Entity public string Describe { get; set; } = ""; public double Price { get; set; } public char Key { get; set; } - public bool Active { get; set; } + public bool IsActive { get; set; } public bool Enable { get; set; } public Character? Character { get; set; } = null; public Skill? Skill { get; set; } = null; internal Item(bool active = false) { - Active = active; + IsActive = active; } public override bool Equals(IBaseEntity? other) diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs new file mode 100644 index 0000000..e3a4c39 --- /dev/null +++ b/Entity/Skill/Effect.cs @@ -0,0 +1,312 @@ +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Entity; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + /// + /// 特殊效果类,需要继承 + /// + public abstract class Effect(Skill skill) : BaseEntity + { + /// + /// 所属的技能 + /// + public Skill Skill { get; } = skill; + + /// + /// 作用于自身 + /// + public virtual bool TargetSelf { get; } = false; + + /// + /// 作用目标数量 + /// + public virtual int TargetCount { get; } = 0; + + /// + /// 作用范围 + /// + public virtual double TargetRange { get; } = 0; + + /// + /// 持续性的 + /// 配合 使用,而不是 。 + /// + public virtual bool Durative { get; } = false; + + /// + /// 持续时间 + /// 配合 使用。 + /// + public virtual double Duration { get; } = 0; + + /// + /// 持续时间(回合) + /// 使用此属性需要将 设置为 false。 + /// + public virtual double DurationTurn { get; } = 0; + + /// + /// 剩余持续时间 + /// + public double RemainDuration { get; set; } = 0; + + /// + /// 魔法类型 + /// + public virtual MagicType MagicType { get; } = MagicType.None; + + /// + /// 效果描述 + /// + public virtual string Description { get; } = ""; + + /// + /// 等级,跟随技能的等级 + /// + public int Level => Skill.Level; + + /// + /// 此特效的施加者,用于溯源 + /// + public Character? Source { get; set; } = null; + + /// + /// 获得此特效时 + /// + /// + public virtual void OnEffectGained(Character character) + { + + } + + /// + /// 失去此特效时 + /// + /// + public virtual void OnEffectLost(Character character) + { + + } + + /// + /// 在伤害计算前修改预期伤害 + /// + /// + /// + /// + /// + /// + /// + /// + /// 返回 true 表示修改了伤害 + public virtual bool AlterExpectedDamageBeforeCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, out double newDamage) + { + newDamage = damage; + return false; + } + + /// + /// 在伤害计算完成后修改实际伤害 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// 返回 true 表示修改了伤害 + public virtual bool AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, bool isCritical, out double newDamage) + { + newDamage = damage; + return false; + } + + /// + /// 在完成普通攻击动作之后修改硬直时间 + /// + /// + /// + /// + /// 返回 true 表示修改了硬直时间 + public virtual bool AlterHardnessTimeAfterNormalAttack(Character character, double baseHardnessTime, out double newHardnessTime) + { + newHardnessTime = baseHardnessTime; + return false; + } + + /// + /// 在完成释放技能动作之后修改硬直时间 + /// + /// + /// + /// + /// 返回 true 表示修改了硬直时间 + public virtual bool AlterHardnessTimeAfterCastSkill(Character character, double baseHardnessTime, out double newHardnessTime) + { + newHardnessTime = baseHardnessTime; + return false; + } + + /// + /// 在造成伤害时,修改获得的能量 + /// + /// + /// + /// + /// 返回 true 表示修改了获得的能量 + public virtual bool AlterEPAfterDamage(Character character, double baseEP, out double newEP) + { + newEP = baseEP; + return false; + } + + /// + /// 在受到伤害时,修改获得的能量 + /// + /// + /// + /// + /// 返回 true 表示修改了获得的能量 + public virtual bool AlterEPAfterGetDamage(Character character, double baseEP, out double newEP) + { + newEP = baseEP; + return false; + } + + /// + /// 技能开始吟唱时 [ 爆发技插队可触发此项 ] + /// + /// + public virtual void OnSkillCasting(Character caster) + { + + } + + /// + /// 技能吟唱被打断时 + /// + /// + /// + public virtual void OnSkillCastInterrupted(Character caster, Character interrupter) + { + + } + + /// + /// 吟唱结束后释放技能(魔法)/ 直接释放技能(战技/爆发技) + /// + /// + /// + /// + /// + /// + public virtual void OnSkillCasted(ActionQueue queue, Character caster, List enemys, List teammates, Dictionary others) + { + + } + + /// + /// 时间流逝时 + /// + /// + /// + public virtual void OnTimeElapsed(Character character, double elapsed) + { + + } + + /// + /// 在完成伤害结算后 + /// + /// + /// + /// + /// + /// + public virtual void AfterDamageCalculation(Character character, Character enemy, double damage, bool isMagicDamage, MagicType magicType) + { + + } + + /// + /// 在特效持有者的回合开始前 + /// + /// + public virtual void OnTurnStart(Character character) + { + + } + + /// + /// 在特效持有者的回合开始后 + /// + /// + public virtual void OnTurnEnd(Character character) + { + + } + + /// + /// 技能被升级时 + /// + /// + /// + public virtual void OnSkillLevelUp(Character character, double level) + { + + } + + /// + /// 特效持有者升级时 + /// + /// + /// + public virtual void OnOwnerLevelUp(Character owner, double level) + { + + } + + /// + /// 在完成死亡结算后 [ 全体广播 ] + /// + /// + /// + /// + /// + public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary continuousKilling, Dictionary earnedMoney) + { + + } + + /// + /// 在触发闪避时 + /// + /// + /// + /// + /// 返回 true 表示无视闪避 + public virtual bool OnEvadedTriggered(Character attacker, Character evader, double dice) + { + return false; + } + + /// + /// 在触发暴击时 + /// + /// + /// + public virtual void OnCriticalDamageTriggered(Character character, double dice) + { + + } + + public override bool Equals(IBaseEntity? other) + { + return other is Effect c && c.Name == Name; + } + } +} diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs new file mode 100644 index 0000000..bb98669 --- /dev/null +++ b/Entity/Skill/NormalAttack.cs @@ -0,0 +1,101 @@ +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Entity; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + public class NormalAttack(Character character, bool isMagic = false, MagicType magicType = MagicType.None) : BaseEntity + { + /// + /// 普通攻击名称 + /// + public override string Name => "普通攻击"; + + /// + /// 普通攻击说明 + /// + public string Description => $"对目标敌人造成 {Calculation.Round4Digits((1.0 + 0.05 * (Level - 1)) * 100)}% [ {Damage} ] 点{(IsMagic ? CharacterSet.GetMagicName(MagicType) : "物理")}伤害。"; + + /// + /// 所属的角色 + /// + public Character Character { get; } = character; + + /// + /// 普通攻击的伤害 + /// + public double Damage => Calculation.Round2Digits(Character.ATK * (1.0 + 0.05 * (Level - 1))); + + /// + /// 普通攻击等级 + /// + public int Level + { + get + { + return Math.Max(1, _Level); + } + set + { + _Level = Math.Min(Math.Max(1, value), 8); + } + } + + /// + /// 是否是魔法伤害 + /// + public bool IsMagic { get; } = isMagic; + + /// + /// 魔法伤害需要指定魔法类型 + /// + public MagicType MagicType { get; } = magicType; + + /// + /// 硬直时间 + /// + public double HardnessTime { get; } = 7; + + /// + /// 对目标(或多个目标)发起普通攻击 + /// + /// + /// + /// + public void Attack(ActionQueue queue, Character attacker, params Character[] enemys) + { + foreach (Character enemy in enemys) + { + queue.WriteLine("[ " + Character + $" ] 对 [ {enemy} ] 发起了普通攻击!"); + double expected = Damage; + DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage); + if (result != DamageResult.Evaded) + { + queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic, MagicType, result == DamageResult.Critical); + } + } + } + + public override bool Equals(IBaseEntity? other) + { + return other is NormalAttack c && c.Name == Name; + } + + public override string ToString() + { + StringBuilder builder = new(); + + builder.AppendLine(Name + " - " + "等级:" + Level); + builder.AppendLine("技能描述:" + Description); + builder.AppendLine("硬直时间:" + HardnessTime); + + return builder.ToString(); + } + + /// + /// 等级 + /// + private int _Level = 0; + } +} diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs index ef5b7e4..71eced5 100644 --- a/Entity/Skill/Skill.cs +++ b/Entity/Skill/Skill.cs @@ -1,24 +1,218 @@ -using Milimoe.FunGame.Core.Interface.Entity; -using Milimoe.FunGame.Core.Library.Constant; +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Entity; namespace Milimoe.FunGame.Core.Entity { + /// + /// 与 不同,构造技能时,建议继承此类再构造 + /// public class Skill : BaseEntity, IActiveEnable { - public string Describe { get; set; } = ""; - public char Key { get; set; } - public bool Active { get; set; } - public bool Enable { get; set; } - public MagicType MagicType { get; set; } + /// + /// 此技能所属的角色 + /// + public Character? Character { get; set; } = null; - internal Skill(bool active = false) + /// + /// 技能描述 + /// + public virtual string Description { get; set; } = ""; + + /// + /// 快捷键 + /// + public char Key { get; set; } = '/'; + + /// + /// 技能等级,等于 0 时可以称之为尚未学习 + /// + public int Level { - Active = active; + get + { + return Math.Max(0, _Level); + } + set + { + int max = IsSuperSkill ? 6 : (IsMagic ? 8 : 6); + _Level = Math.Min(Math.Max(0, value), max); + OnLevelUp(); + } + } + + /// + /// 是否是主动技能 + /// + [InitRequired] + public bool IsActive { get; set; } = true; + + /// + /// 是否可用 + /// + public bool Enable { get; set; } = true; + + /// + /// 是否是爆发技 [ 此项为最高优先级 ] + /// + [InitRequired] + public bool IsSuperSkill { get; set; } = false; + + /// + /// 是否属于魔法 [ 会失效 ],反之为战技 + /// + [InitRequired] + public bool IsMagic { get; set; } = true; + + /// + /// 魔法消耗 [ 魔法 ] + /// + [InitOptional] + public virtual double MPCost { get; } = 0; + + /// + /// 基础魔法消耗 [ 魔法 ] + /// + [InitOptional] + protected virtual double BaseMPCost { get; } = 0; + + /// + /// 吟唱时间 [ 魔法 ] + /// + [InitOptional] + public virtual double CastTime { get; } = 0; + + /// + /// 能量消耗 [ 战技 ] + /// + [InitOptional] + public virtual double EPCost { get; } = 0; + + /// + /// 基础能量消耗 [ 战技 ] + /// + [InitOptional] + protected virtual double BaseEPCost { get; } = 0; + + /// + /// 冷却时间 + /// + [InitRequired] + public virtual double CD { get; } = 0; + + /// + /// 剩余冷却时间 [ 建议配合 属性使用 ] + /// + public double CurrentCD { get; set; } = 0; + + /// + /// 硬直时间 + /// + [InitRequired] + public virtual double HardnessTime { get; } = 0; + + /// + /// 效果列表 + /// + public HashSet Effects { get; } = []; + + /// + /// 其他参数 + /// + public Dictionary OtherArgs { get; } = []; + + protected Skill(bool active = true, bool magic = true, Character? character = null) + { + IsActive = active; + IsMagic = magic; + Character = character; + } + + protected Skill(bool super = false, Character? character = null) + { + IsSuperSkill = super; + Character = character; + } + + internal Skill() { } + + /// + /// 触发技能升级 + /// + public void OnLevelUp() + { + if (!IsActive) + { + foreach (Effect e in AddInactiveEffectToCharacter()) + { + if (Character != null && !Character.Effects.Contains(e)) + { + Character.Effects.Add(e); + } + } + } + } + + /// + /// 触发技能效果 + /// + public void Trigger(ActionQueue queue, Character actor, List enemys, List teammates) + { + foreach (Effect e in Effects) + { + e.OnSkillCasted(queue, actor, enemys, teammates, OtherArgs); + } + } + + /// + /// 被动技能,需要重写此方法,返回被动特效给角色 [ 此方法会在游戏开始时和技能升级时调用 ] + /// + /// + public virtual IEnumerable AddInactiveEffectToCharacter() + { + return []; + } + + public override string ToString() + { + StringBuilder builder = new(); + + string type = IsSuperSkill ? "【爆发技】" : (IsMagic ? "【魔法】" : (IsActive ? "【主动】" : "【被动】")); + builder.AppendLine(type + Name + " - " + "等级 " + Level); + builder.AppendLine("技能描述:" + Description); + if (IsActive) + { + if (IsSuperSkill) + { + builder.AppendLine("能量消耗:" + EPCost); + } + else + { + if (IsMagic) + { + builder.AppendLine("魔法消耗:" + MPCost); + builder.AppendLine("吟唱时间:" + CastTime); + } + else + { + builder.AppendLine("能量消耗:" + EPCost); + } + } + builder.AppendLine("冷却时间:" + CD); + builder.AppendLine("硬直时间:" + HardnessTime); + } + + return builder.ToString(); } public override bool Equals(IBaseEntity? other) { return other is Skill c && c.Name == Name; } + + /// + /// 等级 + /// + private int _Level = 0; } } diff --git a/Interface/Entity/Property/IActiveEnable.cs b/Interface/Entity/Property/IActiveEnable.cs index e4dfd02..13c37df 100644 --- a/Interface/Entity/Property/IActiveEnable.cs +++ b/Interface/Entity/Property/IActiveEnable.cs @@ -2,7 +2,7 @@ { public interface IActiveEnable { - public bool Active { get; set; } + public bool IsActive { get; set; } public bool Enable { get; set; } } } diff --git a/Library/Common/Addon/Example/ExampleGameModule.cs b/Library/Common/Addon/Example/ExampleGameModule.cs index 147c043..98a1bfb 100644 --- a/Library/Common/Addon/Example/ExampleGameModule.cs +++ b/Library/Common/Addon/Example/ExampleGameModule.cs @@ -334,7 +334,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example List list = []; Skill s = Factory.GetSkill(); s.Name = "Example Skill"; - s.MagicType = MagicType.PurityNatural; + s.Description = "技能应该在GameModule中继承实现,再自行构造。"; list.Add(s); return list; } diff --git a/Library/Common/JsonConverter/CharacterConverter.cs b/Library/Common/JsonConverter/CharacterConverter.cs index 31f5608..1136228 100644 --- a/Library/Common/JsonConverter/CharacterConverter.cs +++ b/Library/Common/JsonConverter/CharacterConverter.cs @@ -78,7 +78,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter result.ExDEF2 = reader.GetDouble(); break; case nameof(Character.MDF): - result.MDF = reader.GetDouble(); + result.MDF = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); break; case nameof(Character.PhysicalPenetration): result.PhysicalPenetration = reader.GetDouble(); @@ -86,6 +86,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Character.MagicalPenetration): result.MagicalPenetration = reader.GetDouble(); break; + case nameof(Character.CharacterState): + result.CharacterState = (CharacterState)reader.GetInt32(); + break; case nameof(Character.InitialHR): result.InitialHR = reader.GetDouble(); break; @@ -172,6 +175,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteNumber(nameof(Character.PrimaryAttribute), (int)value.PrimaryAttribute); writer.WriteNumber(nameof(Character.Level), value.Level); writer.WriteNumber(nameof(Character.EXP), value.EXP); + writer.WriteNumber(nameof(Character.CharacterState), (int)value.CharacterState); writer.WriteNumber(nameof(Character.InitialHP), value.InitialHP); writer.WriteNumber(nameof(Character.ExHP2), value.ExHP2); writer.WriteNumber(nameof(Character.InitialMP), value.InitialMP); @@ -181,7 +185,8 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteNumber(nameof(Character.ExATK2), value.ExATK2); writer.WriteNumber(nameof(Character.InitialDEF), value.InitialDEF); writer.WriteNumber(nameof(Character.ExDEF2), value.ExDEF2); - writer.WriteNumber(nameof(Character.MDF), value.MDF); + writer.WritePropertyName(nameof(Character.MDF)); + JsonSerializer.Serialize(writer, value.MDF, options); writer.WriteNumber(nameof(Character.PhysicalPenetration), value.PhysicalPenetration); writer.WriteNumber(nameof(Character.MagicalPenetration), value.MagicalPenetration); writer.WriteNumber(nameof(Character.InitialHR), value.InitialHR); diff --git a/Library/Common/JsonConverter/MagicResistanceConverter.cs b/Library/Common/JsonConverter/MagicResistanceConverter.cs new file mode 100644 index 0000000..cdd20c5 --- /dev/null +++ b/Library/Common/JsonConverter/MagicResistanceConverter.cs @@ -0,0 +1,150 @@ +using System.Text.Json; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Library.Common.Architecture; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Library.Common.JsonConverter +{ + public class MDFConverter : BaseEntityConverter + { + public override MDF NewInstance() + { + return new(); + } + + public override void ReadPropertyName(ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref MDF result) + { + MagicResistance temp; + switch (propertyName) + { + case nameof(MDF.None): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.None) + { + result.None.Value = temp.Value; + } + break; + case nameof(MDF.Starmark): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.Starmark) + { + result.Starmark.Value = temp.Value; + } + break; + case nameof(MDF.PurityNatural): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.PurityNatural) + { + result.PurityNatural.Value = temp.Value; + } + break; + case nameof(MDF.PurityContemporary): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.PurityContemporary) + { + result.PurityContemporary.Value = temp.Value; + } + break; + case nameof(MDF.Bright): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.Bright) + { + result.Bright.Value = temp.Value; + } + break; + case nameof(MDF.Shadow): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.Shadow) + { + result.Shadow.Value = temp.Value; + } + break; + case nameof(MDF.Element): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.Element) + { + result.Element.Value = temp.Value; + } + break; + case nameof(MDF.Fleabane): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.Fleabane) + { + result.Fleabane.Value = temp.Value; + } + break; + case nameof(MDF.Particle): + temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); + if (temp.MagicType == MagicType.Particle) + { + result.Particle.Value = temp.Value; + } + break; + } + } + + public override void Write(Utf8JsonWriter writer, MDF value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(MDF.None)); + JsonSerializer.Serialize(writer, value.None, options); + + writer.WritePropertyName(nameof(MDF.Starmark)); + JsonSerializer.Serialize(writer, value.Starmark, options); + + writer.WritePropertyName(nameof(MDF.PurityNatural)); + JsonSerializer.Serialize(writer, value.PurityNatural, options); + + writer.WritePropertyName(nameof(MDF.PurityContemporary)); + JsonSerializer.Serialize(writer, value.PurityContemporary, options); + + writer.WritePropertyName(nameof(MDF.Bright)); + JsonSerializer.Serialize(writer, value.Bright, options); + + writer.WritePropertyName(nameof(MDF.Shadow)); + JsonSerializer.Serialize(writer, value.Shadow, options); + + writer.WritePropertyName(nameof(MDF.Element)); + JsonSerializer.Serialize(writer, value.Element, options); + + writer.WritePropertyName(nameof(MDF.Fleabane)); + JsonSerializer.Serialize(writer, value.Fleabane, options); + + writer.WritePropertyName(nameof(MDF.Particle)); + JsonSerializer.Serialize(writer, value.Particle, options); + + writer.WriteEndObject(); + } + } + + public class MagicResistanceConverter : BaseEntityConverter + { + public override MagicResistance NewInstance() + { + return new(); + } + + public override void ReadPropertyName(ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref MagicResistance result) + { + switch (propertyName) + { + case nameof(MagicResistance.MagicType): + result = new((MagicType)reader.GetInt32(), result.Value); + break; + case nameof(MagicResistance.Value): + result.Value = reader.GetDouble(); + break; + } + } + + public override void Write(Utf8JsonWriter writer, MagicResistance value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(nameof(MagicResistance.MagicType), (int)value.MagicType); + writer.WriteNumber(nameof(MagicResistance.Value), value.Value); + writer.WriteEndObject(); + } + } +} diff --git a/Library/Common/JsonConverter/RoomConverter.cs b/Library/Common/JsonConverter/RoomConverter.cs index 4734909..ba33de8 100644 --- a/Library/Common/JsonConverter/RoomConverter.cs +++ b/Library/Common/JsonConverter/RoomConverter.cs @@ -33,8 +33,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter else result.CreateTime = General.DefaultTime; break; case RoomQuery.Column_RoomMaster: - string master = reader.GetString() ?? ""; - result.RoomMaster = JsonSerializer.Deserialize(master, options) ?? General.UnknownUserInstance; + result.RoomMaster = JsonSerializer.Deserialize(ref reader, options) ?? General.UnknownUserInstance; break; case RoomQuery.Column_RoomType: result.RoomType = (RoomType)reader.GetInt64(); @@ -63,7 +62,8 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteNumber(RoomQuery.Column_ID, value.Id); writer.WriteString(RoomQuery.Column_RoomID, value.Roomid); writer.WriteString(RoomQuery.Column_CreateTime, value.CreateTime.ToString(General.GeneralDateTimeFormat)); - writer.WriteString(RoomQuery.Column_RoomMaster, JsonSerializer.Serialize(value.RoomMaster, typeof(User), options)); + writer.WritePropertyName(RoomQuery.Column_RoomMaster); + JsonSerializer.Serialize(writer, value.RoomMaster, options); writer.WriteString(RoomQuery.Column_GameModule, value.GameModule); writer.WriteString(RoomQuery.Column_GameMap, value.GameMap); writer.WriteNumber(RoomQuery.Column_RoomType, (long)value.RoomType); diff --git a/Library/Constant/ConstantSet.cs b/Library/Constant/ConstantSet.cs index e8bd7e7..6601470 100644 --- a/Library/Constant/ConstantSet.cs +++ b/Library/Constant/ConstantSet.cs @@ -285,4 +285,74 @@ namespace Milimoe.FunGame.Core.Library.Constant public const string GuestUserName = "游客用户"; public const string LocalUserName = "本地用户"; } + + public class CharacterActionSet + { + public const string ActionQueue = "ActionQueue"; + public const string Actor = "Actor"; + public const string CastSkill = "CastSkill"; + public const string Enemys = "Enemys"; + public const string Teammates = "Teammates"; + public const string GameMap = "GameMap"; + } + + public class CharacterSet + { + public static string GetPrimaryAttributeName(PrimaryAttribute type) + { + return type switch + { + PrimaryAttribute.AGI => "敏捷", + PrimaryAttribute.INT => "智力", + _ => "力量" + }; + } + + public static string GetMagicName(MagicType type) + { + return type switch + { + MagicType.Starmark => "星痕魔法伤害", + MagicType.PurityNatural => "现代结晶魔法伤害", + MagicType.PurityContemporary => "纯粹结晶魔法伤害", + MagicType.Bright => "光魔法伤害", + MagicType.Shadow => "影魔法伤害", + MagicType.Element => "元素魔法伤害", + MagicType.Fleabane => "紫宛魔法伤害", + MagicType.Particle => "时空魔法伤害", + _ => "魔法伤害", + }; + } + + public static string GetContinuousKilling(int kills) + { + return kills switch + { + 2 => "双杀", + 3 => "三杀", + 4 => "大杀特杀", + 5 => "杀人如麻", + 6 => "主宰比赛", + 7 => "无人能挡", + 8 => "变态杀戮", + 9 => "如同神一般", + 10 => "超越神的杀戮", + _ => "" + }; + } + + public static string GetCharacterState(CharacterState state) + { + return state switch + { + CharacterState.Casting => "角色正在吟唱魔法", + CharacterState.PreCastSuperSkill => "角色预释放了爆发技", + CharacterState.ActionRestricted => "角色现在行动受限", + CharacterState.BattleRestricted => "角色现在战斗不能", + CharacterState.SkillRestricted => "角色现在技能受限", + CharacterState.Neutral => "角色现在是无敌的", + _ => "角色现在完全行动不能" + }; + } + } } diff --git a/Library/Constant/ResultEnum.cs b/Library/Constant/ResultEnum.cs index 856fb00..c8fb21c 100644 --- a/Library/Constant/ResultEnum.cs +++ b/Library/Constant/ResultEnum.cs @@ -57,4 +57,11 @@ namespace Milimoe.FunGame.Core.Library.Constant Fail, NotSend } + + public enum DamageResult + { + Normal, + Critical, + Evaded + } } diff --git a/Library/Constant/TypeEnum.cs b/Library/Constant/TypeEnum.cs index 36b88bd..b004872 100644 --- a/Library/Constant/TypeEnum.cs +++ b/Library/Constant/TypeEnum.cs @@ -262,6 +262,52 @@ namespace Milimoe.FunGame.Core.Library.Constant Particle } + /// + /// 角色目前所处的状态 + /// + public enum CharacterState + { + /// + /// 可以行动 [ 战斗相关 ] + /// + Actionable, + + /// + /// 完全行动不能 [ 战斗相关 ] + /// + NotActionable, + + /// + /// 行动受限 [ 战斗相关 ] + /// + ActionRestricted, + + /// + /// 战斗不能 [ 战斗相关 ] + /// + BattleRestricted, + + /// + /// 技能受限 [ 战斗相关 ] + /// + SkillRestricted, + + /// + /// 处于吟唱中 [ 战斗相关 ] [ 技能相关 ] + /// + Casting, + + /// + /// 预释放爆发技(插队) [ 战斗相关 ] [ 技能相关 ] + /// + PreCastSuperSkill, + + /// + /// 是中立单位(无敌的) [ 战斗相关 ] + /// + Neutral + } + public enum PrimaryAttribute { None, @@ -290,6 +336,16 @@ namespace Milimoe.FunGame.Core.Library.Constant Punish } + public enum CharacterActionType + { + None, + NormalAttack, + PreCastSkill, + CastSkill, + CastSuperSkill, + UseItem + } + public enum VerifyCodeType { NumberVerifyCode, diff --git a/Service/JsonManager.cs b/Service/JsonManager.cs index fddef52..af296ba 100644 --- a/Service/JsonManager.cs +++ b/Service/JsonManager.cs @@ -19,7 +19,7 @@ namespace Milimoe.FunGame.Core.Service Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), ReferenceHandler = ReferenceHandler.IgnoreCycles, Converters = { new DateTimeConverter(), new DataTableConverter(), new DataSetConverter(), new UserConverter(), new RoomConverter(), - new CharacterConverter() } + new CharacterConverter(), new MagicResistanceConverter(), new MDFConverter() } }; /// @@ -56,6 +56,18 @@ namespace Milimoe.FunGame.Core.Service return JsonSerializer.Deserialize(json, GeneralOptions); } + /// + /// 反序列化Json对象,使用 + /// + /// + /// + /// + /// + internal static T? GetObject(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(ref reader, options); + } + /// /// 反序列化Json对象 /// @@ -203,7 +215,7 @@ namespace Milimoe.FunGame.Core.Service internal static List GetObjects(string json) { json = "[" + json.Replace("}{", "},{") + "]"; // 将Json字符串转换为数组 - return JsonSerializer.Deserialize>(json, GeneralOptions) ?? new List(); + return JsonSerializer.Deserialize>(json, GeneralOptions) ?? []; } /// @@ -217,7 +229,7 @@ namespace Milimoe.FunGame.Core.Service internal static List GetObjects(string json, JsonSerializerOptions options) { json = "[" + json.Replace("}{", "},{") + "]"; // 将Json字符串转换为数组 - return JsonSerializer.Deserialize>(json, options) ?? new List(); + return JsonSerializer.Deserialize>(json, options) ?? []; } ///