.NET 9;库存、物品相关更新;伤害乘算修改 (#101)

* 添加 SQL 文件

* 完善库存的显示;从用户类中移除余额;使用 Guid 关联物品与其技能;取消特效类的伤害乘区,改为加算

* 升级 .NET 9

* 回合数在获取到下一个角色时累加

* 更新 .NET9 的工作流
This commit is contained in:
milimoe 2024-11-15 00:52:49 +08:00 committed by GitHub
parent 184a5342b8
commit c55e9262cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 589 additions and 123 deletions

View File

@ -21,17 +21,17 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 9.0.x
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Build - name: Build
run: dotnet build --no-restore run: dotnet build --no-restore --configuration Release
- name: Test - name: Test
run: dotnet test --no-build --verbosity normal run: dotnet test --no-build --configuration Release --verbosity normal
- name: Prepare files for latest branch - name: Prepare files for latest branch
run: | run: |
mkdir -p latest mkdir -p latest
cp -r ./bin/Debug/net8.0/FunGame.Core.dll ./bin/Debug/net8.0/FunGame.Core.xml ./bin/Debug/net8.0/FunGame.Core.deps.json ./latest/ cp -r ./bin/Release/net9.0/FunGame.Core.dll ./bin/Release/net9.0/FunGame.Core.xml ./bin/Release/net9.0/FunGame.Core.deps.json ./latest/
- name: Commit and push to latest - name: Commit and push to latest
if: success() if: success()
run: | run: |

View File

@ -13,9 +13,9 @@ namespace Milimoe.FunGame.Core.Api.EntityFactory
return General.UnknownUserInstance; return General.UnknownUserInstance;
} }
public static User Create(long Id = 0, string Username = "", DateTime? RegTime = null, DateTime? LastTime = null, string Email = "", string NickName = "", bool IsAdmin = false, bool IsOperator = false, bool IsEnable = true, double Credits = 0, double Materials = 0, double GameTime = 0, string AutoKey = "") public static User Create(long Id = 0, string Username = "", DateTime? RegTime = null, DateTime? LastTime = null, string Email = "", string NickName = "", bool IsAdmin = false, bool IsOperator = false, bool IsEnable = true, double GameTime = 0, string AutoKey = "")
{ {
return new(Id, Username, RegTime, LastTime, Email, NickName, IsAdmin, IsOperator, IsEnable, Credits, Materials, GameTime, AutoKey); return new(Id, Username, RegTime, LastTime, Email, NickName, IsAdmin, IsOperator, IsEnable, GameTime, AutoKey);
} }
public static User Create(UserType type) public static User Create(UserType type)

View File

@ -500,14 +500,12 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <param name="IsAdmin"></param> /// <param name="IsAdmin"></param>
/// <param name="IsOperator"></param> /// <param name="IsOperator"></param>
/// <param name="IsEnable"></param> /// <param name="IsEnable"></param>
/// <param name="Credits"></param>
/// <param name="Materials"></param>
/// <param name="GameTime"></param> /// <param name="GameTime"></param>
/// <param name="AutoKey"></param> /// <param name="AutoKey"></param>
/// <returns></returns> /// <returns></returns>
public static User GetUser(long Id = 0, string Username = "", DateTime? RegTime = null, DateTime? LastTime = null, string Email = "", string NickName = "", bool IsAdmin = false, bool IsOperator = false, bool IsEnable = true, double Credits = 0, double Materials = 0, double GameTime = 0, string AutoKey = "") public static User GetUser(long Id = 0, string Username = "", DateTime? RegTime = null, DateTime? LastTime = null, string Email = "", string NickName = "", bool IsAdmin = false, bool IsOperator = false, bool IsEnable = true, double GameTime = 0, string AutoKey = "")
{ {
return UserFactory.Create(Id, Username, RegTime, LastTime, Email, NickName, IsAdmin, IsOperator, IsEnable, Credits, Materials, GameTime, AutoKey); return UserFactory.Create(Id, Username, RegTime, LastTime, Email, NickName, IsAdmin, IsOperator, IsEnable, GameTime, AutoKey);
} }
/// <summary> /// <summary>
@ -542,11 +540,9 @@ namespace Milimoe.FunGame.Core.Api.Utility
bool IsAdmin = Convert.ToInt32(dr[UserQuery.Column_IsAdmin]) == 1; bool IsAdmin = Convert.ToInt32(dr[UserQuery.Column_IsAdmin]) == 1;
bool IsOperator = Convert.ToInt32(dr[UserQuery.Column_IsOperator]) == 1; bool IsOperator = Convert.ToInt32(dr[UserQuery.Column_IsOperator]) == 1;
bool IsEnable = Convert.ToInt32(dr[UserQuery.Column_IsEnable]) == 1; bool IsEnable = Convert.ToInt32(dr[UserQuery.Column_IsEnable]) == 1;
double Credits = Convert.ToDouble(dr[UserQuery.Column_Credits]);
double Materials = Convert.ToDouble(dr[UserQuery.Column_Materials]);
double GameTime = Convert.ToDouble(dr[UserQuery.Column_GameTime]); double GameTime = Convert.ToDouble(dr[UserQuery.Column_GameTime]);
string AutoKey = (string)dr[UserQuery.Column_AutoKey]; string AutoKey = (string)dr[UserQuery.Column_AutoKey];
return UserFactory.Create(Id, Username, RegTime, LastTime, Email, NickName, IsAdmin, IsOperator, IsEnable, Credits, Materials, GameTime, AutoKey); return UserFactory.Create(Id, Username, RegTime, LastTime, Email, NickName, IsAdmin, IsOperator, IsEnable, GameTime, AutoKey);
} }
return UserFactory.Create(); return UserFactory.Create();
} }

View File

@ -1054,9 +1054,25 @@ namespace Milimoe.FunGame.Core.Entity
if (str != "") str += ", "; if (str != "") str += ", ";
str += NickName; str += NickName;
} }
str += " - 等级 " + Level;
if (User != null && User.Username != "") if (User != null && User.Username != "")
{ {
str += "(" + User.Username + ")"; str += "" + User.Username + "";
}
return str;
}
/// <summary>
/// 获取角色实例的名字、昵称以及等级
/// </summary>
/// <returns></returns>
public string ToStringWithLevelWithOutUser()
{
string str = GetName();
if (NickName != "")
{
if (str != "") str += ", ";
str += NickName;
} }
str += " - 等级 " + Level; str += " - 等级 " + Level;
return str; return str;
@ -1085,11 +1101,11 @@ namespace Milimoe.FunGame.Core.Entity
/// 获取角色的详细信息 /// 获取角色的详细信息
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public string GetInfo() public string GetInfo(bool showUser = true)
{ {
StringBuilder builder = new(); StringBuilder builder = new();
builder.AppendLine(ToStringWithLevel()); builder.AppendLine(showUser ? ToStringWithLevel() : ToStringWithLevelWithOutUser());
builder.AppendLine($"生命值:{HP:0.##} / {MaxHP:0.##}" + (ExHP + ExHP2 > 0 ? $" [{BaseHP:0.##} + {(ExHP + ExHP2):0.##}]" : "")); builder.AppendLine($"生命值:{HP:0.##} / {MaxHP:0.##}" + (ExHP + ExHP2 > 0 ? $" [{BaseHP:0.##} + {(ExHP + ExHP2):0.##}]" : ""));
builder.AppendLine($"魔法值:{MP:0.##} / {MaxMP:0.##}" + (ExMP + ExMP2 > 0 ? $" [{BaseMP:0.##} + {(ExMP + ExMP2):0.##}]" : "")); builder.AppendLine($"魔法值:{MP:0.##} / {MaxMP:0.##}" + (ExMP + ExMP2 > 0 ? $" [{BaseMP:0.##} + {(ExMP + ExMP2):0.##}]" : ""));
builder.AppendLine($"能量值:{EP:0.##} / {General.GameplayEquilibriumConstant.MaxEP:0.##}"); builder.AppendLine($"能量值:{EP:0.##} / {General.GameplayEquilibriumConstant.MaxEP:0.##}");
@ -1372,6 +1388,7 @@ namespace Milimoe.FunGame.Core.Entity
/// <summary> /// <summary>
/// 复活此角色,回复出厂状态 /// 复活此角色,回复出厂状态
/// <para>注意:此方法仅用于角色的复活,如果需要完全重构相同角色,请使用 <see cref="CharacterBuilder"/></para>
/// </summary> /// </summary>
/// <param name="original">需要一个原始的角色用于还原状态</param> /// <param name="original">需要一个原始的角色用于还原状态</param>
/// <returns></returns> /// <returns></returns>

View File

@ -3,27 +3,107 @@ using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Entity namespace Milimoe.FunGame.Core.Entity
{ {
public class CharacterBuilder(long id, string name, string firstName, string nickName, PrimaryAttribute primaryAttribute, double initialATK, double initialHP, double initialMP, double initialSTR, double strGrowth, double initialAGI, double agiGrowth, double initialINT, double intGrowth, double initialSPD, double initialHR, double initialMR) public class CharacterBuilder
{ {
public long Id { get; set; } = id; public long Id { get; set; }
public string Name { get; set; } = name; public string Name { get; set; }
public string FirstName { get; set; } = firstName; public string FirstName { get; set; }
public string NickName { get; set; } = nickName; public string NickName { get; set; }
public PrimaryAttribute PrimaryAttribute { get; set; } = primaryAttribute; public PrimaryAttribute PrimaryAttribute { get; set; }
public double InitialATK { get; set; } = initialATK; public double InitialATK { get; set; }
public double InitialHP { get; set; } = initialHP; public double InitialDEF { get; set; }
public double InitialMP { get; set; } = initialMP; public double InitialHP { get; set; }
public double InitialSTR { get; set; } = initialSTR; public double InitialMP { get; set; }
public double STRGrowth { get; set; } = strGrowth; public double InitialSTR { get; set; }
public double InitialAGI { get; set; } = initialAGI; public double STRGrowth { get; set; }
public double AGIGrowth { get; set; } = agiGrowth; public double InitialAGI { get; set; }
public double InitialINT { get; set; } = initialINT; public double AGIGrowth { get; set; }
public double INTGrowth { get; set; } = intGrowth; public double InitialINT { get; set; }
public double InitialSPD { get; set; } = initialSPD; public double INTGrowth { get; set; }
public double InitialHR { get; set; } = initialHR; public double InitialSPD { get; set; }
public double InitialMR { get; set; } = initialMR; public double InitialHR { get; set; }
public double InitialMR { get; set; }
public Character Build(int level, IEnumerable<Skill> skills, IEnumerable<Item> items, EquipSlot? equips = null) /// <summary>
/// 基于初始属性创建角色构建器
/// </summary>
/// <param name="id"></param>
/// <param name="name"></param>
/// <param name="firstName"></param>
/// <param name="nickName"></param>
/// <param name="primaryAttribute"></param>
/// <param name="initialATK"></param>
/// <param name="initialDEF"></param>
/// <param name="initialHP"></param>
/// <param name="initialMP"></param>
/// <param name="initialSTR"></param>
/// <param name="strGrowth"></param>
/// <param name="initialAGI"></param>
/// <param name="agiGrowth"></param>
/// <param name="initialINT"></param>
/// <param name="intGrowth"></param>
/// <param name="initialSPD"></param>
/// <param name="initialHR"></param>
/// <param name="initialMR"></param>
public CharacterBuilder(long id, string name, string firstName, string nickName, PrimaryAttribute primaryAttribute, double initialATK, double initialDEF, double initialHP, double initialMP, double initialSTR, double strGrowth, double initialAGI, double agiGrowth, double initialINT, double intGrowth, double initialSPD, double initialHR, double initialMR)
{
Id = id;
Name = name;
FirstName = firstName;
NickName = nickName;
PrimaryAttribute = primaryAttribute;
InitialATK = initialATK;
InitialDEF = initialDEF;
InitialHP = initialHP;
InitialMP = initialMP;
InitialSTR = initialSTR;
STRGrowth = strGrowth;
InitialAGI = initialAGI;
AGIGrowth = agiGrowth;
InitialINT = initialINT;
INTGrowth = intGrowth;
InitialSPD = initialSPD;
InitialHR = initialHR;
InitialMR = initialMR;
}
/// <summary>
/// 基于模板角色创建角色构建器
/// </summary>
/// <param name="character"></param>
public CharacterBuilder(Character character)
{
Id = character.Id;
Name = character.Name;
FirstName = character.FirstName;
NickName = character.NickName;
PrimaryAttribute = character.PrimaryAttribute;
InitialATK = character.InitialATK;
InitialDEF = character.InitialDEF;
InitialHP = character.InitialHP;
InitialMP = character.InitialMP;
InitialSTR = character.InitialSTR;
STRGrowth = character.STRGrowth;
InitialAGI = character.InitialAGI;
AGIGrowth = character.AGIGrowth;
InitialINT = character.InitialINT;
INTGrowth = character.INTGrowth;
InitialSPD = character.InitialSPD;
InitialHR = character.InitialHR;
InitialMR = character.InitialMR;
}
/// <summary>
/// 基于初始条件构建新的角色
/// <para>需要传入等级、技能、物品,可选构建装备</para>
/// </summary>
/// <param name="level"></param>
/// <param name="skills"></param>
/// <param name="items"></param>
/// <param name="newItemGuid"></param>
/// <param name="equips"></param>
/// <returns>构建的新角色</returns>
public Character Build(int level, IEnumerable<Skill> skills, IEnumerable<Item> items, bool newItemGuid = true, EquipSlot? equips = null)
{ {
Character character = Factory.GetCharacter(); Character character = Factory.GetCharacter();
character.Id = Id; character.Id = Id;
@ -32,6 +112,7 @@ namespace Milimoe.FunGame.Core.Entity
character.NickName = NickName; character.NickName = NickName;
character.PrimaryAttribute = PrimaryAttribute; character.PrimaryAttribute = PrimaryAttribute;
character.InitialATK = InitialATK; character.InitialATK = InitialATK;
character.InitialDEF = InitialDEF;
character.InitialHP = InitialHP; character.InitialHP = InitialHP;
character.InitialMP = InitialMP; character.InitialMP = InitialMP;
character.InitialSTR = InitialSTR; character.InitialSTR = InitialSTR;
@ -48,16 +129,24 @@ namespace Milimoe.FunGame.Core.Entity
character.Level = level; character.Level = level;
} }
foreach (Skill skill in skills) foreach (Skill skill in skills)
{
// 主动技能的Guid表示与其关联的物品
if (skill.Guid == Guid.Empty)
{ {
Skill newskill = skill.Copy(); Skill newskill = skill.Copy();
newskill.Character = character; newskill.Character = character;
newskill.Level = skill.Level; newskill.Level = skill.Level;
if (skill.CurrentCD > 0 && !skill.Enable)
{
newskill.Enable = true;
newskill.CurrentCD = 0; newskill.CurrentCD = 0;
}
character.Skills.Add(newskill); character.Skills.Add(newskill);
} }
}
foreach (Item item in items) foreach (Item item in items)
{ {
Item newitem = item.Copy(true); Item newitem = item.Copy(true, !newItemGuid);
newitem.Character = character; newitem.Character = character;
character.Items.Add(newitem); character.Items.Add(newitem);
} }
@ -69,13 +158,53 @@ namespace Milimoe.FunGame.Core.Entity
Item? s = equips.Shoes; Item? s = equips.Shoes;
Item? ac1 = equips.Accessory1; Item? ac1 = equips.Accessory1;
Item? ac2 = equips.Accessory2; Item? ac2 = equips.Accessory2;
if (mcp != null) character.Equip(mcp); if (mcp != null)
if (w != null) character.Equip(w); {
if (a != null) character.Equip(a); mcp = mcp.Copy(true, !newItemGuid);
if (s != null) character.Equip(s); character.Equip(mcp);
if (ac1 != null) character.Equip(ac1);
if (ac2 != null) character.Equip(ac2);
} }
if (w != null)
{
w = w.Copy(true, !newItemGuid);
character.Equip(w);
}
if (a != null)
{
a = a.Copy(true, !newItemGuid);
character.Equip(a);
}
if (s != null)
{
s = s.Copy(true, !newItemGuid);
character.Equip(s);
}
if (ac1 != null)
{
ac1 = ac1.Copy(true, !newItemGuid);
character.Equip(ac1);
}
if (ac2 != null)
{
ac2 = ac2.Copy(true, !newItemGuid);
character.Equip(ac2);
}
}
character.Recovery();
return character;
}
/// <summary>
/// 使用 <paramref name="reference"/> 角色构建
/// <para>新角色将获得参考角色的等级、技能、装备和身上的物品</para>
/// </summary>
/// <param name="reference"></param>
/// <param name="newItemGuid"></param>
/// <returns>构建的新角色</returns>
public static Character Build(Character reference, bool newItemGuid = true)
{
Character character = new CharacterBuilder(reference).Build(reference.Level, reference.Skills, reference.Items, newItemGuid, reference.EquipSlot);
character.NormalAttack.Level = reference.Level;
character.NormalAttack.SetMagicType(reference.NormalAttack.IsMagic, reference.NormalAttack.MagicType);
return character; return character;
} }
} }

View File

@ -36,6 +36,25 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary> /// </summary>
public virtual ItemType ItemType { get; set; } = ItemType.Others; public virtual ItemType ItemType { get; set; } = ItemType.Others;
/// <summary>
/// 是否是装备
/// </summary>
public bool IsEquipment
{
get
{
return ItemType switch
{
ItemType.MagicCardPack => true,
ItemType.Weapon => true,
ItemType.Armor => true,
ItemType.Shoes => true,
ItemType.Accessory => true,
_ => false
};
}
}
/// <summary> /// <summary>
/// 是否允许装备 /// 是否允许装备
/// </summary> /// </summary>
@ -407,15 +426,12 @@ namespace Milimoe.FunGame.Core.Entity
} }
else else
{ {
List<string> sellandtrade = [""]; List<string> sellandtrade = [];
if (IsSellable) if (IsSellable)
{ {
sellandtrade.Add("可出售"); sellandtrade.Add("可出售");
} }
if (IsTradable)
{
sellandtrade.Add("可交易");
}
if (!IsSellable && NextSellableTime != DateTime.MinValue) if (!IsSellable && NextSellableTime != DateTime.MinValue)
{ {
@ -426,6 +442,11 @@ namespace Milimoe.FunGame.Core.Entity
sellandtrade.Add("不可出售"); sellandtrade.Add("不可出售");
} }
if (IsTradable)
{
sellandtrade.Add("可交易");
}
if (!IsTradable && NextTradableTime != DateTime.MinValue) if (!IsTradable && NextTradableTime != DateTime.MinValue)
{ {
builder.AppendLine($"此物品将在 {NextTradableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可交易"); builder.AppendLine($"此物品将在 {NextTradableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可交易");
@ -435,7 +456,7 @@ namespace Milimoe.FunGame.Core.Entity
sellandtrade.Add("不可交易"); sellandtrade.Add("不可交易");
} }
builder.AppendLine(string.Join(" ", sellandtrade).Trim()); if (sellandtrade.Count > 0) builder.AppendLine(string.Join(" ", sellandtrade).Trim());
} }
if (isShowGeneralDescription && GeneralDescription != "") if (isShowGeneralDescription && GeneralDescription != "")
@ -467,6 +488,46 @@ namespace Milimoe.FunGame.Core.Entity
return builder.ToString(); return builder.ToString();
} }
public string ToStringInventory(bool showAll)
{
StringBuilder builder = new();
if (showAll)
{
builder.Append($"{ToString()}");
if (IsEquipment && Character != null) builder.AppendLine($"装备于:{Character.ToStringWithLevelWithOutUser()}");
builder.AppendLine();
}
else
{
List<string> sellandtrade = [];
if (!IsSellable && NextSellableTime != DateTime.MinValue)
{
builder.AppendLine($"此物品将在 {NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售");
}
else if (!IsSellable)
{
sellandtrade.Add("不可出售");
}
if (!IsTradable && NextTradableTime != DateTime.MinValue)
{
builder.AppendLine($"此物品将在 {NextTradableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可交易");
}
else if (!IsTradable)
{
sellandtrade.Add("不可交易");
}
if (sellandtrade.Count > 0) builder.AppendLine(string.Join(" ", sellandtrade).Trim());
if (Description != "") builder.AppendLine($"{Description}");
if (IsEquipment && Character != null) builder.AppendLine($"装备于:{Character.ToStringWithLevelWithOutUser()}");
}
return builder.ToString();
}
/// <summary> /// <summary>
/// 判断两个物品是否相同 检查Id.Name /// 判断两个物品是否相同 检查Id.Name
/// </summary> /// </summary>
@ -498,13 +559,13 @@ namespace Milimoe.FunGame.Core.Entity
/// 复制一个物品 /// 复制一个物品
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public Item Copy(bool copyLevel = false) public Item Copy(bool copyLevel = false, bool copyGuid = false)
{ {
Item item = Factory.OpenFactory.GetInstance<Item>(Id, Name, []); Item item = Factory.OpenFactory.GetInstance<Item>(Id, Name, []);
SetPropertyToItemModuleNew(item); SetPropertyToItemModuleNew(item);
item.Id = Id; item.Id = Id;
item.Name = Name; item.Name = Name;
item.Guid = Guid; if (copyGuid) item.Guid = Guid;
item.Description = Description; item.Description = Description;
item.GeneralDescription = GeneralDescription; item.GeneralDescription = GeneralDescription;
item.BackgroundStory = BackgroundStory; item.BackgroundStory = BackgroundStory;

View File

@ -149,9 +149,11 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="isNormalAttack"></param> /// <param name="isNormalAttack"></param>
/// <param name="isMagicDamage"></param> /// <param name="isMagicDamage"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
public virtual void AlterExpectedDamageBeforeCalculation(Character character, Character enemy, ref double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType) /// <param name="totalDamageBonus"></param>
/// <returns>返回伤害增减值</returns>
public virtual double AlterExpectedDamageBeforeCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, Dictionary<Effect, double> totalDamageBonus)
{ {
return 0;
} }
/// <summary> /// <summary>
@ -164,10 +166,12 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="isMagicDamage"></param> /// <param name="isMagicDamage"></param>
/// <param name="magicType"></param> /// <param name="magicType"></param>
/// <param name="damageResult"></param> /// <param name="damageResult"></param>
/// <returns>返回 true 表示取消此伤害,等同于闪避</returns> /// <param name="isEvaded"></param>
public virtual bool AlterActualDamageAfterCalculation(Character character, Character enemy, ref double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult) /// <param name="totalDamageBonus"></param>
/// <returns>返回伤害增减值</returns>
public virtual double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary<Effect, double> totalDamageBonus)
{ {
return false; return 0;
} }
/// <summary> /// <summary>

View File

@ -11,6 +11,12 @@ namespace Milimoe.FunGame.Core.Entity
/// </summary> /// </summary>
public class Skill : BaseEntity, IActiveEnable public class Skill : BaseEntity, IActiveEnable
{ {
/// <summary>
/// 唯一标识符 [ 只有物品技能需要赋值,用于表示与其关联的物品:<see cref="Item.Guid"/> ]
/// <para>其他情况请保持此属性为 <see cref="Guid.Empty"/></para>
/// </summary>
public override Guid Guid { get; set; } = Guid.Empty;
/// <summary> /// <summary>
/// 此技能所属的角色 /// 此技能所属的角色
/// </summary> /// </summary>

View File

@ -1,17 +1,99 @@
namespace Milimoe.FunGame.Core.Entity using System.Text;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
namespace Milimoe.FunGame.Core.Entity
{ {
public class Inventory public class Inventory
{ {
/// <summary>
/// 库存 ID 与用户 ID 绑定
/// </summary>
public long Id => User.Id; public long Id => User.Id;
/// <summary>
/// 库存的名称,默认为 “<see cref="User.Username"/>的库存”;可更改
/// </summary>
public string Name { get; set; } = ""; public string Name { get; set; } = "";
/// <summary>
/// 库存属于哪个玩家
/// </summary>
public User User { get; } public User User { get; }
public Dictionary<string, Character> Characters { get; } = [];
public Dictionary<string, Item> Items { get; } = []; /// <summary>
/// 玩家持有 <see cref="EquilibriumConstant.InGameCurrency"/> 的数量
/// </summary>
public double Credits { get; set; } = 0;
/// <summary>
/// 玩家持有 <see cref="EquilibriumConstant.InGameMaterial"/> 的数量
/// </summary>
public double Materials { get; set; } = 0;
/// <summary>
/// 玩家拥有的角色
/// </summary>
public HashSet<Character> Characters { get; } = [];
/// <summary>
/// 玩家拥有的物品
/// </summary>
public HashSet<Item> Items { get; } = [];
internal Inventory(User user) internal Inventory(User user)
{ {
User = user; User = user;
Name = user.Username + "的库存"; Name = user.Username + "的库存";
} }
public override string ToString()
{
return Name + $"{User}";
}
public string ToString(bool showAll)
{
StringBuilder builder = new();
builder.AppendLine($"☆★☆ {Name} ☆★☆");
builder.AppendLine($"{General.GameplayEquilibriumConstant.InGameCurrency}{Credits:0.00}");
builder.AppendLine($"{General.GameplayEquilibriumConstant.InGameMaterial}{Materials:0.00}");
builder.AppendLine($"======= 角色 =======");
Character[] characters = [.. Characters];
for (int i = 1; i <= characters.Length; i++)
{
Character character = characters[i - 1];
if (showAll)
{
builder.AppendLine($"===== 第 {i} 个角色 =====");
builder.AppendLine($"{character.GetInfo(false)}");
}
else
{
builder.AppendLine($"{i}. {character.ToStringWithLevelWithOutUser()}");
}
}
builder.AppendLine($"======= 物品 =======");
Item[] items = [.. Items];
for (int i = 1; i <= items.Length; i++)
{
Item item = items[i - 1];
if (showAll)
{
builder.AppendLine($"===== 第 {i} 个物品 =====");
}
else
{
builder.AppendLine($"{i}. [{ItemSet.GetQualityTypeName(item.QualityType)}|{ItemSet.GetItemTypeName(item.ItemType)}] {item.Name}");
}
builder.AppendLine($"{item.ToStringInventory(showAll).Trim()}");
if (showAll) builder.AppendLine();
}
return builder.ToString();
}
} }
} }

View File

@ -14,8 +14,6 @@ namespace Milimoe.FunGame.Core.Entity
public bool IsAdmin { get; set; } = false; public bool IsAdmin { get; set; } = false;
public bool IsOperator { get; set; } = false; public bool IsOperator { get; set; } = false;
public bool IsEnable { get; set; } = true; public bool IsEnable { get; set; } = true;
public double Credits { get; set; } = 0;
public double Materials { get; set; } = 0;
public double GameTime { get; set; } = 0; public double GameTime { get; set; } = 0;
public string AutoKey { get; set; } = ""; public string AutoKey { get; set; } = "";
public UserStatistics Statistics { get; } public UserStatistics Statistics { get; }
@ -27,7 +25,7 @@ namespace Milimoe.FunGame.Core.Entity
Inventory = new(this); Inventory = new(this);
} }
internal User(long Id = 0, string Username = "", DateTime? RegTime = null, DateTime? LastTime = null, string Email = "", string NickName = "", bool IsAdmin = false, bool IsOperator = false, bool IsEnable = true, double Credits = 0, double Materials = 0, double GameTime = 0, string AutoKey = "") internal User(long Id = 0, string Username = "", DateTime? RegTime = null, DateTime? LastTime = null, string Email = "", string NickName = "", bool IsAdmin = false, bool IsOperator = false, bool IsEnable = true, double GameTime = 0, string AutoKey = "")
{ {
this.Id = Id; this.Id = Id;
this.Username = Username; this.Username = Username;
@ -38,8 +36,6 @@ namespace Milimoe.FunGame.Core.Entity
this.IsAdmin = IsAdmin; this.IsAdmin = IsAdmin;
this.IsOperator = IsOperator; this.IsOperator = IsOperator;
this.IsEnable = IsEnable; this.IsEnable = IsEnable;
this.Credits = Credits;
this.Materials = Materials;
this.GameTime = GameTime; this.GameTime = GameTime;
this.AutoKey = AutoKey; this.AutoKey = AutoKey;
Statistics = new(this); Statistics = new(this);

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<BaseOutputPath>bin\</BaseOutputPath> <BaseOutputPath>bin\</BaseOutputPath>

View File

@ -19,18 +19,24 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
case nameof(Inventory.Name): case nameof(Inventory.Name):
result.Name = reader.GetString() ?? ""; result.Name = reader.GetString() ?? "";
break; break;
case nameof(Inventory.Credits):
result.Credits = reader.GetDouble();
break;
case nameof(Inventory.Materials):
result.Materials = reader.GetDouble();
break;
case nameof(Inventory.Characters): case nameof(Inventory.Characters):
Dictionary<string, Character> characters = NetworkUtility.JsonDeserialize<Dictionary<string, Character>>(ref reader, options) ?? []; HashSet<Character> characters = NetworkUtility.JsonDeserialize<HashSet<Character>>(ref reader, options) ?? [];
foreach (string key in characters.Keys) foreach (Character character in characters)
{ {
result.Characters[key] = characters[key]; result.Characters.Add(character);
} }
break; break;
case nameof(Inventory.Items): case nameof(Inventory.Items):
Dictionary<string, Item> items = NetworkUtility.JsonDeserialize<Dictionary<string, Item>>(ref reader, options) ?? []; HashSet<Item> items = NetworkUtility.JsonDeserialize<HashSet<Item>>(ref reader, options) ?? [];
foreach (string key in items.Keys) foreach (Item item in items)
{ {
result.Items[key] = items[key]; result.Items.Add(item);
} }
break; break;
} }
@ -41,6 +47,8 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
writer.WriteStartObject(); writer.WriteStartObject();
writer.WriteString(nameof(Inventory.Name), value.Name); writer.WriteString(nameof(Inventory.Name), value.Name);
writer.WriteNumber(nameof(Inventory.Credits), value.Credits);
writer.WriteNumber(nameof(Inventory.Materials), value.Materials);
writer.WritePropertyName(nameof(Inventory.Characters)); writer.WritePropertyName(nameof(Inventory.Characters));
JsonSerializer.Serialize(writer, value.Characters, options); JsonSerializer.Serialize(writer, value.Characters, options);
writer.WritePropertyName(nameof(Inventory.Items)); writer.WritePropertyName(nameof(Inventory.Items));

View File

@ -23,6 +23,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
case nameof(Skill.Name): case nameof(Skill.Name):
result.Name = reader.GetString() ?? ""; result.Name = reader.GetString() ?? "";
break; break;
case nameof(Skill.Guid):
result.Guid = reader.GetGuid();
break;
case nameof(Skill.Description): case nameof(Skill.Description):
result.Description = reader.GetString() ?? ""; result.Description = reader.GetString() ?? "";
break; break;
@ -106,6 +109,11 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
writer.WriteNumber(nameof(Skill.Id), (int)value.Id); writer.WriteNumber(nameof(Skill.Id), (int)value.Id);
writer.WriteString(nameof(Skill.Name), value.Name); writer.WriteString(nameof(Skill.Name), value.Name);
if (value.Guid != Guid.Empty)
{
writer.WritePropertyName(nameof(Skill.Guid));
JsonSerializer.Serialize(writer, value.Guid, options);
}
writer.WriteString(nameof(Skill.Description), value.Description); writer.WriteString(nameof(Skill.Description), value.Description);
if (value.GeneralDescription.Length > 0) writer.WriteString(nameof(Skill.GeneralDescription), value.GeneralDescription); if (value.GeneralDescription.Length > 0) writer.WriteString(nameof(Skill.GeneralDescription), value.GeneralDescription);
if (value.Slogan.Length > 0) writer.WriteString(nameof(Skill.Slogan), value.Slogan); if (value.Slogan.Length > 0) writer.WriteString(nameof(Skill.Slogan), value.Slogan);

View File

@ -56,12 +56,6 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
case UserQuery.Column_IsEnable: case UserQuery.Column_IsEnable:
result.IsEnable = reader.GetBoolean(); result.IsEnable = reader.GetBoolean();
break; break;
case UserQuery.Column_Credits:
result.Credits = reader.GetDouble();
break;
case UserQuery.Column_Materials:
result.Materials = reader.GetDouble();
break;
case UserQuery.Column_GameTime: case UserQuery.Column_GameTime:
result.GameTime = reader.GetDouble(); result.GameTime = reader.GetDouble();
break; break;
@ -71,13 +65,15 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
case nameof(Inventory): case nameof(Inventory):
Inventory inventory = NetworkUtility.JsonDeserialize<Inventory>(ref reader, options) ?? Factory.GetInventory(); Inventory inventory = NetworkUtility.JsonDeserialize<Inventory>(ref reader, options) ?? Factory.GetInventory();
result.Inventory.Name = inventory.Name; result.Inventory.Name = inventory.Name;
foreach (string key in inventory.Characters.Keys) result.Inventory.Credits = inventory.Credits;
result.Inventory.Materials = inventory.Materials;
foreach (Character character in inventory.Characters)
{ {
result.Inventory.Characters[key] = inventory.Characters[key]; result.Inventory.Characters.Add(character);
} }
foreach (string key in inventory.Items.Keys) foreach (Item item in inventory.Items)
{ {
result.Inventory.Items[key] = inventory.Items[key]; result.Inventory.Items.Add(item);
} }
break; break;
} }
@ -96,8 +92,6 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
writer.WriteBoolean(UserQuery.Column_IsAdmin, value.IsAdmin); writer.WriteBoolean(UserQuery.Column_IsAdmin, value.IsAdmin);
writer.WriteBoolean(UserQuery.Column_IsOperator, value.IsOperator); writer.WriteBoolean(UserQuery.Column_IsOperator, value.IsOperator);
writer.WriteBoolean(UserQuery.Column_IsEnable, value.IsEnable); writer.WriteBoolean(UserQuery.Column_IsEnable, value.IsEnable);
writer.WriteNumber(UserQuery.Column_Credits, value.Credits);
writer.WriteNumber(UserQuery.Column_Materials, value.Materials);
writer.WriteNumber(UserQuery.Column_GameTime, value.GameTime); writer.WriteNumber(UserQuery.Column_GameTime, value.GameTime);
writer.WriteString(UserQuery.Column_AutoKey, value.AutoKey); writer.WriteString(UserQuery.Column_AutoKey, value.AutoKey);
writer.WritePropertyName(nameof(Inventory)); writer.WritePropertyName(nameof(Inventory));

View File

@ -14,8 +14,6 @@
public const string Column_IsAdmin = "IsAdmin"; public const string Column_IsAdmin = "IsAdmin";
public const string Column_IsOperator = "IsOperator"; public const string Column_IsOperator = "IsOperator";
public const string Column_IsEnable = "IsEnable"; public const string Column_IsEnable = "IsEnable";
public const string Column_Credits = "Credits";
public const string Column_Materials = "Materials";
public const string Column_GameTime = "GameTime"; public const string Column_GameTime = "GameTime";
public const string Column_AutoKey = "AutoKey"; public const string Column_AutoKey = "AutoKey";
public const string Select_Users = $"{Command_Select} {Command_All} {Command_From} {TableName}"; public const string Select_Users = $"{Command_Select} {Command_All} {Command_From} {TableName}";

View File

@ -0,0 +1,76 @@
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for forgetverifycodes
-- ----------------------------
DROP TABLE IF EXISTS `forgetverifycodes`;
CREATE TABLE `forgetverifycodes` (
`Username` varchar(255) DEFAULT NULL,
`Email` varchar(255) DEFAULT NULL,
`ForgetVerifyCode` varchar(255) DEFAULT NULL,
`SendTime` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for regverifycodes
-- ----------------------------
DROP TABLE IF EXISTS `regverifycodes`;
CREATE TABLE `regverifycodes` (
`Username` varchar(255) DEFAULT NULL,
`Email` varchar(255) DEFAULT NULL,
`RegVerifyCode` varchar(255) DEFAULT NULL,
`RegTime` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for rooms
-- ----------------------------
DROP TABLE IF EXISTS `rooms`;
CREATE TABLE `rooms` (
`Id` bigint(20) NOT NULL AUTO_INCREMENT,
`Roomid` varchar(255) NOT NULL DEFAULT '-1',
`CreateTime` datetime NOT NULL,
`RoomMaster` bigint(20) NOT NULL DEFAULT '0',
`RoomType` int(8) DEFAULT '0',
`GameModule` varchar(255) DEFAULT '',
`GameMap` varchar(255) DEFAULT '',
`RoomState` int(8) DEFAULT '0',
`IsRank` int(1) DEFAULT '0',
`HasPass` int(1) DEFAULT '0',
`Password` varchar(255) DEFAULT '',
`MaxUsers` int(8) DEFAULT '0',
PRIMARY KEY (`Id`,`Roomid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for serverloginlogs
-- ----------------------------
DROP TABLE IF EXISTS `serverloginlogs`;
CREATE TABLE `serverloginlogs` (
`ServerName` varchar(255) DEFAULT NULL,
`ServerKey` varchar(255) DEFAULT NULL,
`LoginTime` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`UID` bigint(20) NOT NULL AUTO_INCREMENT,
`Username` varchar(255) NOT NULL,
`Password` varchar(255) NOT NULL,
`RegTime` datetime DEFAULT NULL,
`LastTime` datetime DEFAULT NULL,
`LastIP` varchar(255) DEFAULT '',
`Email` varchar(255) NOT NULL DEFAULT '',
`Nickname` varchar(255) DEFAULT '',
`IsAdmin` int(1) DEFAULT '0',
`IsOperator` int(1) DEFAULT '0',
`IsEnable` int(1) DEFAULT '1',
`Credits` decimal(20,0) DEFAULT '0',
`Materials` decimal(20,0) DEFAULT '0',
`GameTime` decimal(20,0) DEFAULT '0',
`AutoKey` varchar(255) DEFAULT '',
PRIMARY KEY (`UID`,`Username`,`Email`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,73 @@
PRAGMA foreign_keys = OFF;
-- ----------------------------
-- Table structure for forgetverifycodes
-- ----------------------------
DROP TABLE IF EXISTS "main"."forgetverifycodes";
CREATE TABLE forgetverifycodes (
Username TEXT,
Email TEXT,
ForgetVerifyCode TEXT,
SendTime DATETIME
);
-- ----------------------------
-- Table structure for regverifycodes
-- ----------------------------
DROP TABLE IF EXISTS "main"."regverifycodes";
CREATE TABLE regverifycodes (
Username TEXT,
Email TEXT,
RegVerifyCode TEXT,
RegTime DATETIME
);
-- ----------------------------
-- Table structure for rooms
-- ----------------------------
DROP TABLE IF EXISTS "main"."rooms";
CREATE TABLE "rooms" (
"Id" INTEGER PRIMARY KEY AUTOINCREMENT,
"Roomid" TEXT NOT NULL DEFAULT '-1',
"CreateTime" DATETIME NOT NULL,
"RoomMaster" INTEGER NOT NULL DEFAULT 0,
"RoomType" INTEGER DEFAULT 0,
"GameModule" TEXT DEFAULT '',
"GameMap" TEXT DEFAULT '',
"RoomState" INTEGER DEFAULT 0,
"IsRank" INTEGER DEFAULT 0,
"HasPass" INTEGER DEFAULT 0,
"Password" TEXT DEFAULT ''
);
-- ----------------------------
-- Table structure for serverloginlogs
-- ----------------------------
DROP TABLE IF EXISTS "main"."serverloginlogs";
CREATE TABLE serverloginlogs (
ServerName TEXT,
ServerKey TEXT,
LoginTime DATETIME
);
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS "main"."users";
CREATE TABLE users (
UID INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL,
Password TEXT NOT NULL,
RegTime DATETIME,
LastTime DATETIME,
LastIP TEXT DEFAULT '',
Email TEXT NOT NULL DEFAULT '',
Nickname TEXT DEFAULT '',
IsAdmin INTEGER DEFAULT 0,
IsOperator INTEGER DEFAULT 0,
IsEnable INTEGER DEFAULT 1,
Credits REAL DEFAULT 0,
Materials REAL DEFAULT 0,
GameTime REAL DEFAULT 0,
AutoKey TEXT DEFAULT ''
);

View File

@ -492,6 +492,12 @@ namespace Milimoe.FunGame.Core.Model
{ {
_queue.Remove(character); _queue.Remove(character);
_cutCount.Remove(character); _cutCount.Remove(character);
// 进入下一回合
TotalRound++;
LastRound = new(TotalRound);
Rounds.Add(LastRound);
return character; return character;
} }
@ -1048,11 +1054,6 @@ namespace Milimoe.FunGame.Core.Model
WriteLine("\r\n"); WriteLine("\r\n");
// 在时间流逝后,进入下一回合
TotalRound++;
LastRound = new(TotalRound);
Rounds.Add(LastRound);
return timeToReduce; return timeToReduce;
} }
@ -1078,16 +1079,20 @@ namespace Milimoe.FunGame.Core.Model
{ {
LastRound.IsCritical[enemy] = true; LastRound.IsCritical[enemy] = true;
} }
bool isEvaded = damageResult == DamageResult.Evaded; bool isEvaded = damageResult == DamageResult.Evaded;
Dictionary<Effect, double> totalDamageBonus = [];
List<Effect> effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); List<Effect> effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList();
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
if (effect.AlterActualDamageAfterCalculation(actor, enemy, ref damage, isNormalAttack, isMagicDamage, magicType, damageResult)) double damageBonus = effect.AlterActualDamageAfterCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult, ref isEvaded, totalDamageBonus);
totalDamageBonus[effect] = damageBonus;
if (isEvaded)
{ {
isEvaded = true;
damageResult = DamageResult.Evaded; damageResult = DamageResult.Evaded;
} }
} }
damage += totalDamageBonus.Sum(kv => kv.Value);
// 闪避了就没伤害了 // 闪避了就没伤害了
if (!isEvaded) if (!isEvaded)
@ -1215,11 +1220,14 @@ namespace Milimoe.FunGame.Core.Model
return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage); return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage);
} }
Dictionary<Effect, double> totalDamageBonus = [];
effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList();
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
effect.AlterExpectedDamageBeforeCalculation(actor, enemy, ref expectedDamage, isNormalAttack, false, MagicType.None); double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, false, MagicType.None, totalDamageBonus);
totalDamageBonus[effect] = damageBonus;
} }
expectedDamage += totalDamageBonus.Sum(kv => kv.Value);
double dice = Random.Shared.NextDouble(); double dice = Random.Shared.NextDouble();
double throwingBonus = 0; double throwingBonus = 0;
@ -1317,11 +1325,14 @@ namespace Milimoe.FunGame.Core.Model
return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage); return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage);
} }
Dictionary<Effect, double> totalDamageBonus = [];
effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList();
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
effect.AlterExpectedDamageBeforeCalculation(actor, enemy, ref expectedDamage, isNormalAttack, true, magicType); double damageBonus = effect.AlterExpectedDamageBeforeCalculation(actor, enemy, expectedDamage, isNormalAttack, true, magicType, totalDamageBonus);
totalDamageBonus[effect] = damageBonus;
} }
expectedDamage += totalDamageBonus.Sum(kv => kv.Value);
double dice = Random.Shared.NextDouble(); double dice = Random.Shared.NextDouble();
double throwingBonus = 0; double throwingBonus = 0;
@ -1457,6 +1468,7 @@ namespace Milimoe.FunGame.Core.Model
{ {
// 没有其他的团队了,游戏结束 // 没有其他的团队了,游戏结束
EndGameInfo(killTeam); EndGameInfo(killTeam);
return;
} }
if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin) if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin)
{ {
@ -1465,6 +1477,7 @@ namespace Milimoe.FunGame.Core.Model
_eliminatedTeams.Clear(); _eliminatedTeams.Clear();
_eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score)); _eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score));
EndGameInfo(killTeam); EndGameInfo(killTeam);
return;
} }
} }
} }

View File

@ -10,6 +10,11 @@
/// </summary> /// </summary>
public string InGameCurrency { get; set; } = "金币"; public string InGameCurrency { get; set; } = "金币";
/// <summary>
/// 游戏材料名称(第二货币)
/// </summary>
public string InGameMaterial { get; set; } = "材料";
/// <summary> /// <summary>
/// 晋升点数上限 /// 晋升点数上限
/// </summary> /// </summary>