添加行动顺序表和角色技能操作 (#87)

* 添加行动顺序表和角色技能操作

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

* 添加爆发技插队和插队保护机制
This commit is contained in:
milimoe 2024-09-09 01:45:46 +08:00 committed by GitHub
parent 306b0ec148
commit 3d02cb3db3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 2277 additions and 67 deletions

View File

@ -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<Skill>
{
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();
}
}
}

996
Api/Utility/ActionQueue.cs Normal file
View File

@ -0,0 +1,996 @@
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);
}
}
}
}
}
}
}

View File

@ -140,11 +140,10 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary>
/// 获取技能实例
/// </summary>
/// <param name="type">Skill类型 主动 或 被动</param>
/// <returns></returns>
public static Skill GetSkill(SkillType type = SkillType.Passive)
public static Skill GetSkill()
{
return SkillFactory.Create(type);
return SkillFactory.Create();
}
/// <summary>

View File

@ -190,6 +190,15 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns>
public static T? JsonDeserialize<T>(string json) => Service.JsonManager.GetObject<T>(json);
/// <summary>
/// 反序列化Json对象 使用 <paramref name="reader"/> 可指定反序列化选项
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader"></param>
/// <param name="options"></param>
/// <returns></returns>
public static T? JsonDeserialize<T>(ref Utf8JsonReader reader, JsonSerializerOptions options) => Service.JsonManager.GetObject<T>(ref reader, options);
/// <summary>
/// 反序列化Json对象 可指定反序列化选项
/// </summary>

View File

@ -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
{
/// <summary>
/// 角色需要使用 Factory.Get 的方式来构造,并赋值 <see cref="InitRequired"/> 标记的属性<para />
/// 在使用时仅需要调用 <see cref="Copy"/> 方法即可获得相同对象<para />
/// 不建议继承
/// </summary>
public class Character : BaseEntity, ICopyable<Character>
{
/// <summary>
@ -27,9 +34,9 @@ namespace Milimoe.FunGame.Core.Entity
public User User { get; set; }
/// <summary>
/// 角色统计数据
/// 角色的详细资料
/// </summary>
public CharacterStatistics Statistics { get; set; }
public CharacterProfile Profile { get; set; }
/// <summary>
/// 魔法属性
@ -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
/// </summary>
public double EXP { get; set; } = 0;
/// <summary>
/// 角色目前所处的状态 [ 战斗相关 ]
/// </summary>
public CharacterState CharacterState { get; set; } = CharacterState.Actionable;
/// <summary>
/// 初始生命值 [ 初始设定 ]
/// </summary>
@ -147,7 +160,7 @@ namespace Milimoe.FunGame.Core.Entity
/// <summary>
/// 最大生命值 = 基础生命值 + 额外生命值 + 额外生命值2
/// </summary>
public double MaxHP => BaseHP + ExHP + ExHP2;
public double MaxHP => Calculation.Round2Digits(BaseHP + ExHP + ExHP2);
/// <summary>
/// 当前生命值 [ 战斗相关 ]
@ -178,7 +191,7 @@ namespace Milimoe.FunGame.Core.Entity
/// <summary>
/// 最大魔法值 = 基础魔法值 + 额外魔法值 + 额外魔法值2
/// </summary>
public double MaxMP => BaseMP + ExMP + ExMP2;
public double MaxMP => Calculation.Round2Digits(BaseMP + ExMP + ExMP2);
/// <summary>
/// 当前魔法值 [ 战斗相关 ]
@ -188,7 +201,19 @@ namespace Milimoe.FunGame.Core.Entity
/// <summary>
/// 当前爆发能量 [ 战斗相关 ]
/// </summary>
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;
}
}
/// <summary>
/// 初始攻击力 [ 初始设定 ]
@ -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
/// <summary>
/// 攻击力 = 基础攻击力 + 额外攻击力 + 额外攻击力2
/// </summary>
public double ATK => BaseATK + ExATK + ExATK2;
public double ATK => Calculation.Round2Digits(BaseATK + ExATK + ExATK2);
/// <summary>
/// 初始物理护甲 [ 初始设定 ]
@ -275,7 +300,7 @@ namespace Milimoe.FunGame.Core.Entity
/// <summary>
/// 物理护甲 = 基础物理护甲 + 额外物理护甲 + 额外物理护甲2
/// </summary>
public double DEF => BaseDEF + ExDEF + ExDEF2;
public double DEF => Calculation.Round2Digits(BaseDEF + ExDEF + ExDEF2);
/// <summary>
/// 物理伤害减免(%) = [ 与物理护甲相关 ] + 额外物理伤害减免(%)
@ -297,7 +322,7 @@ namespace Milimoe.FunGame.Core.Entity
/// <summary>
/// 魔法抗性(%) [ 与技能和物品相关 ]
/// </summary>
public double MDF { get; set; } = 0;
public MDF MDF { get; set; }
/// <summary>
/// 物理穿透(%) [ 与技能和物品相关 ]
@ -366,6 +391,72 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary>
public double ER { get; set; } = 0;
/// <summary>
/// 核心属性的值 [ 核心属性相关 ]
/// </summary>
public double PrimaryAttributeValue
{
get
{
if (PrimaryAttribute == PrimaryAttribute.AGI)
{
return AGI;
}
else if (PrimaryAttribute == PrimaryAttribute.INT)
{
return INT;
}
else
{
return STR;
}
}
}
/// <summary>
/// 基础核心属性的值 [ 核心属性相关 ]
/// </summary>
public double BasePrimaryAttributeValue
{
get
{
if (PrimaryAttribute == PrimaryAttribute.AGI)
{
return BaseAGI;
}
else if (PrimaryAttribute == PrimaryAttribute.INT)
{
return BaseINT;
}
else
{
return BaseSTR;
}
}
}
/// <summary>
/// 额外核心属性的值 [ 核心属性相关 ]
/// </summary>
public double ExPrimaryAttributeValue
{
get
{
if (PrimaryAttribute == PrimaryAttribute.AGI)
{
return ExAGI;
}
else if (PrimaryAttribute == PrimaryAttribute.INT)
{
return ExINT;
}
else
{
return ExSTR;
}
}
}
/// <summary>
/// 初始力量 [ 初始设定 ]
/// </summary>
@ -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;
/// <summary>
/// 角色的技能组
/// 普通攻击对象
/// </summary>
public Dictionary<string, Skill> Skills { get; set; } = [];
public NormalAttack NormalAttack { get; }
/// <summary>
/// 角色的技能列表
/// </summary>
public HashSet<Skill> Skills { get; } = [];
/// <summary>
/// 角色的持续性特效列表
/// </summary>
public HashSet<Effect> Effects { get; } = [];
/// <summary>
/// 角色携带的物品
/// </summary>
public Dictionary<string, Item> Items { get; set; } = [];
public HashSet<Item> Items { get; } = [];
/**
* ===== =====
@ -573,6 +674,11 @@ namespace Milimoe.FunGame.Core.Entity
/// 等级
/// </summary>
private int _Level = 1;
/// <summary>
/// 能量值
/// </summary>
private double _EP = 0;
/// <summary>
/// 物理穿透
@ -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;
}
/// <summary>
/// 按时间回复状态
/// </summary>
@ -621,6 +729,20 @@ namespace Milimoe.FunGame.Core.Entity
}
}
/// <summary>
/// 提升角色的等级
/// </summary>
/// <param name="up">可为负数</param>
/// <returns></returns>
public int LevelUp(int up = 1)
{
if (up != 0)
{
Level += up;
}
return Level;
}
/// <summary>
/// 比较一个角色(只比较 <see cref="Name"/>
/// </summary>
@ -637,8 +759,7 @@ namespace Milimoe.FunGame.Core.Entity
/// <returns></returns>
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;
}
/// <summary>
/// 获取角色实例的名字、昵称以及所属玩家,包含等级
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 获取角色的名字
/// </summary>
/// <param name="full"></param>
/// <returns>如果 <paramref name="full"/> = false返回 <see cref="FirstName"/>;反之,返回 <see cref="Name"/> + <see cref="FirstName"/>。</returns>
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;
}
}
/// <summary>
/// 获取角色的详细信息
/// </summary>
/// <returns></returns>
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();
}
/// <summary>
/// 复制一个角色
/// [ 推荐从模组中复制后使用对象 ]
@ -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;
}
}
}
}

View File

@ -0,0 +1,62 @@
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Entity
{
public class CharacterProfile(string name, string firstname, string nickname)
{
/// <summary>
/// 角色的姓
/// </summary>
public string Name { get; set; } = name;
/// <summary>
/// 角色的名字
/// </summary>
public string FirstName { get; set; } = firstname;
/// <summary>
/// 角色的昵称
/// </summary>
public string NickName { get; set; } = nickname;
/// <summary>
/// 角色的出生地
/// </summary>
public string Birthplace { get; set; } = "";
/// <summary>
/// 角色的出生日期
/// </summary>
public DateTime Birthday { get; set; } = General.DefaultTime;
/// <summary>
/// 角色的身份
/// </summary>
public string Status { get; set; } = "";
/// <summary>
/// 角色的隶属
/// </summary>
public string Affiliation { get; set; } = "";
/// <summary>
/// 角色的性别
/// </summary>
public string Sex { get; set; } = "";
/// <summary>
/// 角色的身高
/// </summary>
public string Height { get; set; } = "";
/// <summary>
/// 角色的体重
/// </summary>
public string Weight { get; set; } = "";
/// <summary>
/// 角色的故事
/// </summary>
public Dictionary<string, string> Stories { get; set; } = [];
}
}

View File

@ -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;
}
}

View File

@ -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)

312
Entity/Skill/Effect.cs Normal file
View File

@ -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
{
/// <summary>
/// 特殊效果类,需要继承
/// </summary>
public abstract class Effect(Skill skill) : BaseEntity
{
/// <summary>
/// 所属的技能
/// </summary>
public Skill Skill { get; } = skill;
/// <summary>
/// 作用于自身
/// </summary>
public virtual bool TargetSelf { get; } = false;
/// <summary>
/// 作用目标数量
/// </summary>
public virtual int TargetCount { get; } = 0;
/// <summary>
/// 作用范围
/// </summary>
public virtual double TargetRange { get; } = 0;
/// <summary>
/// 持续性的<para/>
/// 配合 <see cref="Duration"/> 使用,而不是 <see cref="DurationTurn"/>。
/// </summary>
public virtual bool Durative { get; } = false;
/// <summary>
/// 持续时间<para/>
/// 配合 <see cref="Durative"/> 使用。
/// </summary>
public virtual double Duration { get; } = 0;
/// <summary>
/// 持续时间(回合)<para/>
/// 使用此属性需要将 <see cref="Durative"/> 设置为 false。
/// </summary>
public virtual double DurationTurn { get; } = 0;
/// <summary>
/// 剩余持续时间
/// </summary>
public double RemainDuration { get; set; } = 0;
/// <summary>
/// 魔法类型
/// </summary>
public virtual MagicType MagicType { get; } = MagicType.None;
/// <summary>
/// 效果描述
/// </summary>
public virtual string Description { get; } = "";
/// <summary>
/// 等级,跟随技能的等级
/// </summary>
public int Level => Skill.Level;
/// <summary>
/// 此特效的施加者,用于溯源
/// </summary>
public Character? Source { get; set; } = null;
/// <summary>
/// 获得此特效时
/// </summary>
/// <param name="character"></param>
public virtual void OnEffectGained(Character character)
{
}
/// <summary>
/// 失去此特效时
/// </summary>
/// <param name="character"></param>
public virtual void OnEffectLost(Character character)
{
}
/// <summary>
/// 在伤害计算前修改预期伤害
/// </summary>
/// <param name="character"></param>
/// <param name="enemy"></param>
/// <param name="damage"></param>
/// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param>
/// <param name="magicType"></param>
/// <param name="newDamage"></param>
/// <returns>返回 true 表示修改了伤害</returns>
public virtual bool AlterExpectedDamageBeforeCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, out double newDamage)
{
newDamage = damage;
return false;
}
/// <summary>
/// 在伤害计算完成后修改实际伤害
/// </summary>
/// <param name="character"></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>
/// <param name="newDamage"></param>
/// <returns>返回 true 表示修改了伤害</returns>
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;
}
/// <summary>
/// 在完成普通攻击动作之后修改硬直时间
/// </summary>
/// <param name="character"></param>
/// <param name="baseHardnessTime"></param>
/// <param name="newHardnessTime"></param>
/// <returns>返回 true 表示修改了硬直时间</returns>
public virtual bool AlterHardnessTimeAfterNormalAttack(Character character, double baseHardnessTime, out double newHardnessTime)
{
newHardnessTime = baseHardnessTime;
return false;
}
/// <summary>
/// 在完成释放技能动作之后修改硬直时间
/// </summary>
/// <param name="character"></param>
/// <param name="baseHardnessTime"></param>
/// <param name="newHardnessTime"></param>
/// <returns>返回 true 表示修改了硬直时间</returns>
public virtual bool AlterHardnessTimeAfterCastSkill(Character character, double baseHardnessTime, out double newHardnessTime)
{
newHardnessTime = baseHardnessTime;
return false;
}
/// <summary>
/// 在造成伤害时,修改获得的能量
/// </summary>
/// <param name="character"></param>
/// <param name="baseEP"></param>
/// <param name="newEP"></param>
/// <returns>返回 true 表示修改了获得的能量</returns>
public virtual bool AlterEPAfterDamage(Character character, double baseEP, out double newEP)
{
newEP = baseEP;
return false;
}
/// <summary>
/// 在受到伤害时,修改获得的能量
/// </summary>
/// <param name="character"></param>
/// <param name="baseEP"></param>
/// <param name="newEP"></param>
/// <returns>返回 true 表示修改了获得的能量</returns>
public virtual bool AlterEPAfterGetDamage(Character character, double baseEP, out double newEP)
{
newEP = baseEP;
return false;
}
/// <summary>
/// 技能开始吟唱时 [ 爆发技插队可触发此项 ]
/// </summary>
/// <param name="caster"></param>
public virtual void OnSkillCasting(Character caster)
{
}
/// <summary>
/// 技能吟唱被打断时
/// </summary>
/// <param name="caster"></param>
/// <param name="interrupter"></param>
public virtual void OnSkillCastInterrupted(Character caster, Character interrupter)
{
}
/// <summary>
/// 吟唱结束后释放技能(魔法)/ 直接释放技能(战技/爆发技)
/// </summary>
/// <param name="queue"></param>
/// <param name="caster"></param>
/// <param name="enemys"></param>
/// <param name="teammates"></param>
/// <param name="others"></param>
public virtual void OnSkillCasted(ActionQueue queue, Character caster, List<Character> enemys, List<Character> teammates, Dictionary<string, object> others)
{
}
/// <summary>
/// 时间流逝时
/// </summary>
/// <param name="character"></param>
/// <param name="elapsed"></param>
public virtual void OnTimeElapsed(Character character, double elapsed)
{
}
/// <summary>
/// 在完成伤害结算后
/// </summary>
/// <param name="character"></param>
/// <param name="enemy"></param>
/// <param name="damage"></param>
/// <param name="isMagicDamage"></param>
/// <param name="magicType"></param>
public virtual void AfterDamageCalculation(Character character, Character enemy, double damage, bool isMagicDamage, MagicType magicType)
{
}
/// <summary>
/// 在特效持有者的回合开始前
/// </summary>
/// <param name="character"></param>
public virtual void OnTurnStart(Character character)
{
}
/// <summary>
/// 在特效持有者的回合开始后
/// </summary>
/// <param name="character"></param>
public virtual void OnTurnEnd(Character character)
{
}
/// <summary>
/// 技能被升级时
/// </summary>
/// <param name="character"></param>
/// <param name="level"></param>
public virtual void OnSkillLevelUp(Character character, double level)
{
}
/// <summary>
/// 特效持有者升级时
/// </summary>
/// <param name="owner"></param>
/// <param name="level"></param>
public virtual void OnOwnerLevelUp(Character owner, double level)
{
}
/// <summary>
/// 在完成死亡结算后 [ 全体广播 ]
/// </summary>
/// <param name="death"></param>
/// <param name="killer"></param>
/// <param name="continuousKilling"></param>
/// <param name="earnedMoney"></param>
public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary<Character, int> continuousKilling, Dictionary<Character, double> earnedMoney)
{
}
/// <summary>
/// 在触发闪避时
/// </summary>
/// <param name="attacker"></param>
/// <param name="evader"></param>
/// <param name="dice"></param>
/// <returns>返回 true 表示无视闪避</returns>
public virtual bool OnEvadedTriggered(Character attacker, Character evader, double dice)
{
return false;
}
/// <summary>
/// 在触发暴击时
/// </summary>
/// <param name="character"></param>
/// <param name="dice"></param>
public virtual void OnCriticalDamageTriggered(Character character, double dice)
{
}
public override bool Equals(IBaseEntity? other)
{
return other is Effect c && c.Name == Name;
}
}
}

View File

@ -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
{
/// <summary>
/// 普通攻击名称
/// </summary>
public override string Name => "普通攻击";
/// <summary>
/// 普通攻击说明
/// </summary>
public string Description => $"对目标敌人造成 {Calculation.Round4Digits((1.0 + 0.05 * (Level - 1)) * 100)}% [ {Damage} ] 点{(IsMagic ? CharacterSet.GetMagicName(MagicType) : "")}伤害。";
/// <summary>
/// 所属的角色
/// </summary>
public Character Character { get; } = character;
/// <summary>
/// 普通攻击的伤害
/// </summary>
public double Damage => Calculation.Round2Digits(Character.ATK * (1.0 + 0.05 * (Level - 1)));
/// <summary>
/// 普通攻击等级
/// </summary>
public int Level
{
get
{
return Math.Max(1, _Level);
}
set
{
_Level = Math.Min(Math.Max(1, value), 8);
}
}
/// <summary>
/// 是否是魔法伤害
/// </summary>
public bool IsMagic { get; } = isMagic;
/// <summary>
/// 魔法伤害需要指定魔法类型
/// </summary>
public MagicType MagicType { get; } = magicType;
/// <summary>
/// 硬直时间
/// </summary>
public double HardnessTime { get; } = 7;
/// <summary>
/// 对目标(或多个目标)发起普通攻击
/// </summary>
/// <param name="queue"></param>
/// <param name="attacker"></param>
/// <param name="enemys"></param>
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();
}
/// <summary>
/// 等级
/// </summary>
private int _Level = 0;
}
}

View File

@ -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
{
/// <summary>
/// 与 <see cref="Character"/> 不同,构造技能时,建议继承此类再构造
/// </summary>
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; }
/// <summary>
/// 此技能所属的角色
/// </summary>
public Character? Character { get; set; } = null;
internal Skill(bool active = false)
/// <summary>
/// 技能描述
/// </summary>
public virtual string Description { get; set; } = "";
/// <summary>
/// 快捷键
/// </summary>
public char Key { get; set; } = '/';
/// <summary>
/// 技能等级,等于 0 时可以称之为尚未学习
/// </summary>
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();
}
}
/// <summary>
/// 是否是主动技能
/// </summary>
[InitRequired]
public bool IsActive { get; set; } = true;
/// <summary>
/// 是否可用
/// </summary>
public bool Enable { get; set; } = true;
/// <summary>
/// 是否是爆发技 [ 此项为最高优先级 ]
/// </summary>
[InitRequired]
public bool IsSuperSkill { get; set; } = false;
/// <summary>
/// 是否属于魔法 [ <see cref="IsActive"/> 会失效 ],反之为战技
/// </summary>
[InitRequired]
public bool IsMagic { get; set; } = true;
/// <summary>
/// 魔法消耗 [ 魔法 ]
/// </summary>
[InitOptional]
public virtual double MPCost { get; } = 0;
/// <summary>
/// 基础魔法消耗 [ 魔法 ]
/// </summary>
[InitOptional]
protected virtual double BaseMPCost { get; } = 0;
/// <summary>
/// 吟唱时间 [ 魔法 ]
/// </summary>
[InitOptional]
public virtual double CastTime { get; } = 0;
/// <summary>
/// 能量消耗 [ 战技 ]
/// </summary>
[InitOptional]
public virtual double EPCost { get; } = 0;
/// <summary>
/// 基础能量消耗 [ 战技 ]
/// </summary>
[InitOptional]
protected virtual double BaseEPCost { get; } = 0;
/// <summary>
/// 冷却时间
/// </summary>
[InitRequired]
public virtual double CD { get; } = 0;
/// <summary>
/// 剩余冷却时间 [ 建议配合 <see cref="Enable"/> 属性使用 ]
/// </summary>
public double CurrentCD { get; set; } = 0;
/// <summary>
/// 硬直时间
/// </summary>
[InitRequired]
public virtual double HardnessTime { get; } = 0;
/// <summary>
/// 效果列表
/// </summary>
public HashSet<Effect> Effects { get; } = [];
/// <summary>
/// 其他参数
/// </summary>
public Dictionary<string, object> 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() { }
/// <summary>
/// 触发技能升级
/// </summary>
public void OnLevelUp()
{
if (!IsActive)
{
foreach (Effect e in AddInactiveEffectToCharacter())
{
if (Character != null && !Character.Effects.Contains(e))
{
Character.Effects.Add(e);
}
}
}
}
/// <summary>
/// 触发技能效果
/// </summary>
public void Trigger(ActionQueue queue, Character actor, List<Character> enemys, List<Character> teammates)
{
foreach (Effect e in Effects)
{
e.OnSkillCasted(queue, actor, enemys, teammates, OtherArgs);
}
}
/// <summary>
/// 被动技能,需要重写此方法,返回被动特效给角色 [ 此方法会在游戏开始时和技能升级时调用 ]
/// </summary>
/// <returns></returns>
public virtual IEnumerable<Effect> 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;
}
/// <summary>
/// 等级
/// </summary>
private int _Level = 0;
}
}

View File

@ -2,7 +2,7 @@
{
public interface IActiveEnable
{
public bool Active { get; set; }
public bool IsActive { get; set; }
public bool Enable { get; set; }
}
}

View File

@ -334,7 +334,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
List<Skill> list = [];
Skill s = Factory.GetSkill();
s.Name = "Example Skill";
s.MagicType = MagicType.PurityNatural;
s.Description = "技能应该在GameModule中继承实现再自行构造。";
list.Add(s);
return list;
}

View File

@ -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<MDF>(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);

View File

@ -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<MDF>
{
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<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.None)
{
result.None.Value = temp.Value;
}
break;
case nameof(MDF.Starmark):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.Starmark)
{
result.Starmark.Value = temp.Value;
}
break;
case nameof(MDF.PurityNatural):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.PurityNatural)
{
result.PurityNatural.Value = temp.Value;
}
break;
case nameof(MDF.PurityContemporary):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.PurityContemporary)
{
result.PurityContemporary.Value = temp.Value;
}
break;
case nameof(MDF.Bright):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.Bright)
{
result.Bright.Value = temp.Value;
}
break;
case nameof(MDF.Shadow):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.Shadow)
{
result.Shadow.Value = temp.Value;
}
break;
case nameof(MDF.Element):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.Element)
{
result.Element.Value = temp.Value;
}
break;
case nameof(MDF.Fleabane):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(ref reader, options) ?? new();
if (temp.MagicType == MagicType.Fleabane)
{
result.Fleabane.Value = temp.Value;
}
break;
case nameof(MDF.Particle):
temp = NetworkUtility.JsonDeserialize<MagicResistance>(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<MagicResistance>
{
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();
}
}
}

View File

@ -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<User>(master, options) ?? General.UnknownUserInstance;
result.RoomMaster = JsonSerializer.Deserialize<User>(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);

View File

@ -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 => "角色现在是无敌的",
_ => "角色现在完全行动不能"
};
}
}
}

View File

@ -57,4 +57,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
Fail,
NotSend
}
public enum DamageResult
{
Normal,
Critical,
Evaded
}
}

View File

@ -262,6 +262,52 @@ namespace Milimoe.FunGame.Core.Library.Constant
Particle
}
/// <summary>
/// 角色目前所处的状态
/// </summary>
public enum CharacterState
{
/// <summary>
/// 可以行动 [ 战斗相关 ]
/// </summary>
Actionable,
/// <summary>
/// 完全行动不能 [ 战斗相关 ]
/// </summary>
NotActionable,
/// <summary>
/// 行动受限 [ 战斗相关 ]
/// </summary>
ActionRestricted,
/// <summary>
/// 战斗不能 [ 战斗相关 ]
/// </summary>
BattleRestricted,
/// <summary>
/// 技能受限 [ 战斗相关 ]
/// </summary>
SkillRestricted,
/// <summary>
/// 处于吟唱中 [ 战斗相关 ] [ 技能相关 ]
/// </summary>
Casting,
/// <summary>
/// 预释放爆发技(插队) [ 战斗相关 ] [ 技能相关 ]
/// </summary>
PreCastSuperSkill,
/// <summary>
/// 是中立单位(无敌的) [ 战斗相关 ]
/// </summary>
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,

View File

@ -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() }
};
/// <summary>
@ -56,6 +56,18 @@ namespace Milimoe.FunGame.Core.Service
return JsonSerializer.Deserialize<T>(json, GeneralOptions);
}
/// <summary>
/// 反序列化Json对象使用 <paramref name="reader"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader"></param>
/// <param name="options"></param>
/// <returns></returns>
internal static T? GetObject<T>(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader, options);
}
/// <summary>
/// 反序列化Json对象
/// </summary>
@ -203,7 +215,7 @@ namespace Milimoe.FunGame.Core.Service
internal static List<T> GetObjects<T>(string json)
{
json = "[" + json.Replace("}{", "},{") + "]"; // 将Json字符串转换为数组
return JsonSerializer.Deserialize<List<T>>(json, GeneralOptions) ?? new List<T>();
return JsonSerializer.Deserialize<List<T>>(json, GeneralOptions) ?? [];
}
/// <summary>
@ -217,7 +229,7 @@ namespace Milimoe.FunGame.Core.Service
internal static List<T> GetObjects<T>(string json, JsonSerializerOptions options)
{
json = "[" + json.Replace("}{", "},{") + "]"; // 将Json字符串转换为数组
return JsonSerializer.Deserialize<List<T>>(json, options) ?? new List<T>();
return JsonSerializer.Deserialize<List<T>>(json, options) ?? [];
}
/// <summary>