mirror of
https://github.com/project-redbud/FunGame-Core.git
synced 2025-12-05 08:09:02 +00:00
添加行动顺序表和角色技能操作 (#87)
* 添加行动顺序表和角色技能操作 * 添加更多内容(特效的设计接口等) * 添加爆发技插队和插队保护机制
This commit is contained in:
parent
306b0ec148
commit
3d02cb3db3
@ -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
996
Api/Utility/ActionQueue.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Entity/Character/CharacterProfile.cs
Normal file
62
Entity/Character/CharacterProfile.cs
Normal 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; } = [];
|
||||
}
|
||||
}
|
||||
23
Entity/Character/MagicResistance.cs
Normal file
23
Entity/Character/MagicResistance.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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
312
Entity/Skill/Effect.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Entity/Skill/NormalAttack.cs
Normal file
101
Entity/Skill/NormalAttack.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
{
|
||||
public interface IActiveEnable
|
||||
{
|
||||
public bool Active { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool Enable { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
150
Library/Common/JsonConverter/MagicResistanceConverter.cs
Normal file
150
Library/Common/JsonConverter/MagicResistanceConverter.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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 => "角色现在是无敌的",
|
||||
_ => "角色现在完全行动不能"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,4 +57,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
|
||||
Fail,
|
||||
NotSend
|
||||
}
|
||||
|
||||
public enum DamageResult
|
||||
{
|
||||
Normal,
|
||||
Critical,
|
||||
Evaded
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user