From 75d1337ce1106bebda0fb82dce0fc8a434b9ec9d Mon Sep 17 00:00:00 2001
From: milimoe <110188673+milimoe@users.noreply.github.com>
Date: Sun, 10 Nov 2024 00:30:43 +0800
Subject: [PATCH] =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=B7=A5=E5=8E=82=E5=8F=AF?=
=?UTF-8?q?=E4=BB=A5=E6=9E=84=E9=80=A0=E4=B8=BB=E5=8A=A8=E6=8A=80=E8=83=BD?=
=?UTF-8?q?=E4=BA=86=EF=BC=9B=E5=AE=8C=E5=96=84=E6=8A=80=E8=83=BD=E5=BA=95?=
=?UTF-8?q?=E5=B1=82=E9=80=89=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=9B=E5=9B=A2?=
=?UTF-8?q?=E9=98=9F=E6=A8=A1=E5=BC=8F=E5=AE=8C=E5=96=84=EF=BC=9B=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E5=9B=9E=E5=90=88=E6=97=A5=E5=BF=97=20(#99)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 修复诸多复活角色的问题;添加了更多回合记录
* 完善了底层技能选取目标方法;添加筛选条件列表,使技能能够灵活的选取角色
* 优化死亡结算的逻辑;优化回合记录日志
* 现在动态工厂可以构造主动技能了
---
Api/Utility/General.cs | 2 +-
Entity/Character/Character.cs | 17 +-
Entity/Skill/Effect.cs | 33 +-
Entity/Skill/NormalAttack.cs | 15 +-
Entity/Skill/OpenSkill.cs | 86 ++++-
Entity/Skill/Skill.cs | 86 ++++-
Entity/Statistics/CharacterStatistics.cs | 1 +
Entity/System/RoundRecord.cs | 123 +++++-
Entity/System/Season.cs | 2 +-
Interface/Base/IGamingQueue.cs | 24 ++
.../Common/JsonConverter/SkillConverter.cs | 16 +
Library/Constant/ConstantSet.cs | 48 +++
Model/ActionQueue.cs | 364 +++++++++++++-----
Model/EquilibriumConstant.cs | 10 +
14 files changed, 682 insertions(+), 145 deletions(-)
diff --git a/Api/Utility/General.cs b/Api/Utility/General.cs
index f3d055e..a31505d 100644
--- a/Api/Utility/General.cs
+++ b/Api/Utility/General.cs
@@ -675,7 +675,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// 此方法检查一个 百分比(%) 数值是否存在于 [0,1] 区间
///
///
- /// 如果超过0,则返回0;超过1则返回1。
+ /// 如果低于0,则返回0;超过1则返回1。
public static double PercentageCheck(double value)
{
return Math.Max(0, Math.Min(value, 1));
diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs
index 1243518..0ff2089 100644
--- a/Entity/Character/Character.cs
+++ b/Entity/Character/Character.cs
@@ -1307,7 +1307,6 @@ namespace Milimoe.FunGame.Core.Entity
PrimaryAttribute = PrimaryAttribute,
Level = Level,
EXP = EXP,
- CharacterState = CharacterState,
InitialHP = InitialHP,
ExHP2 = ExHP2,
InitialMP = InitialMP,
@@ -1363,8 +1362,9 @@ namespace Milimoe.FunGame.Core.Entity
///
/// 复活此角色,回复出厂状态
///
+ /// 需要一个原始的角色用于还原状态
///
- public void Respawn()
+ public void Respawn(Character original)
{
Item? mcp = UnEquip(EquipSlotType.MagicCardPack);
Item? w = UnEquip(EquipSlotType.Weapon);
@@ -1374,11 +1374,17 @@ namespace Milimoe.FunGame.Core.Entity
Item? ac2 = UnEquip(EquipSlotType.Accessory2);
List skills = new(Skills);
List- items = new(Items);
- Character c = Copy();
+ Character c = original.Copy();
+ List effects = [.. Effects];
+ foreach (Effect e in effects)
+ {
+ e.OnEffectLost(this);
+ }
Effects.Clear();
Skills.Clear();
Items.Clear();
Id = c.Id;
+ Guid = original.Guid;
Name = c.Name;
FirstName = c.FirstName;
NickName = c.NickName;
@@ -1392,6 +1398,10 @@ namespace Milimoe.FunGame.Core.Entity
Level = c.Level;
EXP = c.EXP;
CharacterState = c.CharacterState;
+ CharacterEffectStates.Clear();
+ CharacterEffectTypes.Clear();
+ IsUnselectable = false;
+ UpdateCharacterState();
InitialHP = c.InitialHP;
ExHP2 = c.ExHP2;
InitialMP = c.InitialMP;
@@ -1432,6 +1442,7 @@ namespace Milimoe.FunGame.Core.Entity
Skill newskill = skill.Copy();
newskill.Character = this;
newskill.Level = skill.Level;
+ newskill.CurrentCD = 0;
Skills.Add(newskill);
}
foreach (Item item in items)
diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs
index dc48c7f..4d57522 100644
--- a/Entity/Skill/Effect.cs
+++ b/Entity/Skill/Effect.cs
@@ -22,21 +22,6 @@ namespace Milimoe.FunGame.Core.Entity
///
public virtual EffectType EffectType { get; set; } = EffectType.None;
- ///
- /// 作用于自身
- ///
- public virtual bool TargetSelf { get; set; } = false;
-
- ///
- /// 作用目标数量
- ///
- public virtual int TargetCount { get; set; } = 0;
-
- ///
- /// 作用范围
- ///
- public virtual double TargetRange { get; set; } = 0;
-
///
/// 持续性的
/// 配合 使用,而不是 。
@@ -232,7 +217,8 @@ namespace Milimoe.FunGame.Core.Entity
/// 技能开始吟唱时 [ 爆发技插队可触发此项 ]
///
///
- public virtual void OnSkillCasting(Character caster)
+ ///
+ public virtual void OnSkillCasting(Character caster, List targets)
{
}
@@ -436,6 +422,18 @@ namespace Milimoe.FunGame.Core.Entity
return result;
}
+ ///
+ /// 治疗一个目标 [ 强烈建议使用此方法而不是自行调用 ]
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false)
+ {
+ GamingQueue?.HealToTarget(actor, target, heal, canRespawn);
+ }
+
///
/// 打断施法 [ 尽可能的调用此方法而不是直接调用 ,以防止中断性变更 ]
///
@@ -484,9 +482,6 @@ namespace Milimoe.FunGame.Core.Entity
copy.Name = Name;
copy.Description = Description;
copy.EffectType = EffectType;
- copy.TargetSelf = TargetSelf;
- copy.TargetCount = TargetCount;
- copy.TargetRange = TargetRange;
copy.Durative = Durative;
copy.Duration = Duration;
copy.DurationTurn = DurationTurn;
diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs
index 4891522..de6b497 100644
--- a/Entity/Skill/NormalAttack.cs
+++ b/Entity/Skill/NormalAttack.cs
@@ -15,7 +15,7 @@ namespace Milimoe.FunGame.Core.Entity
///
/// 普通攻击说明
///
- public string Description => $"对目标敌人造成 {(1.0 + 0.05 * (Level - 1)) * 100:0.##}% [ {Damage:0.##} ] 点{(IsMagic ? CharacterSet.GetMagicDamageName(MagicType) : "物理伤害")}。";
+ public string Description => $"对目标敌人造成 {(1.0 + 0.05 * (Level - 1)) * 100:0.##}% 攻击力 [ {Damage:0.##} ] 点{(IsMagic ? CharacterSet.GetMagicDamageName(MagicType) : "物理伤害")}。";
///
/// 所属的角色
@@ -67,10 +67,13 @@ namespace Milimoe.FunGame.Core.Entity
{
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);
- queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic, MagicType, result);
+ if (enemy.HP > 0)
+ {
+ 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);
+ queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic, MagicType, result);
+ }
}
}
@@ -95,7 +98,7 @@ namespace Milimoe.FunGame.Core.Entity
StringBuilder builder = new();
builder.AppendLine(Name + " - 等级 " + Level);
- builder.AppendLine("技能描述:" + Description);
+ builder.AppendLine("描述:" + Description);
builder.AppendLine("硬直时间:" + HardnessTime);
return builder.ToString();
diff --git a/Entity/Skill/OpenSkill.cs b/Entity/Skill/OpenSkill.cs
index 83d90c3..8e82797 100644
--- a/Entity/Skill/OpenSkill.cs
+++ b/Entity/Skill/OpenSkill.cs
@@ -18,8 +18,92 @@ namespace Milimoe.FunGame.Core.Entity
foreach (string str in args.Keys)
{
Values[str] = args[str];
- switch (str)
+ switch (str.ToLower())
{
+ case "active":
+ if (bool.TryParse(args[str].ToString(), out bool isActive) && isActive)
+ {
+ SkillType = SkillType.Item;
+ }
+ break;
+ case "debuff":
+ if (bool.TryParse(args[str].ToString(), out bool isDebuff) && isDebuff)
+ {
+ IsDebuff = isDebuff;
+ }
+ break;
+ case "self":
+ if (bool.TryParse(args[str].ToString(), out bool self))
+ {
+ CanSelectSelf = self;
+ }
+ break;
+ case "enemy":
+ if (bool.TryParse(args[str].ToString(), out bool enemy))
+ {
+ CanSelectEnemy = enemy;
+ }
+ break;
+ case "teammate":
+ if (bool.TryParse(args[str].ToString(), out bool teammate))
+ {
+ CanSelectTeammate = teammate;
+ }
+ break;
+ case "count":
+ if (int.TryParse(args[str].ToString(), out int count) && count > 0)
+ {
+ CanSelectTargetCount = count;
+ }
+ break;
+ case "range":
+ if (int.TryParse(args[str].ToString(), out int range) && range > 0)
+ {
+ CanSelectTargetRange = range;
+ }
+ break;
+ case "mpcost":
+ if (double.TryParse(args[str].ToString(), out double mpcost) && mpcost > 0)
+ {
+ MPCost = mpcost;
+ }
+ break;
+ case "epcost":
+ if (double.TryParse(args[str].ToString(), out double epcost) && epcost > 0)
+ {
+ EPCost = epcost;
+ }
+ break;
+ case "costall":
+ if (bool.TryParse(args[str].ToString(), out bool costall) && costall)
+ {
+ CostAllEP = costall;
+ }
+ break;
+ case "mincost":
+ if (double.TryParse(args[str].ToString(), out double mincost) && mincost > 0)
+ {
+ MinCostEP = mincost;
+ }
+ break;
+ case "cd":
+ if (double.TryParse(args[str].ToString(), out double cd) && cd > 0)
+ {
+ CD = cd;
+ }
+ break;
+ case "cast":
+ if (double.TryParse(args[str].ToString(), out double cast) && cast > 0)
+ {
+ CastTime = cast;
+ }
+ break;
+ case "ht":
+ if (double.TryParse(args[str].ToString(), out double ht) && ht > 0)
+ {
+ HardnessTime = ht;
+ }
+ break;
default:
break;
}
diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs
index 86a48d6..468e1ab 100644
--- a/Entity/Skill/Skill.cs
+++ b/Entity/Skill/Skill.cs
@@ -82,11 +82,26 @@ namespace Milimoe.FunGame.Core.Entity
[InitRequired]
public bool IsMagic => SkillType == SkillType.Magic;
+ ///
+ /// 是否属于 Debuff
+ ///
+ public bool IsDebuff { get; set; } = false;
+
///
/// 可选取自身
///
public virtual bool CanSelectSelf { get; set; } = false;
+ ///
+ /// 可选取敌对角色
+ ///
+ public virtual bool CanSelectEnemy { get; set; } = true;
+
+ ///
+ /// 可选取友方角色
+ ///
+ public virtual bool CanSelectTeammate { get; set; } = false;
+
///
/// 可选取的作用目标数量
///
@@ -97,10 +112,15 @@ namespace Milimoe.FunGame.Core.Entity
///
public virtual double CanSelectTargetRange { get; set; } = 0;
+ ///
+ /// 选取角色的条件
+ ///
+ public List> SelectTargetPredicates { get; } = [];
+
///
/// 实际魔法消耗 [ 魔法 ]
///
- public double RealMPCost => Math.Max(0, MPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * 0.00125)));
+ public double RealMPCost => Math.Max(0, MPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * General.GameplayEquilibriumConstant.INTtoCastMPReduce)));
///
/// 魔法消耗 [ 魔法 ]
@@ -122,7 +142,7 @@ namespace Milimoe.FunGame.Core.Entity
///
/// 实际能量消耗 [ 战技 ]
///
- public double RealEPCost => CostAllEP ? Math.Max(MinCostEP, Character?.EP ?? MinCostEP) : (IsSuperSkill ? EPCost : Math.Max(0, EPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * 0.00075))));
+ public double RealEPCost => CostAllEP ? Math.Max(MinCostEP, Character?.EP ?? MinCostEP) : (IsSuperSkill ? EPCost : Math.Max(0, EPCost * (1 - Calculation.PercentageCheck((Character?.INT ?? 0) * General.GameplayEquilibriumConstant.INTtoCastEPReduce))));
///
/// 能量消耗 [ 战技 ]
@@ -140,10 +160,20 @@ namespace Milimoe.FunGame.Core.Entity
///
public virtual double MinCostEP { get; set; } = 100;
+ ///
+ /// 上一次释放此技能消耗的魔法 [ 魔法 ]
+ ///
+ public double LastCostMP { get; set; } = 0;
+
+ ///
+ /// 上一次释放此技能消耗的能量 [ 战技 ]
+ ///
+ public double LastCostEP { get; set; } = 0;
+
///
/// 实际冷却时间
///
- public double RealCD => Math.Max(0, CD * (1 - Character?.CDR ?? 0));
+ public double RealCD => Math.Max(0, CD * (1 - (Character?.CDR ?? 0)));
///
/// 冷却时间
@@ -247,38 +277,64 @@ namespace Milimoe.FunGame.Core.Entity
///
public virtual List SelectTargets(Character caster, List enemys, List teammates)
{
+ List tobeSelected = [];
+
if (CanSelectSelf)
{
- return [caster];
+ tobeSelected.Add(caster);
+ }
+
+ if (CanSelectEnemy)
+ {
+ tobeSelected.AddRange(enemys);
+ }
+ if (CanSelectTeammate)
+ {
+ tobeSelected.AddRange(teammates);
+ }
+
+ // 筛选出符合条件的角色
+ tobeSelected = [.. tobeSelected.Where(c => SelectTargetPredicates.All(f => f(c)))];
+
+ List targets = [];
+
+ if (tobeSelected.Count <= CanSelectTargetCount)
+ {
+ targets.AddRange(tobeSelected);
}
else
{
- List targets = [];
-
- if (enemys.Count <= CanSelectTargetCount)
- {
- return [.. enemys];
- }
-
- return enemys.OrderBy(x => Random.Shared.Next()).Take(CanSelectTargetCount).ToList();
+ targets.AddRange(tobeSelected.OrderBy(x => Random.Shared.Next()).Take(CanSelectTargetCount));
}
+
+ return [.. targets.Distinct()];
}
-
+
///
/// 技能开始吟唱时 [ 吟唱魔法、释放战技和爆发技、预释放爆发技均可触发 ]
///
///
///
- public void OnSkillCasting(IGamingQueue queue, Character caster)
+ ///
+ public void OnSkillCasting(IGamingQueue queue, Character caster, List targets)
{
GamingQueue = queue;
foreach (Effect e in Effects)
{
e.GamingQueue = GamingQueue;
- e.OnSkillCasting(caster);
+ e.OnSkillCasting(caster, targets);
}
}
+ ///
+ /// 技能效果触发前
+ ///
+ public void BeforeSkillCasted()
+ {
+ LastCostMP = RealMPCost;
+ LastCostEP = RealEPCost;
+ }
+
///
/// 触发技能效果
///
diff --git a/Entity/Statistics/CharacterStatistics.cs b/Entity/Statistics/CharacterStatistics.cs
index eb0a110..52b3628 100644
--- a/Entity/Statistics/CharacterStatistics.cs
+++ b/Entity/Statistics/CharacterStatistics.cs
@@ -43,5 +43,6 @@
public int LastRank { get; set; } = 0;
public double AvgRank { get; set; } = 0;
public double Rating { get; set; } = 0;
+ public int MVPs { get; set; } = 0;
}
}
diff --git a/Entity/System/RoundRecord.cs b/Entity/System/RoundRecord.cs
index 6843c71..7cb08b0 100644
--- a/Entity/System/RoundRecord.cs
+++ b/Entity/System/RoundRecord.cs
@@ -1,14 +1,129 @@
-using Milimoe.FunGame.Core.Library.Constant;
+using System.Text;
+using Milimoe.FunGame.Core.Api.Utility;
+using Milimoe.FunGame.Core.Library.Constant;
-namespace Milimoe.FunGame.Core.Entity.System
+namespace Milimoe.FunGame.Core.Entity
{
- public class RoundRecord(int round, Character actor)
+ public class RoundRecord(int round)
{
public int Round { get; set; } = round;
- public Character Actor { get; set; } = actor;
+ public Character Actor { get; set; } = Factory.GetCharacter();
public CharacterActionType ActionType { get; set; } = CharacterActionType.None;
public List Targets { get; set; } = [];
public Skill? Skill { get; set; } = null;
+ public string SkillCost { get; set; } = "";
public Item? Item { get; set; } = null;
+ public bool HasKill { get; set; } = false;
+ public Dictionary Damages { get; set; } = [];
+ public Dictionary IsCritical { get; set; } = [];
+ public Dictionary Heals { get; set; } = [];
+ public Dictionary Effects { get; set; } = [];
+ public List ActorContinuousKilling { get; set; } = [];
+ public List DeathContinuousKilling { get; set; } = [];
+ public double CastTime { get; set; } = 0;
+ public double HardnessTime { get; set; } = 0;
+ public Dictionary RespawnCountdowns { get; set; } = [];
+ public List Respawns { get; set; } = [];
+ public List RoundRewards { get; set; } = [];
+
+ public override string ToString()
+ {
+ StringBuilder builder = new();
+
+ builder.AppendLine($"=== Round {Round} ===");
+ if (RoundRewards.Count > 0)
+ {
+ builder.AppendLine($"[ {Actor} ] 回合奖励 -> {string.Join(" / ", RoundRewards.Select(s => s.Description)).Trim()}");
+ }
+ if (ActionType == CharacterActionType.NormalAttack || ActionType == CharacterActionType.CastSkill || ActionType == CharacterActionType.CastSuperSkill)
+ {
+ if (ActionType == CharacterActionType.NormalAttack)
+ {
+ builder.Append($"[ {Actor} ] {Actor.NormalAttack.Name} -> ");
+ }
+ else if (ActionType == CharacterActionType.CastSkill || ActionType == CharacterActionType.CastSuperSkill)
+ {
+ if (Skill != null)
+ {
+ builder.Append($"[ {Actor} ] {Skill.Name}({SkillCost})-> ");
+ }
+ else
+ {
+ builder.Append($"释放魔法 -> ");
+ }
+ }
+ builder.AppendLine(string.Join(" / ", GetTargetsState()));
+ if (DeathContinuousKilling.Count > 0) builder.AppendLine($"{string.Join("\r\n", DeathContinuousKilling)}");
+ if (ActorContinuousKilling.Count > 0) builder.AppendLine($"{string.Join("\r\n", ActorContinuousKilling)}");
+ }
+
+ if (ActionType == CharacterActionType.PreCastSkill && Skill != null)
+ {
+ if (Skill.IsMagic)
+ {
+ builder.AppendLine($"[ {Actor} ] 吟唱 [ {Skill.Name} ],持续时间:{CastTime:0.##}");
+ }
+ else
+ {
+ builder.AppendLine($"[ {Actor} ]:{Skill.Name}({SkillCost})-> ");
+ builder.AppendLine(string.Join(" / ", GetTargetsState()));
+ builder.AppendLine($"[ {Actor} ] 回合结束,硬直时间:{HardnessTime:0.##}");
+ }
+ }
+ else
+ {
+ builder.AppendLine($"[ {Actor} ] 回合结束,硬直时间:{HardnessTime:0.##}");
+ }
+
+ foreach (Character character in RespawnCountdowns.Keys)
+ {
+ builder.AppendLine($"[ {character} ] 进入复活倒计时:{RespawnCountdowns[character]:0.##}");
+ }
+
+ foreach (Character character in Respawns)
+ {
+ builder.AppendLine($"[ {character} ] 复活了");
+ }
+
+ return builder.ToString();
+ }
+
+ private List GetTargetsState()
+ {
+ List strings = [];
+ foreach (Character target in Targets.Distinct())
+ {
+ string hasDamage = "";
+ string hasHeal = "";
+ string hasEffect = "";
+ if (Damages.TryGetValue(target, out double damage))
+ {
+ hasDamage = $"伤害:{damage:0.##}";
+ if (IsCritical.TryGetValue(target, out bool isCritical) && isCritical)
+ {
+ hasDamage = "暴击," + hasDamage;
+ }
+ }
+ if (Heals.TryGetValue(target, out double heals))
+ {
+ hasHeal = $"治疗:{heals:0.##}";
+ }
+ if (Effects.TryGetValue(target, out EffectType effectType))
+ {
+ hasEffect = $"施加:{SkillSet.GetEffectTypeName(effectType)}";
+ }
+ if (ActionType == CharacterActionType.NormalAttack && hasDamage == "")
+ {
+ hasDamage = "完美闪避";
+ }
+ if ((ActionType == CharacterActionType.PreCastSkill || ActionType == CharacterActionType.PreCastSkill || ActionType == CharacterActionType.CastSkill) && hasDamage == "" && target != Actor)
+ {
+ hasDamage = "免疫";
+ }
+ string[] strs = [hasDamage, hasHeal, hasEffect];
+ strings.Add($"[ {target}({string.Join(" / ", strs.Where(s => s != ""))})])");
+ }
+ return strings;
+ }
}
}
diff --git a/Entity/System/Season.cs b/Entity/System/Season.cs
index bfeda9f..cf878a4 100644
--- a/Entity/System/Season.cs
+++ b/Entity/System/Season.cs
@@ -1,4 +1,4 @@
-namespace Milimoe.FunGame.Core.Entity.System
+namespace Milimoe.FunGame.Core.Entity
{
public class Season(long id, string name, string description)
{
diff --git a/Interface/Base/IGamingQueue.cs b/Interface/Base/IGamingQueue.cs
index c7c9e5d..7443523 100644
--- a/Interface/Base/IGamingQueue.cs
+++ b/Interface/Base/IGamingQueue.cs
@@ -13,11 +13,26 @@ namespace Milimoe.FunGame.Core.Interface.Base
///
public Action WriteLine { get; }
+ ///
+ /// 原始的角色字典
+ ///
+ public Dictionary Original { get; }
+
///
/// 当前的行动顺序
///
public List Queue { get; }
+ ///
+ /// 上回合记录
+ ///
+ public RoundRecord LastRound { get; set; }
+
+ ///
+ /// 所有回合的记录
+ ///
+ public List Rounds { get; }
+
///
/// 当前已死亡的角色顺序(第一个是最早死的)
///
@@ -63,6 +78,15 @@ namespace Milimoe.FunGame.Core.Interface.Base
///
public void DamageToEnemy(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal);
+ ///
+ /// 治疗一个目标
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false);
+
///
/// 计算物理伤害
///
diff --git a/Library/Common/JsonConverter/SkillConverter.cs b/Library/Common/JsonConverter/SkillConverter.cs
index f7dcc44..73fb483 100644
--- a/Library/Common/JsonConverter/SkillConverter.cs
+++ b/Library/Common/JsonConverter/SkillConverter.cs
@@ -41,6 +41,12 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
case nameof(Skill.CanSelectSelf):
result.CanSelectSelf = reader.GetBoolean();
break;
+ case nameof(Skill.CanSelectEnemy):
+ result.CanSelectEnemy = reader.GetBoolean();
+ break;
+ case nameof(Skill.CanSelectTeammate):
+ result.CanSelectTeammate = reader.GetBoolean();
+ break;
case nameof(Skill.CanSelectTargetCount):
result.CanSelectTargetCount = reader.GetInt32();
break;
@@ -59,6 +65,12 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
case nameof(Skill.EPCost):
result.EPCost = reader.GetDouble();
break;
+ case nameof(Skill.CostAllEP):
+ result.CostAllEP = reader.GetBoolean();
+ break;
+ case nameof(Skill.MinCostEP):
+ result.MinCostEP = reader.GetDouble();
+ break;
case nameof(Skill.CastTime):
result.CastTime = reader.GetDouble();
break;
@@ -100,12 +112,16 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
if (value.Level > 0) writer.WriteNumber(nameof(Skill.Level), value.Level);
writer.WriteNumber(nameof(Skill.SkillType), (int)value.SkillType);
if (value.CanSelectSelf) writer.WriteBoolean(nameof(Skill.CanSelectSelf), value.CanSelectSelf);
+ if (!value.CanSelectEnemy) writer.WriteBoolean(nameof(Skill.CanSelectEnemy), value.CanSelectEnemy);
+ if (value.CanSelectTeammate) writer.WriteBoolean(nameof(Skill.CanSelectTeammate), value.CanSelectTeammate);
if (value.CanSelectTargetCount != 0) writer.WriteNumber(nameof(Skill.CanSelectTargetCount), value.CanSelectTargetCount);
if (value.CanSelectTargetRange != 0) writer.WriteNumber(nameof(Skill.CanSelectTargetRange), value.CanSelectTargetRange);
if (!value.Enable) writer.WriteBoolean(nameof(Skill.Enable), value.Enable);
if (value.IsInEffect) writer.WriteBoolean(nameof(Skill.IsInEffect), value.IsInEffect);
if (value.MPCost > 0) writer.WriteNumber(nameof(Skill.MPCost), value.MPCost);
if (value.EPCost > 0) writer.WriteNumber(nameof(Skill.EPCost), value.EPCost);
+ if (value.CostAllEP) writer.WriteBoolean(nameof(Skill.CostAllEP), value.CostAllEP);
+ if (value.MinCostEP > 0) writer.WriteNumber(nameof(Skill.MinCostEP), value.MinCostEP);
if (value.CastTime > 0) writer.WriteNumber(nameof(Skill.CastTime), value.CastTime);
if (value.CD > 0) writer.WriteNumber(nameof(Skill.CD), value.CD);
if (value.CurrentCD > 0) writer.WriteNumber(nameof(Skill.CurrentCD), value.CurrentCD);
diff --git a/Library/Constant/ConstantSet.cs b/Library/Constant/ConstantSet.cs
index 9cb6f9b..aef6bef 100644
--- a/Library/Constant/ConstantSet.cs
+++ b/Library/Constant/ConstantSet.cs
@@ -521,5 +521,53 @@ namespace Milimoe.FunGame.Core.Library.Constant
_ => General.GameplayEquilibriumConstant.MaxPassiveSkillLevel
};
}
+
+ public static string GetEffectTypeName(EffectType type)
+ {
+ return type switch
+ {
+ EffectType.None => "无特殊效果",
+ EffectType.Item => "装备特效",
+ EffectType.Mark => "标记",
+ EffectType.Stun => "眩晕",
+ EffectType.Freeze => "冰冻",
+ EffectType.Silence => "沉默",
+ EffectType.Root => "定身",
+ EffectType.Fear => "恐惧",
+ EffectType.Sleep => "睡眠",
+ EffectType.Knockback => "击退",
+ EffectType.Knockdown => "击倒",
+ EffectType.Taunt => "嘲讽",
+ EffectType.Slow => "减速",
+ EffectType.Weaken => "衰弱",
+ EffectType.Poison => "中毒",
+ EffectType.Burn => "燃烧",
+ EffectType.Bleed => "流血",
+ EffectType.Blind => "致盲",
+ EffectType.Cripple => "致残",
+ EffectType.Shield => "护盾",
+ EffectType.HealOverTime => "持续治疗",
+ EffectType.Haste => "加速",
+ EffectType.Invulnerable => "无敌",
+ EffectType.Unselectable => "不可选中",
+ EffectType.DamageBoost => "伤害提升",
+ EffectType.DefenseBoost => "防御提升",
+ EffectType.CritBoost => "暴击提升",
+ EffectType.ManaRegen => "魔法恢复",
+ EffectType.ArmorBreak => "破甲",
+ EffectType.MagicResistBreak => "降低魔抗",
+ EffectType.Curse => "诅咒",
+ EffectType.Exhaustion => "疲劳",
+ EffectType.ManaBurn => "魔力燃烧",
+ EffectType.Charm => "魅惑",
+ EffectType.Disarm => "缴械",
+ EffectType.Confusion => "混乱",
+ EffectType.Petrify => "石化",
+ EffectType.SilenceMagic => "法术沉默",
+ EffectType.Banish => "放逐",
+ EffectType.Doom => "毁灭",
+ _ => "未知效果"
+ };
+ }
}
}
diff --git a/Model/ActionQueue.cs b/Model/ActionQueue.cs
index 2e26e7b..261b306 100644
--- a/Model/ActionQueue.cs
+++ b/Model/ActionQueue.cs
@@ -1,6 +1,5 @@
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
-using Milimoe.FunGame.Core.Entity.System;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Constant;
@@ -16,6 +15,11 @@ namespace Milimoe.FunGame.Core.Model
///
public Action WriteLine { get; }
+ ///
+ /// 原始的角色字典
+ ///
+ public Dictionary Original => _original;
+
///
/// 当前的行动顺序
///
@@ -82,8 +86,18 @@ namespace Milimoe.FunGame.Core.Model
///
/// 上回合记录
///
- public RoundRecord LastRound { get; set; } = new(0, Factory.GetCharacter());
+ public RoundRecord LastRound { get; set; } = new(0);
+
+ ///
+ /// 所有回合的记录
+ ///
+ public List Rounds { get; } = [];
+ ///
+ /// 原始的角色字典
+ ///
+ protected readonly Dictionary _original = [];
+
///
/// 当前的行动顺序
///
@@ -119,6 +133,11 @@ namespace Milimoe.FunGame.Core.Model
///
protected readonly Dictionary _earnedMoney = [];
+ ///
+ /// 角色最高连杀数
+ ///
+ protected readonly Dictionary _maxContinuousKilling = [];
+
///
/// 角色目前的连杀数
///
@@ -154,6 +173,11 @@ namespace Milimoe.FunGame.Core.Model
///
protected readonly Dictionary _respawnCountdown = [];
+ ///
+ /// 当前回合死亡角色
+ ///
+ protected readonly List _roundDeaths = [];
+
///
/// 是否是团队模式
///
@@ -202,6 +226,15 @@ namespace Milimoe.FunGame.Core.Model
///
public void InitCharacterQueue(List characters)
{
+ // 保存原始的角色信息。用于复活时还原状态
+ foreach (Character character in characters)
+ {
+ Character original = character.Copy();
+ original.Guid = Guid.NewGuid();
+ character.Guid = original.Guid;
+ _original.Add(original.Guid, original);
+ }
+
// 初始排序:按速度排序
List> groupedBySpeed = [.. characters
.GroupBy(c => c.SPD)
@@ -291,6 +324,7 @@ namespace Milimoe.FunGame.Core.Model
public void ClearQueue()
{
FirstKiller = null;
+ _original.Clear();
_queue.Clear();
_hardnessTimes.Clear();
_assistDamage.Clear();
@@ -298,6 +332,7 @@ namespace Milimoe.FunGame.Core.Model
_cutCount.Clear();
_castingSkills.Clear();
_castingSuperSkills.Clear();
+ _maxContinuousKilling.Clear();
_continuousKilling.Clear();
_earnedMoney.Clear();
_eliminated.Clear();
@@ -491,8 +526,8 @@ namespace Milimoe.FunGame.Core.Model
/// 是否结束游戏
public bool ProcessTurn(Character character)
{
- TotalRound++;
- LastRound = new(TotalRound, character);
+ LastRound.Actor = character;
+ _roundDeaths.Clear();
if (!BeforeTurn(character))
{
@@ -696,7 +731,7 @@ namespace Milimoe.FunGame.Core.Model
character.CharacterState = CharacterState.Casting;
_castingSkills.Add(character, new(skill, targets));
baseTime = skill.CastTime;
- skill.OnSkillCasting(this, character);
+ skill.OnSkillCasting(this, character, targets);
}
}
else
@@ -708,12 +743,14 @@ namespace Milimoe.FunGame.Core.Model
if (!cancel)
{
decided = true;
- skill.OnSkillCasting(this, character);
+ skill.OnSkillCasting(this, character, targets);
+ skill.BeforeSkillCasted();
character.EP -= cost;
baseTime = skill.HardnessTime;
skill.CurrentCD = skill.RealCD;
skill.Enable = false;
+ LastRound.SkillCost = $"{-cost:0.##} EP";
WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}");
skill.OnSkillCasted(this, character, targets);
@@ -742,10 +779,13 @@ namespace Milimoe.FunGame.Core.Model
// 判断是否能够释放技能
if (CheckCanCast(character, skill, out double cost))
{
+ skill.BeforeSkillCasted();
+
character.MP -= cost;
baseTime = skill.HardnessTime;
skill.CurrentCD = skill.RealCD;
skill.Enable = false;
+ LastRound.SkillCost = $"{-cost:0.##} MP";
WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点魔法值,释放了魔法 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}");
skill.OnSkillCasted(this, character, targets);
@@ -779,10 +819,13 @@ namespace Milimoe.FunGame.Core.Model
List targets = SelectTargets(character, skill, enemys, teammates, out _);
LastRound.Targets = [.. targets];
+ skill.BeforeSkillCasted();
+
character.EP -= cost;
baseTime = skill.HardnessTime;
skill.CurrentCD = skill.RealCD;
skill.Enable = false;
+ LastRound.SkillCost = $"{-cost:0.##} EP";
WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量值,释放了爆发技 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}");
skill.OnSkillCasted(this, character, targets);
@@ -811,13 +854,16 @@ namespace Milimoe.FunGame.Core.Model
WriteLine("[ " + character + $" ] 放弃了行动!");
}
+ LastRound.ActionType = type;
+
+ // 统一在回合结束时处理角色的死亡
+ ProcessCharacterDeath(character);
+
if (_isGameEnd)
{
return _isGameEnd;
}
- LastRound.ActionType = type;
-
// 减少硬直时间
double newHardnessTime = baseTime;
if (character.CharacterState != CharacterState.Casting)
@@ -829,8 +875,10 @@ namespace Milimoe.FunGame.Core.Model
{
newHardnessTime = Math.Max(0, Calculation.Round2Digits(baseTime * (1 - character.AccelerationCoefficient)));
WriteLine("[ " + character + " ] 进行吟唱,持续时间: " + newHardnessTime);
+ LastRound.CastTime = newHardnessTime;
}
AddCharacter(character, newHardnessTime, isCheckProtected);
+ LastRound.HardnessTime = newHardnessTime;
// 有人想要插队吗?
WillPreCastSuperSkill(character);
@@ -885,10 +933,38 @@ namespace Milimoe.FunGame.Core.Model
// 获取第一个角色的硬直时间
double timeToReduce = _hardnessTimes[_queue[0]];
- TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce);
+ // 如果复活时间更快,应该先流逝复活时间
+ if (_respawnCountdown.Count != 0)
+ {
+ double timeToRespawn = _respawnCountdown.Values.Min();
+ if (timeToRespawn < timeToReduce)
+ {
+ timeToReduce = Calculation.Round2Digits(timeToRespawn);
+ }
+ }
+ TotalTime = Calculation.Round2Digits(TotalTime + timeToReduce);
WriteLine("时间流逝:" + timeToReduce);
+ // 减少复活倒计时
+ foreach (Character character in _respawnCountdown.Keys)
+ {
+ _respawnCountdown[character] = Calculation.Round2Digits(_respawnCountdown[character] - timeToReduce);
+ if (_respawnCountdown[character] <= 0)
+ {
+ double hardnessTime = 5;
+ character.Respawn(_original[character.Guid]);
+ WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} 硬直时间。");
+ AddCharacter(character, hardnessTime, false);
+ LastRound.Respawns.Add(character);
+ _respawnCountdown.Remove(character);
+ if (!_respawnTimes.TryAdd(character, 1))
+ {
+ _respawnTimes[character] += 1;
+ }
+ }
+ }
+
foreach (Character character in _queue)
{
// 减少所有角色的硬直时间
@@ -970,25 +1046,13 @@ namespace Milimoe.FunGame.Core.Model
}
}
- // 减少复活倒计时
- foreach (Character character in _respawnCountdown.Keys)
- {
- _respawnCountdown[character] -= timeToReduce;
- if (_respawnCountdown[character] <= 0)
- {
- double hardnessTime = 5;
- character.Respawn();
- WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} 硬直时间。");
- AddCharacter(character, hardnessTime, false);
- _respawnCountdown.Remove(character);
- if (!_respawnTimes.TryAdd(character, 1))
- {
- _respawnTimes[character] += 1;
- }
- }
- }
-
WriteLine("\r\n");
+
+ // 在时间流逝后,进入下一回合
+ TotalRound++;
+ LastRound = new(TotalRound);
+ Rounds.Add(LastRound);
+
return timeToReduce;
}
@@ -1010,6 +1074,10 @@ namespace Milimoe.FunGame.Core.Model
return;
}
+ if (!LastRound.IsCritical.TryAdd(enemy, damageResult == DamageResult.Critical) && damageResult == DamageResult.Critical)
+ {
+ LastRound.IsCritical[enemy] = true;
+ }
bool isEvaded = damageResult == DamageResult.Evaded;
List effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList();
foreach (Effect effect in effects)
@@ -1062,72 +1130,55 @@ namespace Milimoe.FunGame.Core.Model
effect.AfterDamageCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult);
}
- if (enemy.HP <= 0 && !_eliminated.Contains(enemy))
+ if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy))
{
+ LastRound.HasKill = true;
+ _roundDeaths.Add(enemy);
DeathCalculation(actor, enemy);
- // 给所有角色的特效广播角色死亡结算
- effects = _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList();
- foreach (Effect effect in effects)
- {
- effect.AfterDeathCalculation(enemy, actor, _continuousKilling, _earnedMoney);
- }
- // 将死者移出队列
- _queue.Remove(enemy);
- if (_isTeamMode)
- {
- Team? killTeam = GetTeam(actor);
- Team? deathTeam = GetTeam(enemy);
- if (deathTeam != null)
- {
- if (deathTeam.GetActiveCharacters(this).Count == 0)
- {
- // 团灭了
- _eliminatedTeams.Add(deathTeam);
- _teams.Remove(deathTeam.Name);
- }
- else
- {
- List remain = deathTeam.GetActiveTeammates(this, enemy);
- int remainCount = remain.Count;
- if (remainCount > 0) WriteLine($"[ {deathTeam} ] 剩余成员:[ {string.Join(" ] / [ ", remain)} ]({remainCount} 人)");
- }
- }
+ }
+ }
- if (killTeam != null)
- {
- if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin)
- {
- List combinedTeams = [.. _eliminatedTeams, .. _teams.Values];
- combinedTeams.Remove(killTeam);
- _eliminatedTeams.Clear();
- _eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score));
- EndGameInfo(killTeam);
- return;
- }
- if (!_teams.Keys.Where(str => str != killTeam.Name).Any())
- {
- // 没有其他的团队了,游戏结束
- EndGameInfo(killTeam);
- return;
- }
- else if (killTeam != null)
- {
- List actives = killTeam.GetActiveCharacters(this);
- actives.Add(actor);
- int remainCount = actives.Count;
- if (remainCount > 0) WriteLine($"[ {killTeam} ] 剩余成员:[ {string.Join(" ] / [ ", actives)} ]({remainCount} 人)");
- }
- }
+ ///
+ /// 治疗一个目标
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false)
+ {
+ if (target.HP == target.MaxHP)
+ {
+ return;
+ }
+
+ bool isDead = target.HP <= 0;
+
+ if (heal < 0) heal = 0;
+ if (target.HP > 0 || (isDead && canRespawn))
+ {
+ target.HP += heal;
+ if (!LastRound.Heals.TryAdd(target, heal))
+ {
+ LastRound.Heals[target] += heal;
+ }
+ }
+
+ if (isDead && canRespawn)
+ {
+ if (target != actor)
+ {
+ WriteLine($"[ {target} ] 被 [ {actor} ] 复苏了,并回复了 {heal:0.##} 点生命值!!");
}
else
{
- if (!_queue.Where(c => c != actor).Any())
- {
- // 没有其他的角色了,游戏结束
- EndGameInfo(actor);
- }
+ WriteLine($"[ {target} ] 复苏了,并回复了 {heal:0.##} 点生命值!!");
}
}
+ else
+ {
+ WriteLine($"[ {target} ] 回复了 {heal:0.##} 点生命值!");
+ }
}
///
@@ -1211,10 +1262,10 @@ namespace Milimoe.FunGame.Core.Model
double penetratedDEF = (1 - actor.PhysicalPenetration) * enemy.DEF;
// 物理伤害减免
- double physicalDamageReduction = penetratedDEF / (penetratedDEF + 120);
+ double physicalDamageReduction = penetratedDEF / (penetratedDEF + General.GameplayEquilibriumConstant.DEFReductionFactor);
// 最终的物理伤害
- finalDamage = expectedDamage * (1 - physicalDamageReduction);
+ finalDamage = expectedDamage * (1 - Calculation.PercentageCheck(physicalDamageReduction + enemy.ExPDR));
// 暴击判定
effects = actor.Effects.Where(e => e.Level > 0).ToList();
@@ -1355,6 +1406,79 @@ namespace Milimoe.FunGame.Core.Model
return DamageResult.Normal;
}
+ public void ProcessCharacterDeath(Character character)
+ {
+ foreach (Character enemy in _roundDeaths)
+ {
+ // 给所有角色的特效广播角色死亡结算
+ List effects = _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList();
+ foreach (Effect effect in effects)
+ {
+ effect.AfterDeathCalculation(enemy, character, _continuousKilling, _earnedMoney);
+ }
+ // 将死者移出队列
+ _queue.Remove(enemy);
+ if (_isTeamMode)
+ {
+ Team? killTeam = GetTeam(character);
+ Team? deathTeam = GetTeam(enemy);
+
+ if (MaxRespawnTimes != 0)
+ {
+ WriteLine($"\r\n=== 当前死亡竞赛比分 ===\r\n{string.Join("\r\n", Teams.OrderByDescending(kv => kv.Value.Score).Select(kv => kv.Key + ":" + kv.Value.Score + "(剩余存活人数:" + kv.Value.GetActiveCharacters(this).Count + ")"))}");
+ }
+
+ if (deathTeam != null)
+ {
+ List remain = deathTeam.GetActiveCharacters(this);
+ int remainCount = remain.Count;
+ if (remainCount == 0)
+ {
+ // 团灭了
+ _eliminatedTeams.Add(deathTeam);
+ _teams.Remove(deathTeam.Name);
+ }
+ else if (MaxRespawnTimes == 0)
+ {
+ WriteLine($"[ {deathTeam} ] 剩余成员:[ {string.Join(" ] / [ ", remain)} ]({remainCount} 人)");
+ }
+ }
+
+ if (killTeam != null)
+ {
+ List actives = killTeam.GetActiveCharacters(this);
+ actives.Add(character);
+ int remainCount = actives.Count;
+ if (remainCount > 0 && MaxRespawnTimes == 0)
+ {
+ WriteLine($"[ {killTeam} ] 剩余成员:[ {string.Join(" ] / [ ", actives)} ]({remainCount} 人)");
+ }
+ if (!_teams.Keys.Where(str => str != killTeam.Name).Any())
+ {
+ // 没有其他的团队了,游戏结束
+ EndGameInfo(killTeam);
+ }
+ if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin)
+ {
+ List combinedTeams = [.. _eliminatedTeams, .. _teams.Values];
+ combinedTeams.Remove(killTeam);
+ _eliminatedTeams.Clear();
+ _eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score));
+ EndGameInfo(killTeam);
+ }
+ }
+ }
+ else
+ {
+ if (!_queue.Where(c => c != character).Any())
+ {
+ // 没有其他的角色了,游戏结束
+ EndGameInfo(character);
+ }
+ }
+ }
+ }
+
///
/// 死亡结算
///
@@ -1363,6 +1487,10 @@ namespace Milimoe.FunGame.Core.Model
public void DeathCalculation(Character killer, Character death)
{
if (!_continuousKilling.TryAdd(killer, 1)) _continuousKilling[killer] += 1;
+ if (!_maxContinuousKilling.TryAdd(killer, 1) && _continuousKilling[killer] > _maxContinuousKilling[killer])
+ {
+ _maxContinuousKilling[killer] = _continuousKilling[killer];
+ }
_stats[killer].Kills += 1;
_stats[death].Deaths += 1;
int money = Random.Shared.Next(250, 350);
@@ -1392,6 +1520,7 @@ namespace Milimoe.FunGame.Core.Model
money += (coefficient + 1) * Random.Shared.Next(50, 100);
string termination = CharacterSet.GetContinuousKilling(coefficient);
string msg = $"[ {killer} ] 终结了 [ {death} ]{(termination != "" ? " 的" + termination : "")},获得 {money} {General.GameplayEquilibriumConstant.InGameCurrency}!";
+ LastRound.DeathContinuousKilling.Add(msg);
if (assists.Length > 1)
{
msg += "助攻:[ " + string.Join(" ] / [ ", assists.Where(c => c != killer)) + " ]";
@@ -1401,6 +1530,7 @@ namespace Milimoe.FunGame.Core.Model
else
{
string msg = $"[ {killer} ] 杀死了 [ {death} ],获得 {money} {General.GameplayEquilibriumConstant.InGameCurrency}!";
+ LastRound.DeathContinuousKilling.Add(msg);
if (assists.Length > 1)
{
msg += "助攻:[ " + string.Join(" ] / [ ", assists.Where(c => c != killer)) + " ]";
@@ -1419,21 +1549,27 @@ namespace Milimoe.FunGame.Core.Model
int kills = _continuousKilling[killer];
string continuousKilling = CharacterSet.GetContinuousKilling(kills);
+ string actorContinuousKilling = "";
if (kills == 2 || kills == 3)
{
- WriteLine("[ " + killer + " ] 完成了" + continuousKilling + "!");
+ actorContinuousKilling = "[ " + killer + " ] 完成了一次" + continuousKilling + "!";
}
else if (kills == 4)
{
- WriteLine("[ " + killer + " ] 正在" + continuousKilling + "!");
+ actorContinuousKilling = "[ " + killer + " ] 正在" + continuousKilling + "!";
}
else if (kills > 4 && kills < 10)
{
- WriteLine("[ " + killer + " ] 已经" + continuousKilling + "!");
+ actorContinuousKilling = "[ " + killer + " ] 已经" + continuousKilling + "!";
}
else if (kills >= 10)
{
- WriteLine("[ " + killer + " ] 已经" + continuousKilling + "!拜托谁去杀了他吧!!!");
+ actorContinuousKilling = "[ " + killer + " ] 已经" + continuousKilling + "!拜托谁去杀了他吧!!!";
+ }
+ if (actorContinuousKilling != "")
+ {
+ LastRound.ActorContinuousKilling.Add(actorContinuousKilling);
+ WriteLine(actorContinuousKilling);
}
if (!_earnedMoney.TryAdd(killer, money)) _earnedMoney[killer] += money;
@@ -1467,8 +1603,34 @@ namespace Milimoe.FunGame.Core.Model
else
{
// 进入复活倒计时
- double respawnTime = Math.Min(90, death.Level * 0.36 + times * 2.77);
+ double respawnTime = Calculation.Round2Digits(Math.Min(90, death.Level * 0.36 + times * 2.77 + kills * Random.Shared.Next(0, 3)));
_respawnCountdown.TryAdd(death, respawnTime);
+ LastRound.RespawnCountdowns.TryAdd(death, respawnTime);
+ WriteLine($"[ {death} ] 进入复活倒计时:{respawnTime:0.##} 时间!");
+ }
+
+ // 移除死者的施法
+ _castingSkills.Remove(death);
+ _castingSuperSkills.Remove(death);
+
+ // 因丢失目标而中断施法
+ List castingSkills = [.. _castingSkills.Keys];
+ foreach (Character caster in castingSkills)
+ {
+ SkillTarget st = _castingSkills[caster];
+ if (st.Targets.Remove(death) && st.Targets.Count == 0)
+ {
+ _castingSkills.Remove(caster);
+ if (caster.CharacterState == CharacterState.Casting)
+ {
+ caster.CharacterState = CharacterState.Actionable;
+ }
+ WriteLine($"[ {caster} ] 终止了 [ {st.Skill.Name} ] 的施法" + (_hardnessTimes[caster] > 3 ? ",并获得了 3 硬直时间的补偿。" : "。"));
+ if (_hardnessTimes[caster] > 3)
+ {
+ _hardnessTimes[caster] = 3;
+ }
+ }
}
}
@@ -1489,7 +1651,7 @@ namespace Milimoe.FunGame.Core.Model
CharacterStatistics statistics = CharacterStatistics[ec];
string topCharacter = ec.ToString() +
(statistics.FirstKills > 0 ? " [ 第一滴血 ]" : "") +
- (_continuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") +
+ (_maxContinuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") +
(_earnedMoney.TryGetValue(ec, out int earned) ? $" [ 已赚取 {earned} {General.GameplayEquilibriumConstant.InGameCurrency} ]" : "");
if (top == 1)
{
@@ -1571,7 +1733,7 @@ namespace Milimoe.FunGame.Core.Model
string topCharacter = respawning + ec.ToString() +
(statistics.FirstKills > 0 ? " [ 第一滴血 ]" : "") +
- (_continuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") +
+ (_maxContinuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") +
(_earnedMoney.TryGetValue(ec, out int earned) ? $" [ 已赚取 {earned} {General.GameplayEquilibriumConstant.InGameCurrency} ]" : "") +
$"({statistics.Kills} / {statistics.Assists}{(MaxRespawnTimes != 0 ? " / " + statistics.Deaths : "")})";
topTeam += topCharacter + "\r\n";
@@ -1671,7 +1833,7 @@ namespace Milimoe.FunGame.Core.Model
_hardnessTimes[c] = Calculation.Round2Digits(_hardnessTimes[c] + 0.01);
}
}
- skill.OnSkillCasting(this, other);
+ skill.OnSkillCasting(this, other, []);
}
}
}
@@ -1779,6 +1941,14 @@ namespace Milimoe.FunGame.Core.Model
}
statsTaken.TotalTakenDamage = Calculation.Round2Digits(statsTaken.TotalTakenDamage + damage);
}
+ if (LastRound.Damages.TryGetValue(characterTaken, out double damageTotal))
+ {
+ LastRound.Damages[characterTaken] = damageTotal + damage;
+ }
+ else
+ {
+ LastRound.Damages[characterTaken] = damage;
+ }
}
///
@@ -1837,6 +2007,10 @@ namespace Milimoe.FunGame.Core.Model
cancel = false;
if (skill.SkillType == SkillType.SuperSkill) cancel = false;
List targets = skill.SelectTargets(caster, enemys, teammates);
+ if (targets.Count == 0)
+ {
+ cancel = true;
+ }
return targets;
}
diff --git a/Model/EquilibriumConstant.cs b/Model/EquilibriumConstant.cs
index af1c6ea..4c36a82 100644
--- a/Model/EquilibriumConstant.cs
+++ b/Model/EquilibriumConstant.cs
@@ -224,6 +224,16 @@
/// 每 1 点智力增加魔法回复力
///
public double INTtoMRFactor { get; set; } = 0.1;
+
+ ///
+ /// 每 1 点智力减少魔法消耗
+ ///
+ public double INTtoCastMPReduce { get; set; } = 0.00125;
+
+ ///
+ /// 每 1 点智力减少能量消耗
+ ///
+ public double INTtoCastEPReduce { get; set; } = 0.00075;
///
/// 每 1 点敏捷增加行动速度