diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs index 0399065..565c662 100644 --- a/Entity/Character/Character.cs +++ b/Entity/Character/Character.cs @@ -125,7 +125,12 @@ namespace Milimoe.FunGame.Core.Entity { get { - return Math.Max(1, field + ExLevel); + int level = field + ExLevel; + if (MaxLevel > 0) + { + level = Math.Min(level, MaxLevel); + } + return Math.Max(0, level); } set { @@ -145,6 +150,11 @@ namespace Milimoe.FunGame.Core.Entity /// public int ExLevel { get; set; } = 0; + /// + /// 自定义最大等级 + /// + public int MaxLevel { get; set; } = 0; + /// /// 经验值 /// @@ -909,6 +919,11 @@ namespace Milimoe.FunGame.Core.Entity /// public Character? Master { get; set; } = null; + /// + /// 角色职业对象 + /// + public CharacterClass Class { get; set; } + /// /// 普通攻击对象 /// @@ -941,6 +956,7 @@ namespace Milimoe.FunGame.Core.Entity MDF = new(); Shield = new(); NormalAttack = new(this); + Class = new(this); } internal static Character GetInstance() @@ -1289,6 +1305,7 @@ namespace Milimoe.FunGame.Core.Entity } if (isUp) { + Class.OnLevelUp(); Effect[] effects = [.. Effects.Where(e => e.IsInEffect).OrderByDescending(e => e.Priority)]; foreach (Effect e in effects) { @@ -2010,6 +2027,7 @@ namespace Milimoe.FunGame.Core.Entity PrimaryAttribute = PrimaryAttribute, Level = Level, ExLevel = ExLevel, + MaxLevel = MaxLevel, LevelBreak = LevelBreak, EXP = EXP, InitialHP = InitialHP, @@ -2124,6 +2142,7 @@ namespace Milimoe.FunGame.Core.Entity PrimaryAttribute = c.PrimaryAttribute; Level = c.Level; ExLevel = c.ExLevel; + MaxLevel = c.MaxLevel; LevelBreak = c.LevelBreak; EXP = c.EXP; CharacterState = c.CharacterState; diff --git a/Entity/Character/CharacterClass.cs b/Entity/Character/CharacterClass.cs new file mode 100644 index 0000000..f45760f --- /dev/null +++ b/Entity/Character/CharacterClass.cs @@ -0,0 +1,141 @@ +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + /// + /// 角色职业管理类 + /// + /// + public class CharacterClass(Character character) + { + /// + /// 所属的角色 + /// + public Character Character { get; set; } = character; + + /// + /// 职业点数 + /// + public int ClassPoints { get; set; } = 1; + + /// + /// 已选择职业 + /// + public HashSet Classes { get; set; } = []; + + /// + /// 已选择流派 + /// + public HashSet SubClasses { get; set; } = []; + + /// + /// 已激活战斗天赋 + /// + public Skill? CombatTalent { get; set; } = null; + + /// + /// 通过升级重新计算职业点数 + /// + public void OnLevelUp() + { + ClassPoints = 0; + foreach (int level in Character.GameplayEquilibriumConstant.ClassPointsGetterList) + { + if (Character.Level >= level) + { + ClassPoints++; + } + } + if (ClassPoints == 0) + { + ClassPoints = 1; + } + } + + /// + /// 重新构建角色职业,设置定位和技能等 + /// + /// + public void ReBuildCharacterClass(ClassObject obj) + { + Character.FirstRoleType = RoleType.None; + Character.SecondRoleType = RoleType.None; + Character.ThirdRoleType = RoleType.None; + CombatTalent?.RemoveSkillFromCharacter(Character); + foreach (SubClass sc in SubClasses) + { + foreach (Skill skill in sc.InherentPassives) + { + skill.RemoveSkillFromCharacter(Character); + } + } + foreach (Class c in Classes) + { + foreach (Skill skill in c.PassiveSkills) + { + skill.RemoveSkillFromCharacter(Character); + } + foreach (Skill skill in c.Skills) + { + skill.RemoveSkillFromCharacter(Character); + } + foreach (Skill skill in c.Magics) + { + skill.RemoveSkillFromCharacter(Character); + } + foreach (Skill skill in c.SuperSkills) + { + skill.RemoveSkillFromCharacter(Character); + } + } + Classes.Clear(); + SubClasses.Clear(); + foreach (Class c in obj.Classes) + { + Classes.Add(c); + foreach (Skill skill in c.PassiveSkills) + { + skill.AddSkillToCharacter(Character); + } + foreach (Skill skill in c.Skills) + { + skill.AddSkillToCharacter(Character); + } + foreach (Skill skill in c.Magics) + { + skill.AddSkillToCharacter(Character); + } + foreach (Skill skill in c.SuperSkills) + { + skill.AddSkillToCharacter(Character); + } + } + foreach (SubClass sc in obj.SubClasses) + { + SubClasses.Add(sc); + foreach (Skill skill in sc.InherentPassives) + { + skill.AddSkillToCharacter(Character); + } + } + if (obj.CurrentCombatTalent != null) + { + CombatTalent = obj.CurrentCombatTalent; + CombatTalent.AddSkillToCharacter(Character); + } + } + } + + /// + /// 决定如何构建角色的职业。这个类没有 JSON 转换器支持 + /// + public class ClassObject(Class[] c, SubClass[] s) + { + public Class[] Classes { get; set; } = c; + public SubClass[] SubClasses { get; set; } = s; + public RoleType FirstRoleType { get; set; } = RoleType.None; + public RoleType SecondRoleType { get; set; } = RoleType.None; + public RoleType ThirdRoleType { get; set; } = RoleType.None; + public Skill? CurrentCombatTalent { get; set; } = null; + } +} diff --git a/Entity/Character/Class.cs b/Entity/Character/Class.cs new file mode 100644 index 0000000..22d96b8 --- /dev/null +++ b/Entity/Character/Class.cs @@ -0,0 +1,55 @@ +using Milimoe.FunGame.Core.Interface.Entity; + +namespace Milimoe.FunGame.Core.Entity +{ + /// + /// 角色职业类 + /// + public class Class : BaseEntity + { + /// + /// 职业名称 + /// + public override string Name { get; set; } = ""; + + /// + /// 职业等级 + /// + public int Level + { + get + { + return Math.Max(0, field); + } + set + { + field = Math.Max(0, value); + } + } + + /// + /// 职业战技 + /// + public HashSet Skills { get; set; } = []; + + /// + /// 职业魔法 + /// + public HashSet Magics { get; set; } = []; + + /// + /// 职业被动 + /// + public HashSet PassiveSkills { get; set; } = []; + + /// + /// 职业爆发技 + /// + public HashSet SuperSkills { get; set; } = []; + + public override bool Equals(IBaseEntity? other) + { + return other is Class && other.GetIdName() == GetIdName(); + } + } +} diff --git a/Entity/Character/SubClass.cs b/Entity/Character/SubClass.cs new file mode 100644 index 0000000..c92d170 --- /dev/null +++ b/Entity/Character/SubClass.cs @@ -0,0 +1,46 @@ +using Milimoe.FunGame.Core.Interface.Entity; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + /// + /// 子职业(流派/派别) + /// + public class SubClass(Class @class) : BaseEntity + { + /// + /// 流派名称 + /// + public override string Name { get; set; } = ""; + + /// + /// 所属职业 + /// + public Class Class => @class; + + /// + /// 职业等级 + /// + public int Level => @class.Level; + + /// + /// 固有被动 + /// + public HashSet InherentPassives { get; set; } = []; + + /// + /// 角色定位 + /// + public HashSet RoleTypes { get; set; } = []; + + /// + /// 战斗天赋 + /// + public HashSet CombatTalents { get; set; } = []; + + public override bool Equals(IBaseEntity? other) + { + return other is SubClass && other.GetIdName() == GetIdName(); + } + } +} diff --git a/Entity/Item/Item.cs b/Entity/Item/Item.cs index 5b69611..4dcd425 100644 --- a/Entity/Item/Item.cs +++ b/Entity/Item/Item.cs @@ -252,16 +252,11 @@ namespace Milimoe.FunGame.Core.Entity { foreach (Skill skill in Skills.Passives) { - List effects = [.. Character.Effects.Where(e => e.Skill == skill && e.IsInEffect).OrderByDescending(e => e.Priority)]; - foreach (Effect e in effects) - { - Character.Effects.Remove(e); - e.OnEffectLost(Character); - } + skill.RemoveSkillFromCharacter(Character); } foreach (Skill skill in Skills.Magics) { - Character.Skills.Remove(skill); + skill.RemoveSkillFromCharacter(Character); } switch (type) { diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs index 4408b2e..cb38b1d 100644 --- a/Entity/Skill/Skill.cs +++ b/Entity/Skill/Skill.cs @@ -56,7 +56,12 @@ namespace Milimoe.FunGame.Core.Entity { get { - return Math.Max(0, field + ExLevel); + int level = field + ExLevel; + if (MaxLevel > 0) + { + level = Math.Min(level, MaxLevel); + } + return Math.Max(0, level); } set { @@ -71,6 +76,11 @@ namespace Milimoe.FunGame.Core.Entity /// public int ExLevel { get; set; } = 0; + /// + /// 自定义最大等级 + /// + public int MaxLevel { get; set; } = 0; + /// /// 技能类型 [ 此项为最高优先级 ] /// @@ -771,7 +781,7 @@ namespace Milimoe.FunGame.Core.Entity } /// - /// 被动技能,需要重写此方法,返回被动特效给角色 [ 此方法会在技能学习时触发 ] + /// 被动技能需要重写此方法,返回被动特效给角色 [ 此方法会在技能学习时触发 ] /// /// public virtual IEnumerable AddPassiveEffectToCharacter() @@ -779,6 +789,50 @@ namespace Milimoe.FunGame.Core.Entity return []; } + /// + /// 将技能添加到角色身上,如果是被动,则添加被动特效 + /// + /// 角色 + /// + public void AddSkillToCharacter(Character character) + { + if (Level > 0) + { + Character = character; + foreach (Effect e in AddPassiveEffectToCharacter()) + { + e.GamingQueue = GamingQueue; + Character.Effects.Add(e); + e.OnEffectGained(Character); + } + // 如果是纯被动技能,则不会添加到角色技能组中 + if (IsActive) + { + Character.Skills.Add(this); + } + } + } + + /// + /// 从角色身上移除技能 + /// + /// 角色 + /// + public void RemoveSkillFromCharacter(Character character) + { + List effects = [.. character.Effects.Where(e => e.Skill == this).OrderByDescending(e => e.Priority)]; + foreach (Effect e in effects) + { + character.Effects.Remove(e); + if (e.IsInEffect) e.OnEffectLost(character); + } + character.Skills.Remove(this); + if (character == Character) + { + Character = null; + } + } + /// /// 在复活时,因为复活是重新构建角色,如果需要继承死亡角色的技能数据,可以重写此方法并设置相关属性 /// diff --git a/Library/Common/JsonConverter/CharacterConverter.cs b/Library/Common/JsonConverter/CharacterConverter.cs index 3310ac5..c66721b 100644 --- a/Library/Common/JsonConverter/CharacterConverter.cs +++ b/Library/Common/JsonConverter/CharacterConverter.cs @@ -62,6 +62,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Character.ExLevel): result.ExLevel = reader.GetInt32(); break; + case nameof(Character.MaxLevel): + result.MaxLevel = reader.GetInt32(); + break; case nameof(Character.LevelBreak): result.LevelBreak = reader.GetInt32(); break; @@ -270,7 +273,8 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteNumber(nameof(Character.Promotion), value.Promotion); writer.WriteNumber(nameof(Character.PrimaryAttribute), (int)value.PrimaryAttribute); writer.WriteNumber(nameof(Character.Level), value.Level); - writer.WriteNumber(nameof(Character.ExLevel), value.ExLevel); + if (value.ExLevel > 0) writer.WriteNumber(nameof(Character.ExLevel), value.ExLevel); + if (value.MaxLevel > 0) writer.WriteNumber(nameof(Character.MaxLevel), value.MaxLevel); writer.WriteNumber(nameof(Character.LevelBreak), value.LevelBreak); writer.WriteNumber(nameof(Character.EXP), value.EXP); writer.WriteBoolean(nameof(Character.IsNeutral), value.IsNeutral); diff --git a/Library/Common/JsonConverter/SkillConverter.cs b/Library/Common/JsonConverter/SkillConverter.cs index 2e3f89f..eef7a5c 100644 --- a/Library/Common/JsonConverter/SkillConverter.cs +++ b/Library/Common/JsonConverter/SkillConverter.cs @@ -44,6 +44,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Skill.ExLevel): result.ExLevel = reader.GetInt32(); break; + case nameof(Skill.MaxLevel): + result.MaxLevel = reader.GetInt32(); + break; case nameof(Skill.CastAnywhere): result.CastAnywhere = reader.GetBoolean(); break; @@ -147,6 +150,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter if (value.Slogan.Length > 0) writer.WriteString(nameof(Skill.Slogan), value.Slogan); if (value.Level > 0) writer.WriteNumber(nameof(Skill.Level), value.Level); if (value.ExLevel > 0) writer.WriteNumber(nameof(Skill.ExLevel), value.ExLevel); + if (value.MaxLevel > 0) writer.WriteNumber(nameof(Skill.MaxLevel), value.MaxLevel); writer.WriteBoolean(nameof(Skill.CastAnywhere), value.CastAnywhere); writer.WriteNumber(nameof(Skill.CastRange), value.CastRange); if (value.CanSelectSelf) writer.WriteBoolean(nameof(Skill.CanSelectSelf), value.CanSelectSelf); diff --git a/Model/EquilibriumConstant.cs b/Model/EquilibriumConstant.cs index a931d35..8f0b118 100644 --- a/Model/EquilibriumConstant.cs +++ b/Model/EquilibriumConstant.cs @@ -635,6 +635,11 @@ namespace Milimoe.FunGame.Core.Model /// public int DecisionPointsCostOther { get; set; } = 1; + /// + /// 角色升级时会获得职业点数,该属性设置可获得点数的等级 + /// + public HashSet ClassPointsGetterList { get; set; } = [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]; + /// /// 应用此游戏平衡常数给实体 /// diff --git a/Model/PrefabricatedEntity/MagicCardPack.cs b/Model/PrefabricatedEntity/MagicCardPack.cs index 5fc6592..d19a338 100644 --- a/Model/PrefabricatedEntity/MagicCardPack.cs +++ b/Model/PrefabricatedEntity/MagicCardPack.cs @@ -122,16 +122,10 @@ namespace Milimoe.FunGame.Core.Model.PrefabricatedEntity if (NeuralCalibration != null) { character.Effects.Remove(NeuralCalibration); - NeuralCalibration.OnEffectLost(character); - } - if (CourageCommand != null) - { - character.Skills.Remove(CourageCommand); - } - if (Soulbound != null) - { - character.Skills.Remove(Soulbound); + if (NeuralCalibration.IsInEffect) NeuralCalibration.OnEffectLost(character); } + CourageCommand?.RemoveSkillFromCharacter(character); + Soulbound?.RemoveSkillFromCharacter(character); } } }