From 914f6c6b4aa872c34e3f8b8f7cc15554829820ac Mon Sep 17 00:00:00 2001
From: milimoe <110188673+milimoe@users.noreply.github.com>
Date: Fri, 30 May 2025 01:17:34 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8A=80=E8=83=BD=E3=80=81=E5=9B=9E=E5=90=88?=
=?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BC=98=E5=8C=96=20(#138)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Entity/Character/Character.cs | 11 +-
Entity/Skill/Skill.cs | 29 ++-
Entity/System/RoundRecord.cs | 1 +
.../JsonConverter/RoundRecordConverter.cs | 233 ++++++++++++++++++
Library/Constant/ConstantSet.cs | 3 +-
Model/GamingQueue.cs | 22 +-
Service/JsonManager.cs | 2 +-
7 files changed, 284 insertions(+), 17 deletions(-)
create mode 100644 Library/Common/JsonConverter/RoundRecordConverter.cs
diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs
index f9c6d13..5d698ce 100644
--- a/Entity/Character/Character.cs
+++ b/Entity/Character/Character.cs
@@ -1353,7 +1353,7 @@ namespace Milimoe.FunGame.Core.Entity
double exATK = ExATK + ExATK2 + ExATK3;
builder.AppendLine($"攻击力:{ATK:0.##}" + (exATK != 0 ? $" [{BaseATK:0.##} {(exATK >= 0 ? "+" : "-")} {Math.Abs(exATK):0.##}]" : ""));
double exDEF = ExDEF + ExDEF2 + ExDEF3;
- builder.AppendLine($"物理护甲:{DEF:0.##}" + (exDEF != 0 ? $" [{BaseDEF:0.##} {(exMP >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({PDR * 100:0.##}%)");
+ builder.AppendLine($"物理护甲:{DEF:0.##}" + (exDEF != 0 ? $" [{BaseDEF:0.##} {(exDEF >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({PDR * 100:0.##}%)");
builder.AppendLine($"魔法抗性:{MDF.Avg:0.##}%(平均)");
double exSPD = AGI * GameplayEquilibriumConstant.AGItoSPDMultiplier + ExSPD;
builder.AppendLine($"行动速度:{SPD:0.##}" + (exSPD != 0 ? $" [{InitialSPD:0.##} {(exSPD >= 0 ? "+" : "-")} {Math.Abs(exSPD):0.##}]" : "") + $" ({ActionCoefficient * 100:0.##}%)");
@@ -1486,7 +1486,7 @@ namespace Milimoe.FunGame.Core.Entity
double exATK = ExATK + ExATK2 + ExATK3;
builder.AppendLine($"攻击力:{ATK:0.##}" + (exATK != 0 ? $" [{BaseATK:0.##} {(exATK >= 0 ? "+" : "-")} {Math.Abs(exATK):0.##}]" : ""));
double exDEF = ExDEF + ExDEF2 + ExDEF3;
- builder.AppendLine($"物理护甲:{DEF:0.##}" + (exDEF != 0 ? $" [{BaseDEF:0.##} {(exMP >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({PDR * 100:0.##}%)");
+ builder.AppendLine($"物理护甲:{DEF:0.##}" + (exDEF != 0 ? $" [{BaseDEF:0.##} {(exDEF >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({PDR * 100:0.##}%)");
builder.AppendLine($"魔法抗性:{MDF.Avg:0.##}%(平均)");
if (showBasicOnly)
{
@@ -1732,7 +1732,7 @@ namespace Milimoe.FunGame.Core.Entity
double exATK = ExATK + ExATK2 + ExATK3;
builder.AppendLine($"攻击力:{ATK:0.##}" + (exATK != 0 ? $" [{BaseATK:0.##} {(exATK >= 0 ? "+" : "-")} {Math.Abs(exATK):0.##}]" : ""));
double exDEF = ExDEF + ExDEF2 + ExDEF3;
- builder.AppendLine($"物理护甲:{DEF:0.##}" + (exDEF != 0 ? $" [{BaseDEF:0.##} {(exMP >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({PDR * 100:0.##}%)");
+ builder.AppendLine($"物理护甲:{DEF:0.##}" + (exDEF != 0 ? $" [{BaseDEF:0.##} {(exDEF >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({PDR * 100:0.##}%)");
builder.AppendLine($"魔法抗性:{MDF.Avg:0.##}%(平均)");
double exSPD = AGI * GameplayEquilibriumConstant.AGItoSPDMultiplier + ExSPD;
builder.AppendLine($"行动速度:{SPD:0.##}" + (exSPD != 0 ? $" [{InitialSPD:0.##} {(exSPD >= 0 ? "+" : "-")} {Math.Abs(exSPD):0.##}]" : "") + $" ({ActionCoefficient * 100:0.##}%)");
@@ -2028,8 +2028,10 @@ namespace Milimoe.FunGame.Core.Entity
EP = c.EP;
InitialATK = c.InitialATK;
ExATK2 = c.ExATK2;
+ ExATKPercentage = c.ExATKPercentage;
InitialDEF = c.InitialDEF;
ExDEF2 = c.ExDEF2;
+ ExDEFPercentage = c.ExDEFPercentage;
MDF = c.MDF.Copy();
PhysicalPenetration = c.PhysicalPenetration;
MagicalPenetration = c.MagicalPenetration;
@@ -2042,8 +2044,11 @@ namespace Milimoe.FunGame.Core.Entity
InitialAGI = c.InitialAGI;
InitialINT = c.InitialINT;
ExSTR = c.ExSTR;
+ ExSTRPercentage = c.ExSTRPercentage;
ExAGI = c.ExAGI;
+ ExAGIPercentage = c.ExAGIPercentage;
ExINT = c.ExINT;
+ ExINTPercentage = c.ExINTPercentage;
STRGrowth = c.STRGrowth;
AGIGrowth = c.AGIGrowth;
INTGrowth = c.INTGrowth;
diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs
index 95fb8d7..fa184b8 100644
--- a/Entity/Skill/Skill.cs
+++ b/Entity/Skill/Skill.cs
@@ -108,6 +108,16 @@ namespace Milimoe.FunGame.Core.Entity
///
public virtual bool CanSelectTeammate { get; set; } = false;
+ ///
+ /// 选取所有敌对角色,优先级大于
+ ///
+ public virtual bool SelectAllEnemies { get; set; } = false;
+
+ ///
+ /// 选取所有友方角色,优先级大于 ,默认包含自身
+ ///
+ public virtual bool SelectAllTeammates { get; set; } = false;
+
///
/// 可选取的作用目标数量
///
@@ -319,6 +329,9 @@ namespace Milimoe.FunGame.Core.Entity
selectable.AddRange(teammates);
}
+ // 其他条件
+ selectable = [.. selectable.Where(c => SelectTargetPredicates.All(f => f(c)))];
+
return selectable;
}
@@ -333,12 +346,20 @@ namespace Milimoe.FunGame.Core.Entity
{
List tobeSelected = GetSelectableTargets(caster, enemys, teammates);
- // 筛选出符合条件的角色
- tobeSelected = [.. tobeSelected.Where(c => SelectTargetPredicates.All(f => f(c)))];
-
List targets = [];
- if (tobeSelected.Count <= CanSelectTargetCount)
+ if (SelectAllTeammates || SelectAllEnemies)
+ {
+ if (SelectAllTeammates)
+ {
+ targets.AddRange(tobeSelected.Where(c => c == caster || teammates.Contains(c)));
+ }
+ if (SelectAllEnemies)
+ {
+ targets.AddRange(tobeSelected.Where(enemys.Contains));
+ }
+ }
+ else if (tobeSelected.Count <= CanSelectTargetCount)
{
targets.AddRange(tobeSelected);
}
diff --git a/Entity/System/RoundRecord.cs b/Entity/System/RoundRecord.cs
index eb11f5d..c7f5774 100644
--- a/Entity/System/RoundRecord.cs
+++ b/Entity/System/RoundRecord.cs
@@ -66,6 +66,7 @@ namespace Milimoe.FunGame.Core.Entity
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 (Assists.Count > 0) builder.AppendLine($"本回合助攻:[ {string.Join(" ] / [ ", Assists)} ]");
}
if (ActionType == CharacterActionType.PreCastSkill && Skill != null)
diff --git a/Library/Common/JsonConverter/RoundRecordConverter.cs b/Library/Common/JsonConverter/RoundRecordConverter.cs
new file mode 100644
index 0000000..6dc4ffa
--- /dev/null
+++ b/Library/Common/JsonConverter/RoundRecordConverter.cs
@@ -0,0 +1,233 @@
+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 RoundRecordConverter : BaseEntityConverter
+ {
+ public override RoundRecord NewInstance()
+ {
+ return new RoundRecord(0);
+ }
+
+ public override void ReadPropertyName(ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref RoundRecord result, Dictionary convertingContext)
+ {
+ switch (propertyName)
+ {
+ case nameof(RoundRecord.Round):
+ result.Round = reader.GetInt32();
+ break;
+ case nameof(RoundRecord.Actor):
+ result.Actor = NetworkUtility.JsonDeserialize(ref reader, options) ?? Factory.GetCharacter();
+ break;
+ case nameof(RoundRecord.Targets):
+ List targets = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ result.Targets.AddRange(targets);
+ break;
+ case nameof(RoundRecord.Damages):
+ Dictionary damagesGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in damagesGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.Damages[character] = kvp.Value;
+ }
+ }
+ break;
+
+ case nameof(RoundRecord.ActionType):
+ result.ActionType = (CharacterActionType)reader.GetInt32();
+ break;
+ case nameof(RoundRecord.Skill):
+ result.Skill = NetworkUtility.JsonDeserialize(ref reader, options);
+ break;
+ case nameof(RoundRecord.SkillCost):
+ result.SkillCost = reader.GetString() ?? "";
+ break;
+ case nameof(RoundRecord.Item):
+ result.Item = NetworkUtility.JsonDeserialize- (ref reader, options);
+ break;
+ case nameof(RoundRecord.HasKill):
+ result.HasKill = reader.GetBoolean();
+ break;
+ case nameof(RoundRecord.Assists):
+ List assists = NetworkUtility.JsonDeserialize
>(ref reader, options) ?? [];
+ result.Assists.AddRange(assists);
+ break;
+
+ case nameof(RoundRecord.IsCritical):
+ Dictionary isCriticalGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in isCriticalGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.IsCritical[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.IsEvaded):
+ Dictionary isEvadedGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in isEvadedGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.IsEvaded[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.IsImmune):
+ Dictionary isImmuneGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in isImmuneGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.IsImmune[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.Heals):
+ Dictionary healsGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in healsGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.Heals[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.Effects):
+ Dictionary effectsGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in effectsGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.Effects[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.ApplyEffects):
+ Dictionary> applyEffectsGuid = NetworkUtility.JsonDeserialize>>(ref reader, options) ?? [];
+ result.ApplyEffects.Clear();
+ foreach (KeyValuePair> kvp in applyEffectsGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.ApplyEffects[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.ActorContinuousKilling):
+ List actorCK = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ result.ActorContinuousKilling.AddRange(actorCK);
+ break;
+ case nameof(RoundRecord.DeathContinuousKilling):
+ List deathCK = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ result.DeathContinuousKilling.AddRange(deathCK);
+ break;
+ case nameof(RoundRecord.CastTime):
+ result.CastTime = reader.GetDouble();
+ break;
+ case nameof(RoundRecord.HardnessTime):
+ result.HardnessTime = reader.GetDouble();
+ break;
+ case nameof(RoundRecord.RespawnCountdowns):
+ Dictionary respawnCountdownGuid = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ foreach (KeyValuePair kvp in respawnCountdownGuid)
+ {
+ Character? character = FindCharacterByGuid(kvp.Key, result);
+ if (character != null)
+ {
+ result.RespawnCountdowns[character] = kvp.Value;
+ }
+ }
+ break;
+ case nameof(RoundRecord.Respawns):
+ List respawns = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ result.Respawns.AddRange(respawns);
+ break;
+ case nameof(RoundRecord.RoundRewards):
+ List rewards = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ result.RoundRewards.AddRange(rewards);
+ break;
+ case nameof(RoundRecord.OtherMessages):
+ List messages = NetworkUtility.JsonDeserialize>(ref reader, options) ?? [];
+ result.OtherMessages.AddRange(messages);
+ break;
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+
+ public override void Write(Utf8JsonWriter writer, RoundRecord value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WriteNumber(nameof(RoundRecord.Round), value.Round);
+ writer.WritePropertyName(nameof(RoundRecord.Actor));
+ JsonSerializer.Serialize(writer, value.Actor, options);
+ writer.WritePropertyName(nameof(RoundRecord.Targets));
+ JsonSerializer.Serialize(writer, value.Targets, options);
+ writer.WritePropertyName(nameof(RoundRecord.Damages));
+ JsonSerializer.Serialize(writer, value.Damages.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WriteNumber(nameof(RoundRecord.ActionType), (int)value.ActionType);
+ writer.WritePropertyName(nameof(RoundRecord.Skill));
+ JsonSerializer.Serialize(writer, value.Skill, options);
+ writer.WriteString(nameof(RoundRecord.SkillCost), value.SkillCost);
+ writer.WritePropertyName(nameof(RoundRecord.Item));
+ JsonSerializer.Serialize(writer, value.Item, options);
+ writer.WriteBoolean(nameof(RoundRecord.HasKill), value.HasKill);
+ writer.WritePropertyName(nameof(RoundRecord.Assists));
+ JsonSerializer.Serialize(writer, value.Assists, options);
+ writer.WritePropertyName(nameof(RoundRecord.IsCritical));
+ JsonSerializer.Serialize(writer, value.IsCritical.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.IsEvaded));
+ JsonSerializer.Serialize(writer, value.IsEvaded.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.IsImmune));
+ JsonSerializer.Serialize(writer, value.IsImmune.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.Heals));
+ JsonSerializer.Serialize(writer, value.Heals.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.Effects));
+ JsonSerializer.Serialize(writer, value.Effects.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.ApplyEffects));
+ JsonSerializer.Serialize(writer, value.ApplyEffects.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.ActorContinuousKilling));
+ JsonSerializer.Serialize(writer, value.ActorContinuousKilling, options);
+ writer.WritePropertyName(nameof(RoundRecord.DeathContinuousKilling));
+ JsonSerializer.Serialize(writer, value.DeathContinuousKilling, options);
+ writer.WriteNumber(nameof(RoundRecord.CastTime), value.CastTime);
+ writer.WriteNumber(nameof(RoundRecord.HardnessTime), value.HardnessTime);
+ writer.WritePropertyName(nameof(RoundRecord.RespawnCountdowns));
+ JsonSerializer.Serialize(writer, value.RespawnCountdowns.ToDictionary(kv => kv.Key.Guid, kv => kv.Value), options);
+ writer.WritePropertyName(nameof(RoundRecord.Respawns));
+ JsonSerializer.Serialize(writer, value.Respawns, options);
+ writer.WritePropertyName(nameof(RoundRecord.RoundRewards));
+ JsonSerializer.Serialize(writer, value.RoundRewards, options);
+ writer.WritePropertyName(nameof(RoundRecord.OtherMessages));
+ JsonSerializer.Serialize(writer, value.OtherMessages, options);
+ writer.WriteEndObject();
+ }
+
+ private static Character? FindCharacterByGuid(Guid guid, RoundRecord record)
+ {
+ Character? character = record.Targets.FirstOrDefault(c => c.Guid == guid);
+ if (character != null) return character;
+ if (record.Actor != null && record.Actor.Guid == guid) return record.Actor;
+ character = record.Assists.FirstOrDefault(c => c.Guid == guid);
+ if (character != null) return character;
+ character = record.Respawns.FirstOrDefault(c => c.Guid == guid);
+ if (character != null) return character;
+ return null;
+ }
+ }
+}
diff --git a/Library/Constant/ConstantSet.cs b/Library/Constant/ConstantSet.cs
index 49e53f5..a85a488 100644
--- a/Library/Constant/ConstantSet.cs
+++ b/Library/Constant/ConstantSet.cs
@@ -439,6 +439,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
CharacterState.ActionRestricted => "角色现在行动受限",
CharacterState.BattleRestricted => "角色现在战斗不能",
CharacterState.SkillRestricted => "角色现在技能受限",
+ CharacterState.AttackRestricted => "角色现在攻击受限",
_ => "角色现在完全行动不能"
};
}
@@ -797,7 +798,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
EffectType.AllImmune => false,
EffectType.EvadeBoost => false,
EffectType.Lifesteal => false,
- EffectType.GrievousWound => false,
+ EffectType.GrievousWound => true,
_ => false
};
}
diff --git a/Model/GamingQueue.cs b/Model/GamingQueue.cs
index 82fe3b7..d193dbd 100644
--- a/Model/GamingQueue.cs
+++ b/Model/GamingQueue.cs
@@ -1496,19 +1496,25 @@ namespace Milimoe.FunGame.Core.Model
// 造成伤害和受伤都可以获得能量。攻击者为全额,被攻击者为 actualDamage,护盾抵消的伤害不算
double ep = GetEP(damage, GameplayEquilibriumConstant.DamageGetEPFactor, GameplayEquilibriumConstant.DamageGetEPMax);
- effects = [.. actor.Effects.Where(e => e.IsInEffect)];
- foreach (Effect effect in effects)
+ if (ep > 0)
{
- effect.AlterEPAfterDamage(actor, ref ep);
+ effects = [.. actor.Effects.Where(e => e.IsInEffect)];
+ foreach (Effect effect in effects)
+ {
+ effect.AlterEPAfterDamage(actor, ref ep);
+ }
+ actor.EP += ep;
}
- actor.EP += ep;
ep = GetEP(actualDamage, GameplayEquilibriumConstant.TakenDamageGetEPFactor, GameplayEquilibriumConstant.TakenDamageGetEPMax);
- effects = [.. enemy.Effects.Where(e => e.IsInEffect)];
- foreach (Effect effect in effects)
+ if (ep > 0)
{
- effect.AlterEPAfterGetDamage(enemy, ref ep);
+ effects = [.. enemy.Effects.Where(e => e.IsInEffect)];
+ foreach (Effect effect in effects)
+ {
+ effect.AlterEPAfterGetDamage(enemy, ref ep);
+ }
+ enemy.EP += ep;
}
- enemy.EP += ep;
// 统计伤害
CalculateCharacterDamageStatistics(actor, enemy, damage, isMagicDamage, actualDamage);
diff --git a/Service/JsonManager.cs b/Service/JsonManager.cs
index 3143c03..af6733b 100644
--- a/Service/JsonManager.cs
+++ b/Service/JsonManager.cs
@@ -22,7 +22,7 @@ namespace Milimoe.FunGame.Core.Service
Converters = { new DateTimeConverter(), new DataTableConverter(), new DataSetConverter(), new UserConverter(), new RoomConverter(),
new CharacterConverter(), new MagicResistanceConverter(), new EquipSlotConverter(), new SkillConverter(), new EffectConverter(), new ItemConverter(),
new InventoryConverter(), new NormalAttackConverter(), new ClubConverter(), new GoodsConverter(), new StoreConverter(),
- new NovelOptionConverter(), new NovelNodeConverter(), new ShieldConverter()
+ new NovelOptionConverter(), new NovelNodeConverter(), new ShieldConverter(), new RoundRecordConverter()
}
};