From a6d8b718292c54b689eba99f9790ea0b6db23cb4 Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:37:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BB=BB=E5=8A=A1=E8=AE=A1?= =?UTF-8?q?=E5=88=92=EF=BC=9B=E6=B7=BB=E5=8A=A0=E5=A4=8D=E5=88=B6=E7=89=A9?= =?UTF-8?q?=E5=93=81=E3=80=81=E6=8A=80=E8=83=BD=E6=96=B0=E5=A2=9E=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E9=80=89=E9=A1=B9=20(#102)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加任务计划 * 复制物品、技能新增复制选项;修改游戏平衡常数 * 更新游戏平衡常数 --------- Co-authored-by: yeziuku <53083103+yeziuku@users.noreply.github.com> --- Api/Utility/TaskScheduler.cs | 129 +++++++++++++++++++++++++++ Entity/Character/Character.cs | 8 +- Entity/Character/CharacterBuilder.cs | 35 +++++--- Entity/Item/Item.cs | 56 ++++++------ Entity/Skill/Skill.cs | 38 +++++--- Model/ActionQueue.cs | 3 +- Model/EquilibriumConstant.cs | 4 +- Model/RecurringTask.cs | 35 ++++++++ Model/ScheduledTask.cs | 35 ++++++++ 9 files changed, 280 insertions(+), 63 deletions(-) create mode 100644 Api/Utility/TaskScheduler.cs create mode 100644 Model/RecurringTask.cs create mode 100644 Model/ScheduledTask.cs diff --git a/Api/Utility/TaskScheduler.cs b/Api/Utility/TaskScheduler.cs new file mode 100644 index 0000000..d2c710d --- /dev/null +++ b/Api/Utility/TaskScheduler.cs @@ -0,0 +1,129 @@ +using Milimoe.FunGame.Core.Model; + +namespace Milimoe.FunGame.Core.Api.Utility +{ + public class TaskScheduler + { + /// + /// 任务计划管理器实例,可以直接使用 + /// + public static TaskScheduler Shared { get; } = new(); + + private readonly List _tasks = []; + private readonly List _recurringTasks = []; + private readonly Timer _timer; + private readonly Lock _lock = new(); + + /// + /// 创建一个轻量级的任务计划管理器 + /// + public TaskScheduler() + { + _timer = new Timer(CheckAndRunTasks, null, TimeSpan.Zero, TimeSpan.FromSeconds(1)); + _timer.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } + + /// + /// 添加一个任务计划 + /// + /// + /// + /// + public void AddTask(string name, TimeSpan timeOfDay, Action action) + { + lock (_lock) + { + _tasks.Add(new ScheduledTask(name, timeOfDay, action)); + } + } + + /// + /// 添加一个循环任务 + /// + /// + /// + /// + /// + public void AddRecurringTask(string name, TimeSpan interval, Action action, bool startNow = false) + { + lock (_lock) + { + DateTime now = DateTime.Now; + now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0); + DateTime nextRun = startNow ? now : now.Add(interval); + RecurringTask recurringTask = new(name, interval, action) + { + NextRun = nextRun + }; + _recurringTasks.Add(recurringTask); + } + } + + /// + /// 移除任务计划 + /// + /// + public void RemoveTask(string name) + { + lock (_lock) + { + int removeTasks = _tasks.RemoveAll(t => t.Name == name); + int removeRecurringTasks = _recurringTasks.RemoveAll(t => t.Name == name); + } + } + + /// + /// 执行任务 + /// + /// + private void CheckAndRunTasks(object? state) + { + lock (_lock) + { + DateTime now = DateTime.Now; + + foreach (ScheduledTask task in _tasks) + { + if (!task.IsTodayRun) + { + if (now.TimeOfDay >= task.TimeOfDay && now.TimeOfDay < task.TimeOfDay.Add(TimeSpan.FromSeconds(10))) + { + task.LastRun = now; + ThreadPool.QueueUserWorkItem(_ => + { + try + { + task.Action(); + } + catch (Exception ex) + { + task.Error = ex; + } + }); + } + } + } + + foreach (RecurringTask recurringTask in _recurringTasks) + { + if (now >= recurringTask.NextRun) + { + recurringTask.LastRun = now; + recurringTask.NextRun = recurringTask.NextRun.Add(recurringTask.Interval); + ThreadPool.QueueUserWorkItem(_ => + { + try + { + recurringTask.Action(); + } + catch (Exception ex) + { + recurringTask.Error = ex; + } + }); + } + } + } + } + } +} diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs index 8c9b9e2..dc1596a 100644 --- a/Entity/Character/Character.cs +++ b/Entity/Character/Character.cs @@ -1239,8 +1239,8 @@ namespace Milimoe.FunGame.Core.Entity builder.AppendLine($"力量:{STR:0.##}" + (ExSTR != 0 ? $" [{BaseSTR:0.##} {(ExSTR >= 0 ? "+" : "-")} {Math.Abs(ExSTR):0.##}]" : "") + (showGrowth ? $"({(STRGrowth >= 0 ? "+" : "-")}{Math.Abs(STRGrowth)}/Lv)" : "")); builder.AppendLine($"敏捷:{AGI:0.##}" + (ExAGI != 0 ? $" [{BaseAGI:0.##} {(ExAGI >= 0 ? "+" : "-")} {Math.Abs(ExAGI):0.##}]" : "") + (showGrowth ? $"({(AGIGrowth >= 0 ? "+" : "-")}{Math.Abs(AGIGrowth)}/Lv)" : "")); builder.AppendLine($"智力:{INT:0.##}" + (ExINT != 0 ? $" [{BaseINT:0.##} {(ExINT >= 0 ? "+" : "-")} {Math.Abs(ExINT):0.##}]" : "") + (showGrowth ? $"({(INTGrowth >= 0 ? "+" : "-")}{Math.Abs(INTGrowth)}/Lv)" : "")); - builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * 0.25:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : "")); - builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * 0.1:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : "")); + builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * General.GameplayEquilibriumConstant.STRtoHRFactor:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : "")); + builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * General.GameplayEquilibriumConstant.INTtoMRFactor:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : "")); builder.AppendLine($"暴击率:{CritRate * 100:0.##}%"); builder.AppendLine($"暴击伤害:{CritDMG * 100:0.##}%"); builder.AppendLine($"闪避率:{EvadeRate * 100:0.##}%"); @@ -1362,8 +1362,8 @@ namespace Milimoe.FunGame.Core.Entity builder.AppendLine($"力量:{STR:0.##}" + (ExSTR != 0 ? $" [{BaseSTR:0.##} {(ExSTR >= 0 ? "+" : "-")} {Math.Abs(ExSTR):0.##}]" : "") + (showGrowth ? $"({(STRGrowth >= 0 ? "+" : "-")}{Math.Abs(STRGrowth)}/Lv)" : "")); builder.AppendLine($"敏捷:{AGI:0.##}" + (ExAGI != 0 ? $" [{BaseAGI:0.##} {(ExAGI >= 0 ? "+" : "-")} {Math.Abs(ExAGI):0.##}]" : "") + (showGrowth ? $"({(AGIGrowth >= 0 ? "+" : "-")}{Math.Abs(AGIGrowth)}/Lv)" : "")); builder.AppendLine($"智力:{INT:0.##}" + (ExINT != 0 ? $" [{BaseINT:0.##} {(ExINT >= 0 ? "+" : "-")} {Math.Abs(ExINT):0.##}]" : "") + (showGrowth ? $"({(INTGrowth >= 0 ? "+" : "-")}{Math.Abs(INTGrowth)}/Lv)" : "")); - builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * 0.25:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : "")); - builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * 0.1:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : "")); + builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * General.GameplayEquilibriumConstant.STRtoHRFactor:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : "")); + builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * General.GameplayEquilibriumConstant.INTtoMRFactor:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : "")); if (CharacterState != CharacterState.Actionable) { diff --git a/Entity/Character/CharacterBuilder.cs b/Entity/Character/CharacterBuilder.cs index b864e6b..777743e 100644 --- a/Entity/Character/CharacterBuilder.cs +++ b/Entity/Character/CharacterBuilder.cs @@ -102,8 +102,10 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// + /// + /// /// 构建的新角色 - public Character Build(int level, IEnumerable skills, IEnumerable items, bool newItemGuid = true, EquipSlot? equips = null) + public Character Build(int level, IEnumerable skills, IEnumerable items, bool newItemGuid = true, EquipSlot? equips = null, IEnumerable? itemsDefined = null, IEnumerable? skillsDefined = null) { Character character = Factory.GetCharacter(); character.Id = Id; @@ -133,7 +135,7 @@ namespace Milimoe.FunGame.Core.Entity // 主动技能的Guid表示与其关联的物品 if (skill.Guid == Guid.Empty) { - Skill newskill = skill.Copy(); + Skill newskill = skill.Copy(true, skillsDefined); newskill.Character = character; newskill.Level = skill.Level; if (skill.CurrentCD > 0 && !skill.Enable) @@ -146,7 +148,16 @@ namespace Milimoe.FunGame.Core.Entity } foreach (Item item in items) { - Item newitem = item.Copy(true, !newItemGuid); + Item newitem; + if (itemsDefined != null && itemsDefined.FirstOrDefault(i => i.GetIdName() == item.GetIdName()) is Item itemDefined) + { + newitem = itemDefined.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); + item.SetPropertyToItemModuleNew(newitem); + } + else + { + newitem = item.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); + } newitem.Character = character; character.Items.Add(newitem); } @@ -160,32 +171,32 @@ namespace Milimoe.FunGame.Core.Entity Item? ac2 = equips.Accessory2; if (mcp != null) { - mcp = mcp.Copy(true, !newItemGuid); + mcp = mcp.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); character.Equip(mcp); } if (w != null) { - w = w.Copy(true, !newItemGuid); + w = w.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); character.Equip(w); } if (a != null) { - a = a.Copy(true, !newItemGuid); + a = a.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); character.Equip(a); } if (s != null) { - s = s.Copy(true, !newItemGuid); + s = s.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); character.Equip(s); } if (ac1 != null) { - ac1 = ac1.Copy(true, !newItemGuid); + ac1 = ac1.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); character.Equip(ac1); } if (ac2 != null) { - ac2 = ac2.Copy(true, !newItemGuid); + ac2 = ac2.Copy(true, !newItemGuid, true, itemsDefined, skillsDefined); character.Equip(ac2); } } @@ -199,10 +210,12 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// + /// 对于动态扩展的物品而言,传入已定义的物品表,不使用被复制物品的数据 + /// 对于动态扩展的技能而言,传入已定义的技能表,不使用被复制技能的数据 /// 构建的新角色 - public static Character Build(Character reference, bool newItemGuid = true) + public static Character Build(Character reference, bool newItemGuid = true, IEnumerable? itemsDefined = null, IEnumerable? skillsDefined = null) { - Character character = new CharacterBuilder(reference).Build(reference.Level, reference.Skills, reference.Items, newItemGuid, reference.EquipSlot); + Character character = new CharacterBuilder(reference).Build(reference.Level, reference.Skills, reference.Items, newItemGuid, reference.EquipSlot, itemsDefined, skillsDefined); character.NormalAttack.Level = reference.Level; character.NormalAttack.SetMagicType(reference.NormalAttack.IsMagic, reference.NormalAttack.MagicType); return character; diff --git a/Entity/Item/Item.cs b/Entity/Item/Item.cs index 870506d..b584869 100644 --- a/Entity/Item/Item.cs +++ b/Entity/Item/Item.cs @@ -549,48 +549,46 @@ namespace Milimoe.FunGame.Core.Entity newbyFactory.WeaponType = WeaponType; newbyFactory.EquipSlotType = EquipSlotType; newbyFactory.Equipable = Equipable; + newbyFactory.Unequipable = Unequipable; newbyFactory.IsPurchasable = IsPurchasable; newbyFactory.Price = Price; newbyFactory.IsSellable = IsSellable; newbyFactory.NextSellableTime = NextSellableTime; newbyFactory.IsTradable = IsTradable; newbyFactory.NextTradableTime = NextTradableTime; + newbyFactory.RemainUseTimes = RemainUseTimes; } /// /// 复制一个物品 /// /// - public Item Copy(bool copyLevel = false, bool copyGuid = false) + public Item Copy(bool copyLevel = false, bool copyGuid = false, bool copyProperty = true, IEnumerable? itemsDefined = null, IEnumerable? skillsDefined = null) { Item item = Factory.OpenFactory.GetInstance(Id, Name, []); - SetPropertyToItemModuleNew(item); - item.Id = Id; - item.Name = Name; + Item? itemDefined = null; + if (itemsDefined != null && itemsDefined.FirstOrDefault(i => i.GetIdName() == item.GetIdName()) is Item temp) + { + itemDefined = temp; + } + if (copyProperty) SetPropertyToItemModuleNew(item); if (copyGuid) item.Guid = Guid; - item.Description = Description; - item.GeneralDescription = GeneralDescription; - item.BackgroundStory = BackgroundStory; - item.ItemType = ItemType; - item.Equipable = Equipable; - item.Unequipable = Unequipable; - item.WeaponType = WeaponType; - item.QualityType = QualityType; - item.RarityType = RarityType; - item.RankType = RankType; - item.Key = Key; - item.Enable = Enable; - item.IsInGameItem = IsInGameItem; - item.IsPurchasable = IsPurchasable; - item.Price = Price; - item.IsSellable = IsSellable; - item.NextSellableTime = NextSellableTime; - item.IsTradable = IsTradable; - item.NextTradableTime = NextTradableTime; - item.RemainUseTimes = RemainUseTimes; + itemDefined ??= this; + item.Id = itemDefined.Id; + item.Name = itemDefined.Name; + item.Description = itemDefined.Description; + item.GeneralDescription = itemDefined.GeneralDescription; + item.BackgroundStory = itemDefined.BackgroundStory; + item.ItemType = itemDefined.ItemType; + item.QualityType = itemDefined.QualityType; + item.RarityType = itemDefined.RarityType; + item.RankType = itemDefined.RankType; + item.Key = itemDefined.Key; + item.Enable = itemDefined.Enable; + item.IsInGameItem = itemDefined.IsInGameItem; if (item is OpenItem) { - item.Skills.Active = Skills.Active?.Copy(); + item.Skills.Active = Skills.Active?.Copy(true, skillsDefined); if (item.Skills.Active != null) { item.Skills.Active.Level = copyLevel ? (Skills.Active?.Level ?? 1) : 1; @@ -598,7 +596,7 @@ namespace Milimoe.FunGame.Core.Entity } foreach (Skill skill in Skills.Passives) { - Skill newskill = skill.Copy(); + Skill newskill = skill.Copy(true, skillsDefined); newskill.Item = item; newskill.Level = copyLevel ? skill.Level : 1; newskill.Guid = item.Guid; @@ -606,7 +604,7 @@ namespace Milimoe.FunGame.Core.Entity } foreach (Skill skill in Skills.Magics) { - Skill newskill = skill.Copy(); + Skill newskill = skill.Copy(true, skillsDefined); newskill.Item = item; newskill.Level = copyLevel ? skill.Level : 1; newskill.Guid = item.Guid; @@ -638,10 +636,6 @@ namespace Milimoe.FunGame.Core.Entity /// public void SetMagicsLevel(int level) { - if (Skills.Active != null) - { - Skills.Active.Level = level; - } foreach (Skill skill in Skills.Magics) { skill.Level = level; diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs index 5b3e048..3907085 100644 --- a/Entity/Skill/Skill.cs +++ b/Entity/Skill/Skill.cs @@ -240,6 +240,7 @@ namespace Milimoe.FunGame.Core.Entity /// public void SetPropertyToItemModuleNew(Skill newbyFactory) { + newbyFactory.GamingQueue = GamingQueue; newbyFactory.Enable = Enable; newbyFactory.IsInEffect = IsInEffect; newbyFactory.CurrentCD = CurrentCD; @@ -452,26 +453,35 @@ namespace Milimoe.FunGame.Core.Entity /// 复制一个技能 /// /// - public Skill Copy() + public Skill Copy(bool copyProperty = true, IEnumerable? skillsDefined = null) { Dictionary args = new() { { "values", Values } }; + Skill? skillDefined = null; + if (skillsDefined != null && skillsDefined.FirstOrDefault(i => i.GetIdName() == GetIdName()) is Skill temp) + { + skillDefined = temp; + } + if (skillDefined != null) + { + args["values"] = skillDefined.Values; + } Skill skill = Factory.OpenFactory.GetInstance(Id, Name, args); - SetPropertyToItemModuleNew(skill); - skill.Id = Id; - skill.Name = Name; - skill.Description = Description; - skill.GeneralDescription = GeneralDescription; - skill.SkillType = SkillType; - skill.MPCost = MPCost; - skill.CastTime = CastTime; - skill.EPCost = EPCost; - skill.CD = CD; - skill.CurrentCD = CurrentCD; - skill.HardnessTime = HardnessTime; - skill.GamingQueue = GamingQueue; + skillDefined ??= skill; + if (copyProperty) SetPropertyToItemModuleNew(skill); + skill.Id = skillDefined.Id; + skill.Name = skillDefined.Name; + skill.Description = skillDefined.Description; + skill.GeneralDescription = skillDefined.GeneralDescription; + skill.SkillType = skillDefined.SkillType; + skill.MPCost = skillDefined.MPCost; + skill.CastTime = skillDefined.CastTime; + skill.EPCost = skillDefined.EPCost; + skill.CD = skillDefined.CD; + skill.HardnessTime = skillDefined.HardnessTime; + skill.GamingQueue = skillDefined.GamingQueue; if (skill is OpenSkill) { foreach (Effect e in Effects) diff --git a/Model/ActionQueue.cs b/Model/ActionQueue.cs index 449938a..d3ddbce 100644 --- a/Model/ActionQueue.cs +++ b/Model/ActionQueue.cs @@ -684,6 +684,7 @@ namespace Milimoe.FunGame.Core.Model } else { + decided = true; WriteLine("[ " + character + $" ] 完全行动不能!"); type = CharacterActionType.None; } @@ -1639,7 +1640,7 @@ namespace Milimoe.FunGame.Core.Model foreach (Character caster in castingSkills) { SkillTarget st = _castingSkills[caster]; - if (st.Targets.Remove(death) && st.Targets.Count == 0) + if (st.Targets.Remove(death)) { _castingSkills.Remove(caster); if (caster.CharacterState == CharacterState.Casting) diff --git a/Model/EquilibriumConstant.cs b/Model/EquilibriumConstant.cs index b386046..d76d3b9 100644 --- a/Model/EquilibriumConstant.cs +++ b/Model/EquilibriumConstant.cs @@ -203,7 +203,7 @@ /// /// 每 1 点力量增加生命回复力 /// - public double STRtoHRFactor { get; set; } = 0.25; + public double STRtoHRFactor { get; set; } = 0.025; /// /// 每 1 点力量增加物理护甲 @@ -228,7 +228,7 @@ /// /// 每 1 点智力增加魔法回复力 /// - public double INTtoMRFactor { get; set; } = 0.1; + public double INTtoMRFactor { get; set; } = 0.01; /// /// 每 1 点智力减少魔法消耗 diff --git a/Model/RecurringTask.cs b/Model/RecurringTask.cs new file mode 100644 index 0000000..61ab15d --- /dev/null +++ b/Model/RecurringTask.cs @@ -0,0 +1,35 @@ +namespace Milimoe.FunGame.Core.Model +{ + public class RecurringTask(string name, TimeSpan interval, Action action) + { + /// + /// 任务名称 + /// + public string Name { get; set; } = name; + + /// + /// 循环执行间隔 + /// + public TimeSpan Interval { get; set; } = interval; + + /// + /// 任务执行逻辑 + /// + public Action Action { get; set; } = action; + + /// + /// 记录上一次运行时间 + /// + public DateTime? LastRun { get; set; } = null; + + /// + /// 记录下一次运行时间 + /// + public DateTime NextRun { get; set; } = DateTime.MaxValue; + + /// + /// 最后一次运行时发生的错误 + /// + public Exception? Error { get; set; } + } +} diff --git a/Model/ScheduledTask.cs b/Model/ScheduledTask.cs new file mode 100644 index 0000000..880e994 --- /dev/null +++ b/Model/ScheduledTask.cs @@ -0,0 +1,35 @@ +namespace Milimoe.FunGame.Core.Model +{ + public class ScheduledTask(string name, TimeSpan timeSpan, Action action) + { + /// + /// 任务名称 + /// + public string Name { get; set; } = name; + + /// + /// 每天的目标时间 + /// + public TimeSpan TimeOfDay { get; set; } = timeSpan; + + /// + /// 任务执行逻辑 + /// + public Action Action { get; set; } = action; + + /// + /// 记录上一次运行时间 + /// + public DateTime? LastRun { get; set; } = null; + + /// + /// 当天是否已经运行 + /// + public bool IsTodayRun => LastRun.HasValue && LastRun.Value.Date == DateTime.Today; + + /// + /// 最后一次运行时发生的错误 + /// + public Exception? Error { get; set; } + } +}