From 3db586cab2d15ddb61f40c827de934f5ba30268b Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:30:26 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=B8=E5=A4=9A=E6=9B=B4=E6=96=B0=E5=92=8C?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D=20(#97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加 OpenFactory,可以动态扩展技能和物品 * 修改 Effect 的反序列化解析;增加对闪避/暴击判定的先前事件编程接口 * 补充魔法伤害的判定 * 装备系统优化;角色的复制问题修复 * 添加物品品质;更新装备饰品替换机制;添加第一滴血、团队模式 * 添加技能选取 * 添加团队死斗模式 --- .../CharacterFactory.cs | 2 +- .../EffectFactory.cs | 2 +- .../InventoryFactory.cs | 2 +- Api/{Factory => EnityFactory}/ItemFactory.cs | 2 +- Api/{Factory => EnityFactory}/RoomFactory.cs | 2 +- Api/{Factory => EnityFactory}/SkillFactory.cs | 2 +- Api/{Factory => EnityFactory}/UserFactory.cs | 2 +- Api/OpenEntityAdapter/OpenItemAdapter.cs | 20 + Api/OpenEntityAdapter/OpenSkillAdapter.cs | 20 + Api/Utility/EntityModuleConfig.cs | 2 +- Api/Utility/Factory.cs | 193 ++- Api/Utility/GameModuleLoader.cs | 1 - Controller/AddonController.cs | 3 +- Entity/Character/Character.cs | 193 ++- Entity/Character/CharacterProfile.cs | 19 + Entity/Character/EquipSlot.cs | 38 +- Entity/Character/MagicResistance.cs | 53 + Entity/Item/Item.cs | 181 ++- Entity/Skill/Effect.cs | 69 +- Entity/Skill/OpenSkill.cs | 34 + Entity/Skill/Skill.cs | 125 +- Entity/Skill/SkillTarget.cs | 8 + Entity/Statistics/CharacterStatistics.cs | 3 + Entity/System/RoundRecord.cs | 14 + Entity/System/Team.cs | 39 + Interface/Base/IGamingQueue.cs | 22 + Library/Common/Addon/CharacterModule.cs | 21 +- .../Common/Addon/Example/ExampleGameModule.cs | 46 +- Library/Common/Addon/GameModuleDepend.cs | 21 +- Library/Common/Addon/ItemModule.cs | 22 +- Library/Common/Addon/SkillModule.cs | 28 +- .../Common/JsonConverter/EffectConverter.cs | 36 + .../JsonConverter/EquipSlotConverter.cs | 4 +- Library/Common/JsonConverter/ItemConverter.cs | 12 + .../Common/JsonConverter/SkillConverter.cs | 30 +- Library/Constant/ConstantSet.cs | 50 +- Library/Constant/General.cs | 6 + Library/Constant/TypeEnum.cs | 19 +- Library/Exception/Exception.cs | 5 + Model/ActionQueue.cs | 1098 ++++++++++++----- Model/EquilibriumConstant.cs | 26 +- Service/AddonManager.cs | 3 +- 42 files changed, 1869 insertions(+), 609 deletions(-) rename Api/{Factory => EnityFactory}/CharacterFactory.cs (84%) rename Api/{Factory => EnityFactory}/EffectFactory.cs (85%) rename Api/{Factory => EnityFactory}/InventoryFactory.cs (90%) rename Api/{Factory => EnityFactory}/ItemFactory.cs (84%) rename Api/{Factory => EnityFactory}/RoomFactory.cs (94%) rename Api/{Factory => EnityFactory}/SkillFactory.cs (84%) rename Api/{Factory => EnityFactory}/UserFactory.cs (95%) create mode 100644 Api/OpenEntityAdapter/OpenItemAdapter.cs create mode 100644 Api/OpenEntityAdapter/OpenSkillAdapter.cs create mode 100644 Entity/Skill/OpenSkill.cs create mode 100644 Entity/Skill/SkillTarget.cs create mode 100644 Entity/System/RoundRecord.cs create mode 100644 Entity/System/Team.cs diff --git a/Api/Factory/CharacterFactory.cs b/Api/EnityFactory/CharacterFactory.cs similarity index 84% rename from Api/Factory/CharacterFactory.cs rename to Api/EnityFactory/CharacterFactory.cs index a4682d9..8f4a313 100644 --- a/Api/Factory/CharacterFactory.cs +++ b/Api/EnityFactory/CharacterFactory.cs @@ -1,7 +1,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class CharacterFactory : IFactory { diff --git a/Api/Factory/EffectFactory.cs b/Api/EnityFactory/EffectFactory.cs similarity index 85% rename from Api/Factory/EffectFactory.cs rename to Api/EnityFactory/EffectFactory.cs index ef16e7f..0448d71 100644 --- a/Api/Factory/EffectFactory.cs +++ b/Api/EnityFactory/EffectFactory.cs @@ -1,7 +1,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class EffectFactory : IFactory { diff --git a/Api/Factory/InventoryFactory.cs b/Api/EnityFactory/InventoryFactory.cs similarity index 90% rename from Api/Factory/InventoryFactory.cs rename to Api/EnityFactory/InventoryFactory.cs index 2138633..66d282b 100644 --- a/Api/Factory/InventoryFactory.cs +++ b/Api/EnityFactory/InventoryFactory.cs @@ -2,7 +2,7 @@ using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class InventoryFactory : IFactory { diff --git a/Api/Factory/ItemFactory.cs b/Api/EnityFactory/ItemFactory.cs similarity index 84% rename from Api/Factory/ItemFactory.cs rename to Api/EnityFactory/ItemFactory.cs index 00b8329..c55ec7c 100644 --- a/Api/Factory/ItemFactory.cs +++ b/Api/EnityFactory/ItemFactory.cs @@ -1,7 +1,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class ItemFactory : IFactory { diff --git a/Api/Factory/RoomFactory.cs b/Api/EnityFactory/RoomFactory.cs similarity index 94% rename from Api/Factory/RoomFactory.cs rename to Api/EnityFactory/RoomFactory.cs index f59f328..66a2a22 100644 --- a/Api/Factory/RoomFactory.cs +++ b/Api/EnityFactory/RoomFactory.cs @@ -2,7 +2,7 @@ using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class RoomFactory : IFactory { diff --git a/Api/Factory/SkillFactory.cs b/Api/EnityFactory/SkillFactory.cs similarity index 84% rename from Api/Factory/SkillFactory.cs rename to Api/EnityFactory/SkillFactory.cs index 336d87f..dccd8ee 100644 --- a/Api/Factory/SkillFactory.cs +++ b/Api/EnityFactory/SkillFactory.cs @@ -1,7 +1,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class SkillFactory : IFactory { diff --git a/Api/Factory/UserFactory.cs b/Api/EnityFactory/UserFactory.cs similarity index 95% rename from Api/Factory/UserFactory.cs rename to Api/EnityFactory/UserFactory.cs index 5668e77..7d91384 100644 --- a/Api/Factory/UserFactory.cs +++ b/Api/EnityFactory/UserFactory.cs @@ -2,7 +2,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; -namespace Milimoe.FunGame.Core.Api.Factory +namespace Milimoe.FunGame.Core.Api.EntityFactory { internal class UserFactory : IFactory { diff --git a/Api/OpenEntityAdapter/OpenItemAdapter.cs b/Api/OpenEntityAdapter/OpenItemAdapter.cs new file mode 100644 index 0000000..e671743 --- /dev/null +++ b/Api/OpenEntityAdapter/OpenItemAdapter.cs @@ -0,0 +1,20 @@ +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Entity; + +namespace Milimoe.FunGame.Core.Api.OpenEntityAdapter +{ + public class OpenItemAdapter + { + public static void Adaptation(EntityModuleConfig config) where T : BaseEntity + { + foreach (string key in config.Keys) + { + if (config[key] is Item prev) + { + Item next = prev.Copy(); + if (next is T t) config[key] = t; + } + } + } + } +} diff --git a/Api/OpenEntityAdapter/OpenSkillAdapter.cs b/Api/OpenEntityAdapter/OpenSkillAdapter.cs new file mode 100644 index 0000000..1b644d4 --- /dev/null +++ b/Api/OpenEntityAdapter/OpenSkillAdapter.cs @@ -0,0 +1,20 @@ +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Entity; + +namespace Milimoe.FunGame.Core.Api.OpenEntityAdapter +{ + public class OpenSkillAdapter + { + public static void Adaptation(EntityModuleConfig config) where T : BaseEntity + { + foreach (string key in config.Keys) + { + if (config[key] is Skill prev) + { + Skill next = prev.Copy(); + if (next is T t) config[key] = t; + } + } + } + } +} diff --git a/Api/Utility/EntityModuleConfig.cs b/Api/Utility/EntityModuleConfig.cs index c16fcb3..8f8769e 100644 --- a/Api/Utility/EntityModuleConfig.cs +++ b/Api/Utility/EntityModuleConfig.cs @@ -4,7 +4,7 @@ using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Api.Utility { /// - /// 简易的实体模组配置文件生成器 + /// 简易的实体模组配置文件生成器,适用范围:动态扩展技能和物品、保存玩家的存档 /// 仅支持继承了 的实体类型,每个 仅保存一种实体类型的数据 /// 文件会保存为:程序目录/configs//.json /// diff --git a/Api/Utility/Factory.cs b/Api/Utility/Factory.cs index 4acdcf7..f71e575 100644 --- a/Api/Utility/Factory.cs +++ b/Api/Utility/Factory.cs @@ -1,5 +1,6 @@ using System.Data; -using Milimoe.FunGame.Core.Api.Factory; +using Milimoe.FunGame.Core.Api.EntityFactory; +using Milimoe.FunGame.Core.Api.OpenEntityAdapter; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Entity; @@ -8,6 +9,194 @@ namespace Milimoe.FunGame.Core.Api.Utility { public class Factory { + /// + /// 支持动态扩展的工厂实例 + /// + public static Factory OpenFactory { get; } = new(); + + private Factory() + { + + } + + internal HashSet> CharacterFactories { get; } = []; + internal HashSet> InventoryFactories { get; } = []; + internal HashSet> SkillFactories { get; } = []; + internal HashSet> EffectFactories { get; } = []; + internal HashSet> ItemFactories { get; } = []; + internal HashSet> RoomFactories { get; } = []; + internal HashSet> UserFactories { get; } = []; + + public delegate T? EntityFactoryDelegate(long id, string name, Dictionary args); + + /// + /// 注册工厂方法 + /// + /// + /// + public void RegisterFactory(EntityFactoryDelegate d) + { + if (typeof(T) == typeof(Character) && d is EntityFactoryDelegate character) + { + CharacterFactories.Add(character); + } + if (typeof(T) == typeof(Inventory) && d is EntityFactoryDelegate inventory) + { + InventoryFactories.Add(inventory); + } + if (typeof(T) == typeof(Skill) && d is EntityFactoryDelegate skill) + { + SkillFactories.Add(skill); + } + if (typeof(T) == typeof(Effect) && d is EntityFactoryDelegate effect) + { + EffectFactories.Add(effect); + } + if (typeof(T) == typeof(Item) && d is EntityFactoryDelegate item) + { + ItemFactories.Add(item); + } + if (typeof(T) == typeof(Room) && d is EntityFactoryDelegate room) + { + RoomFactories.Add(room); + } + if (typeof(T) == typeof(User) && d is EntityFactoryDelegate user) + { + UserFactories.Add(user); + } + } + + /// + /// 构造一个实体实例 + /// + /// + /// + /// + /// + /// + /// + public T GetInstance(long id, string name, Dictionary args) + { + if (typeof(T) == typeof(Character)) + { + foreach (EntityFactoryDelegate d in CharacterFactories) + { + if (d.Invoke(id, name, args) is T character) + { + return character; + } + } + return (T)(object)GetCharacter(); + } + if (typeof(T) == typeof(Inventory)) + { + foreach (EntityFactoryDelegate d in InventoryFactories) + { + if (d.Invoke(id, name, args) is T inventory) + { + return inventory; + } + } + return (T)(object)GetInventory(); + } + if (typeof(T) == typeof(Skill)) + { + foreach (EntityFactoryDelegate d in SkillFactories) + { + if (d.Invoke(id, name, args) is T skill) + { + return skill; + } + } + return (T)(object)new OpenSkill(id, name, args); + } + if (typeof(T) == typeof(Effect)) + { + foreach (EntityFactoryDelegate d in EffectFactories) + { + if (d.Invoke(id, name, args) is T effect) + { + return effect; + } + } + return (T)(object)GetEffect(); + } + if (typeof(T) == typeof(Item)) + { + foreach (EntityFactoryDelegate d in ItemFactories) + { + if (d.Invoke(id, name, args) is T item) + { + return item; + } + } + return (T)(object)GetItem(); + } + if (typeof(T) == typeof(Room)) + { + foreach (EntityFactoryDelegate d in RoomFactories) + { + if (d.Invoke(id, name, args) is T room) + { + return room; + } + } + return (T)(object)GetRoom(); + } + if (typeof(T) == typeof(User)) + { + foreach (EntityFactoryDelegate d in UserFactories) + { + if (d.Invoke(id, name, args) is T user) + { + return user; + } + } + return (T)(object)GetUser(); + } + throw new NotSupportedInstanceClassException(); + } + + /// + /// 此方法使用 取得一个实体字典 + /// + /// + /// + /// + /// + public static Dictionary GetGameModuleInstances(string module_name, string file_name) where T : BaseEntity + { + EntityModuleConfig config = new(module_name, file_name); + config.LoadConfig(); + if (typeof(T) == typeof(Skill)) + { + OpenSkillAdapter.Adaptation(config); + } + if (typeof(T) == typeof(Item)) + { + OpenItemAdapter.Adaptation(config); + } + return config; + } + + /// + /// 使用 构造一个实体字典并保存 + /// + /// + /// + /// + /// + /// + public static void CreateGameModuleEntityConfig(string module_name, string file_name, Dictionary dict) where T : BaseEntity + { + EntityModuleConfig config = new(module_name, file_name); + foreach (string key in dict.Keys) + { + config[key] = dict[key]; + } + config.SaveConfig(); + } + private readonly static CharacterFactory CharacterFactory = new(); private readonly static InventoryFactory InventoryFactory = new(); private readonly static SkillFactory SkillFactory = new(); @@ -152,7 +341,7 @@ namespace Milimoe.FunGame.Core.Api.Utility /// 获取大厅(-1号房) /// /// - internal static Room GetHall() + public static Room GetHall() { return RoomFactory.Create(); } diff --git a/Api/Utility/GameModuleLoader.cs b/Api/Utility/GameModuleLoader.cs index f162434..d5f3d87 100644 --- a/Api/Utility/GameModuleLoader.cs +++ b/Api/Utility/GameModuleLoader.cs @@ -1,4 +1,3 @@ -using System.Collections; using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Service; diff --git a/Controller/AddonController.cs b/Controller/AddonController.cs index 40fe3ce..14a76dd 100644 --- a/Controller/AddonController.cs +++ b/Controller/AddonController.cs @@ -1,5 +1,4 @@ -using System.Collections; -using Milimoe.FunGame.Core.Api.Transmittal; +using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Library.Constant; diff --git a/Entity/Character/Character.cs b/Entity/Character/Character.cs index 3eb9d80..1243518 100644 --- a/Entity/Character/Character.cs +++ b/Entity/Character/Character.cs @@ -809,7 +809,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// - public bool Equip(Item item, EquipItemToSlot slot) + public bool Equip(Item item, EquipSlotType slot) { bool result = false; double pastHP = HP; @@ -818,57 +818,59 @@ namespace Milimoe.FunGame.Core.Entity double pastMaxMP = MaxMP; switch (slot) { - case EquipItemToSlot.MagicCardPack: + case EquipSlotType.MagicCardPack: if (item.ItemType == ItemType.MagicCardPack) { - UnEquip(EquipItemToSlot.MagicCardPack); + UnEquip(EquipSlotType.MagicCardPack); EquipSlot.MagicCardPack = item; - item.OnItemEquip(this, EquipItemToSlot.MagicCardPack); + item.OnItemEquip(this, EquipSlotType.MagicCardPack); result = true; } break; - case EquipItemToSlot.Weapon: + case EquipSlotType.Weapon: if (item.ItemType == ItemType.Weapon) { - UnEquip(EquipItemToSlot.Weapon); + UnEquip(EquipSlotType.Weapon); EquipSlot.Weapon = item; - item.OnItemEquip(this, EquipItemToSlot.Weapon); + item.OnItemEquip(this, EquipSlotType.Weapon); result = true; } break; - case EquipItemToSlot.Armor: + case EquipSlotType.Armor: if (item.ItemType == ItemType.Armor) { - UnEquip(EquipItemToSlot.Armor); + UnEquip(EquipSlotType.Armor); EquipSlot.Armor = item; - item.OnItemEquip(this, EquipItemToSlot.Armor); + item.OnItemEquip(this, EquipSlotType.Armor); result = true; } break; - case EquipItemToSlot.Shoes: + case EquipSlotType.Shoes: if (item.ItemType == ItemType.Shoes) { - UnEquip(EquipItemToSlot.Shoes); + UnEquip(EquipSlotType.Shoes); EquipSlot.Shoes = item; - item.OnItemEquip(this, EquipItemToSlot.Shoes); + item.OnItemEquip(this, EquipSlotType.Shoes); result = true; } break; - case EquipItemToSlot.Accessory1: + case EquipSlotType.Accessory1: if (item.ItemType == ItemType.Accessory) { - UnEquip(EquipItemToSlot.Accessory1); + UnEquip(EquipSlotType.Accessory1); EquipSlot.Accessory1 = item; - item.OnItemEquip(this, EquipItemToSlot.Accessory1); + EquipSlot.LastEquipSlotType = EquipSlotType.Accessory1; + item.OnItemEquip(this, EquipSlotType.Accessory1); result = true; } break; - case EquipItemToSlot.Accessory2: + case EquipSlotType.Accessory2: if (item.ItemType == ItemType.Accessory) { - UnEquip(EquipItemToSlot.Accessory2); + UnEquip(EquipSlotType.Accessory2); EquipSlot.Accessory2 = item; - item.OnItemEquip(this, EquipItemToSlot.Accessory2); + EquipSlot.LastEquipSlotType = EquipSlotType.Accessory2; + item.OnItemEquip(this, EquipSlotType.Accessory2); result = true; } break; @@ -891,21 +893,29 @@ namespace Milimoe.FunGame.Core.Entity switch (item.ItemType) { case ItemType.MagicCardPack: - return Equip(item, EquipItemToSlot.MagicCardPack); + return Equip(item, EquipSlotType.MagicCardPack); case ItemType.Weapon: - return Equip(item, EquipItemToSlot.Weapon); + return Equip(item, EquipSlotType.Weapon); case ItemType.Armor: - return Equip(item, EquipItemToSlot.Armor); + return Equip(item, EquipSlotType.Armor); case ItemType.Shoes: - return Equip(item, EquipItemToSlot.Shoes); + return Equip(item, EquipSlotType.Shoes); case ItemType.Accessory: - if (EquipSlot.Accessory1 != null && EquipSlot.Accessory2 is null) + if (EquipSlot.Accessory1 is null) { - return Equip(item, EquipItemToSlot.Accessory2); + return Equip(item, EquipSlotType.Accessory1); + } + else if (EquipSlot.Accessory1 != null && EquipSlot.Accessory2 is null) + { + return Equip(item, EquipSlotType.Accessory2); + } + else if (EquipSlot.Accessory1 != null && EquipSlot.Accessory2 != null && EquipSlot.LastEquipSlotType == EquipSlotType.Accessory1) + { + return Equip(item, EquipSlotType.Accessory2); } else { - return Equip(item, EquipItemToSlot.Accessory1); + return Equip(item, EquipSlotType.Accessory1); } } return false; @@ -916,51 +926,51 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// - public Item? UnEquip(EquipItemToSlot type) + public Item? UnEquip(EquipSlotType type) { Item? result = null; switch (type) { - case EquipItemToSlot.MagicCardPack: + case EquipSlotType.MagicCardPack: if (EquipSlot.MagicCardPack != null) { result = EquipSlot.MagicCardPack; - EquipSlot.MagicCardPack.OnItemUnEquip(EquipItemToSlot.MagicCardPack); + EquipSlot.MagicCardPack.OnItemUnEquip(EquipSlotType.MagicCardPack); } break; - case EquipItemToSlot.Weapon: + case EquipSlotType.Weapon: if (EquipSlot.Weapon != null) { result = EquipSlot.Weapon; - EquipSlot.Weapon.OnItemUnEquip(EquipItemToSlot.Weapon); + EquipSlot.Weapon.OnItemUnEquip(EquipSlotType.Weapon); } break; - case EquipItemToSlot.Armor: + case EquipSlotType.Armor: if (EquipSlot.Armor != null) { result = EquipSlot.Armor; - EquipSlot.Armor.OnItemUnEquip(EquipItemToSlot.Armor); + EquipSlot.Armor.OnItemUnEquip(EquipSlotType.Armor); } break; - case EquipItemToSlot.Shoes: + case EquipSlotType.Shoes: if (EquipSlot.Shoes != null) { result = EquipSlot.Shoes; - EquipSlot.Shoes.OnItemUnEquip(EquipItemToSlot.Shoes); + EquipSlot.Shoes.OnItemUnEquip(EquipSlotType.Shoes); } break; - case EquipItemToSlot.Accessory1: + case EquipSlotType.Accessory1: if (EquipSlot.Accessory1 != null) { result = EquipSlot.Accessory1; - EquipSlot.Accessory1.OnItemUnEquip(EquipItemToSlot.Accessory1); + EquipSlot.Accessory1.OnItemUnEquip(EquipSlotType.Accessory1); } break; - case EquipItemToSlot.Accessory2: + case EquipSlotType.Accessory2: if (EquipSlot.Accessory2 != null) { result = EquipSlot.Accessory2; - EquipSlot.Accessory2.OnItemUnEquip(EquipItemToSlot.Accessory2); + EquipSlot.Accessory2.OnItemUnEquip(EquipSlotType.Accessory2); } break; } @@ -1128,32 +1138,32 @@ namespace Milimoe.FunGame.Core.Entity builder.AppendLine("== 装备栏 =="); if (EquipSlot.MagicCardPack != null) { - builder.AppendLine(ItemSet.GetEquipItemToSlotTypeName(EquipItemToSlot.MagicCardPack) + ":" + EquipSlot.MagicCardPack.Name); + builder.AppendLine(ItemSet.GetEquipSlotTypeName(EquipSlotType.MagicCardPack) + ":" + EquipSlot.MagicCardPack.Name); builder.AppendLine(EquipSlot.MagicCardPack.Description); } if (EquipSlot.Weapon != null) { - builder.AppendLine(ItemSet.GetEquipItemToSlotTypeName(EquipItemToSlot.Weapon) + ":" + EquipSlot.Weapon.Name); + builder.AppendLine(ItemSet.GetEquipSlotTypeName(EquipSlotType.Weapon) + ":" + EquipSlot.Weapon.Name); builder.AppendLine(EquipSlot.Weapon.Description); } if (EquipSlot.Armor != null) { - builder.AppendLine(ItemSet.GetEquipItemToSlotTypeName(EquipItemToSlot.Armor) + ":" + EquipSlot.Armor.Name); + builder.AppendLine(ItemSet.GetEquipSlotTypeName(EquipSlotType.Armor) + ":" + EquipSlot.Armor.Name); builder.AppendLine(EquipSlot.Armor.Description); } if (EquipSlot.Shoes != null) { - builder.AppendLine(ItemSet.GetEquipItemToSlotTypeName(EquipItemToSlot.Shoes) + ":" + EquipSlot.Shoes.Name); + builder.AppendLine(ItemSet.GetEquipSlotTypeName(EquipSlotType.Shoes) + ":" + EquipSlot.Shoes.Name); builder.AppendLine(EquipSlot.Shoes.Description); } if (EquipSlot.Accessory1 != null) { - builder.AppendLine(ItemSet.GetEquipItemToSlotTypeName(EquipItemToSlot.Accessory1) + ":" + EquipSlot.Accessory1.Name); + builder.AppendLine(ItemSet.GetEquipSlotTypeName(EquipSlotType.Accessory1) + ":" + EquipSlot.Accessory1.Name); builder.AppendLine(EquipSlot.Accessory1.Description); } if (EquipSlot.Accessory2 != null) { - builder.AppendLine(ItemSet.GetEquipItemToSlotTypeName(EquipItemToSlot.Accessory2) + ":" + EquipSlot.Accessory2.Name); + builder.AppendLine(ItemSet.GetEquipSlotTypeName(EquipSlotType.Accessory2) + ":" + EquipSlot.Accessory2.Name); builder.AppendLine(EquipSlot.Accessory2.Description); } } @@ -1288,7 +1298,7 @@ namespace Milimoe.FunGame.Core.Entity Name = Name, FirstName = FirstName, NickName = NickName, - Profile = Profile, + Profile = Profile.Copy(), MagicType = MagicType, FirstRoleType = FirstRoleType, SecondRoleType = SecondRoleType, @@ -1307,7 +1317,7 @@ namespace Milimoe.FunGame.Core.Entity ExATK2 = ExATK2, InitialDEF = InitialDEF, ExDEF2 = ExDEF2, - MDF = MDF, + MDF = MDF.Copy(), PhysicalPenetration = PhysicalPenetration, MagicalPenetration = MagicalPenetration, InitialHR = InitialHR, @@ -1349,5 +1359,94 @@ namespace Milimoe.FunGame.Core.Entity c.Recovery(); return c; } + + /// + /// 复活此角色,回复出厂状态 + /// + /// + public void Respawn() + { + Item? mcp = UnEquip(EquipSlotType.MagicCardPack); + Item? w = UnEquip(EquipSlotType.Weapon); + Item? a = UnEquip(EquipSlotType.Armor); + Item? s = UnEquip(EquipSlotType.Shoes); + Item? ac1 = UnEquip(EquipSlotType.Accessory1); + Item? ac2 = UnEquip(EquipSlotType.Accessory2); + List skills = new(Skills); + List items = new(Items); + Character c = Copy(); + Effects.Clear(); + Skills.Clear(); + Items.Clear(); + Id = c.Id; + Name = c.Name; + FirstName = c.FirstName; + NickName = c.NickName; + Profile = c.Profile.Copy(); + MagicType = c.MagicType; + FirstRoleType = c.FirstRoleType; + SecondRoleType = c.SecondRoleType; + ThirdRoleType = c.ThirdRoleType; + Promotion = c.Promotion; + PrimaryAttribute = c.PrimaryAttribute; + Level = c.Level; + EXP = c.EXP; + CharacterState = c.CharacterState; + InitialHP = c.InitialHP; + ExHP2 = c.ExHP2; + InitialMP = c.InitialMP; + ExMP2 = c.ExMP2; + EP = c.EP; + InitialATK = c.InitialATK; + ExATK2 = c.ExATK2; + InitialDEF = c.InitialDEF; + ExDEF2 = c.ExDEF2; + MDF = c.MDF.Copy(); + PhysicalPenetration = c.PhysicalPenetration; + MagicalPenetration = c.MagicalPenetration; + InitialHR = c.InitialHR; + ExHR = c.ExHR; + InitialMR = c.InitialMR; + ExMR = c.ExMR; + ER = c.ER; + InitialSTR = c.InitialSTR; + InitialAGI = c.InitialAGI; + InitialINT = c.InitialINT; + ExSTR = c.ExSTR; + ExAGI = c.ExAGI; + ExINT = c.ExINT; + STRGrowth = c.STRGrowth; + AGIGrowth = c.AGIGrowth; + INTGrowth = c.INTGrowth; + InitialSPD = c.InitialSPD; + ExSPD = c.ExSPD; + ExActionCoefficient = c.ExActionCoefficient; + AccelerationCoefficient = c.AccelerationCoefficient; + ExCDR = c.ExCDR; + ATR = c.ATR; + ExCritRate = c.ExCritRate; + ExCritDMG = c.ExCritDMG; + ExEvadeRate = c.ExEvadeRate; + foreach (Skill skill in skills) + { + Skill newskill = skill.Copy(); + newskill.Character = this; + newskill.Level = skill.Level; + Skills.Add(newskill); + } + foreach (Item item in items) + { + Item newitem = item.Copy(true); + newitem.Character = this; + Items.Add(newitem); + } + if (mcp != null) Equip(mcp); + if (w != null) Equip(w); + if (a != null) Equip(a); + if (s != null) Equip(s); + if (ac1 != null) Equip(ac1); + if (ac2 != null) Equip(ac2); + Recovery(0D); + } } } \ No newline at end of file diff --git a/Entity/Character/CharacterProfile.cs b/Entity/Character/CharacterProfile.cs index 30fde0b..4e15be6 100644 --- a/Entity/Character/CharacterProfile.cs +++ b/Entity/Character/CharacterProfile.cs @@ -64,5 +64,24 @@ namespace Milimoe.FunGame.Core.Entity /// 角色的故事 /// public Dictionary Stories { get; set; } = []; + + /// + /// 复制一个角色资料 + /// + /// + public CharacterProfile Copy() + { + return new(Name, FirstName, NickName) + { + Birthplace = Birthplace, + Birthday = Birthday, + Status = Status, + Affiliation = Affiliation, + Sex = Sex, + Height = Height, + Weight = Weight, + Stories = new(Stories) + }; + } } } \ No newline at end of file diff --git a/Entity/Character/EquipSlot.cs b/Entity/Character/EquipSlot.cs index cd93eb8..1823a86 100644 --- a/Entity/Character/EquipSlot.cs +++ b/Entity/Character/EquipSlot.cs @@ -32,6 +32,11 @@ namespace Milimoe.FunGame.Core.Entity /// public Item? Accessory2 { get; internal set; } = null; + /// + /// 上一次装备的饰品槽 + /// + public EquipSlotType LastEquipSlotType { get; internal set; } = EquipSlotType.Accessory1; + /// /// 是否有任意装备 /// @@ -40,38 +45,5 @@ namespace Milimoe.FunGame.Core.Entity { return MagicCardPack != null || Weapon != null || Armor != null || Shoes != null || Accessory1 != null || Accessory2 != null; } - - /// - /// 获取物品所装备的栏位 - /// - /// - public EquipItemToSlot GetEquipItemToSlot(Item item) - { - if (MagicCardPack == item) - { - return EquipItemToSlot.MagicCardPack; - } - else if (Weapon == item) - { - return EquipItemToSlot.Weapon; - } - else if (Armor == item) - { - return EquipItemToSlot.Armor; - } - else if (Shoes == item) - { - return EquipItemToSlot.Shoes; - } - else if (Accessory1 == item) - { - return EquipItemToSlot.Accessory1; - } - else if (Accessory2 == item) - { - return EquipItemToSlot.Accessory2; - } - return EquipItemToSlot.None; - } } } diff --git a/Entity/Character/MagicResistance.cs b/Entity/Character/MagicResistance.cs index f05edb3..d832085 100644 --- a/Entity/Character/MagicResistance.cs +++ b/Entity/Character/MagicResistance.cs @@ -14,5 +14,58 @@ public double Element { get; set; } = 0; public double Fleabane { get; set; } = 0; public double Particle { get; set; } = 0; + + /// + /// 对所有抗性赋值 + /// + /// + /// + public void SetAllValue(double value, bool assignment = true) + { + if (assignment) + { + None = value; + Particle = value; + Fleabane = value; + Element = value; + Shadow = value; + Bright = value; + PurityContemporary = value; + PurityNatural = value; + Starmark = value; + } + else + { + None += value; + Particle += value; + Fleabane += value; + Element += value; + Shadow += value; + Bright += value; + PurityContemporary += value; + PurityNatural += value; + Starmark += value; + } + } + + /// + /// 复制一个魔法抗性对象 + /// + /// + public MagicResistance Copy() + { + return new() + { + None = None, + Starmark = Starmark, + PurityNatural = PurityNatural, + PurityContemporary = PurityContemporary, + Bright = Bright, + Shadow = Shadow, + Element = Element, + Fleabane = Fleabane, + Particle = Particle + }; + } } } diff --git a/Entity/Item/Item.cs b/Entity/Item/Item.cs index c1d67ad..24a8ee3 100644 --- a/Entity/Item/Item.cs +++ b/Entity/Item/Item.cs @@ -1,7 +1,8 @@ -using System.Text; +using System.Net.NetworkInformation; +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Interface.Entity; -using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Entity @@ -42,7 +43,7 @@ namespace Milimoe.FunGame.Core.Entity public bool Unequipable { get; set; } = true; /// - /// 装备槽位 + /// 当前装备的槽位 /// public virtual EquipSlotType EquipSlotType { get; set; } = EquipSlotType.None; @@ -51,6 +52,21 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual WeaponType WeaponType { get; set; } = WeaponType.None; + /// + /// 品质类型 + /// + public virtual QualityType QualityType { get; set; } = QualityType.White; + + /// + /// 稀有度类型 + /// + public virtual RarityType RarityType { get; set; } = RarityType.OneStar; + + /// + /// 物品评级 + /// + public virtual ItemRankType RankType { get; set; } = ItemRankType.D; + /// /// 快捷键 /// @@ -140,9 +156,10 @@ namespace Milimoe.FunGame.Core.Entity /// /// 当装备物品时 /// - public void OnItemEquip(Character character, EquipItemToSlot type) + public void OnItemEquip(Character character, EquipSlotType type) { Character = character; + EquipSlotType = type; foreach (Skill skill in Skills.Passives) { if (!skill.IsActive && skill.Level > 0) @@ -164,13 +181,14 @@ namespace Milimoe.FunGame.Core.Entity /// /// 当取消装备物品时 /// - public void OnItemUnEquip(EquipItemToSlot type) + public void OnItemUnEquip(EquipSlotType type) { if (Character != null) { if (Skills.Active != null) { - foreach (Effect e in Character.Effects.Where(e => e.Skill == Skills.Active && e.Level > 0).ToList()) + List effects = Character.Effects.Where(e => e.Skill == Skills.Active && e.Level > 0).ToList(); + foreach (Effect e in effects) { Character.Effects.Remove(e); e.OnEffectLost(Character); @@ -178,7 +196,8 @@ namespace Milimoe.FunGame.Core.Entity } foreach (Skill skill in Skills.Passives) { - foreach (Effect e in Character.Effects.Where(e => e.Skill == skill && e.Level > 0).ToList()) + List effects = Character.Effects.Where(e => e.Skill == skill && e.Level > 0).ToList(); + foreach (Effect e in effects) { Character.Effects.Remove(e); e.OnEffectLost(Character); @@ -186,28 +205,42 @@ namespace Milimoe.FunGame.Core.Entity } switch (type) { - case EquipItemToSlot.MagicCardPack: + case EquipSlotType.MagicCardPack: Character.EquipSlot.MagicCardPack = null; break; - case EquipItemToSlot.Weapon: + case EquipSlotType.Weapon: Character.EquipSlot.Weapon = null; break; - case EquipItemToSlot.Armor: + case EquipSlotType.Armor: Character.EquipSlot.Armor = null; break; - case EquipItemToSlot.Shoes: + case EquipSlotType.Shoes: Character.EquipSlot.Shoes = null; break; - case EquipItemToSlot.Accessory1: + case EquipSlotType.Accessory1: Character.EquipSlot.Accessory1 = null; break; - case EquipItemToSlot.Accessory2: + case EquipSlotType.Accessory2: Character.EquipSlot.Accessory2 = null; break; } OnItemUnEquipped(Character, this, type); } Character = null; + EquipSlotType = EquipSlotType.None; + } + + /// + /// 设置游戏内的行动顺序表实例 + /// + /// + public void SetGamingQueue(IGamingQueue queue) + { + if (Skills.Active != null) Skills.Active.GamingQueue = queue; + foreach (Skill skill in Skills.Passives) + { + skill.GamingQueue = queue; + } } /// @@ -215,8 +248,19 @@ namespace Milimoe.FunGame.Core.Entity /// public void UseItem(IGamingQueue queue, Character character, List enemys, List teammates) { - OnItemUsed(character, this); - Skills.Active?.OnSkillCasted(queue, character, enemys, teammates); + bool cancel = false; + bool used = false; + if (Skills.Active != null) + { + Skill skill = Skills.Active; + List targets = queue.SelectTargets(character, skill, enemys, teammates, out cancel); + if (!cancel) + { + skill.OnSkillCasted(queue, character, targets); + used = true; + } + } + OnItemUsed(character, this, cancel, used); } /// @@ -232,7 +276,9 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// - public virtual void OnItemUsed(Character character, Item item) + /// + /// + public virtual void OnItemUsed(Character character, Item item, bool cancel, bool used) { } @@ -253,7 +299,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// - public virtual void OnItemEquipped(Character character, Item item, EquipItemToSlot type) + public virtual void OnItemEquipped(Character character, Item item, EquipSlotType type) { } @@ -264,17 +310,16 @@ namespace Milimoe.FunGame.Core.Entity /// /// /// - public virtual void OnItemUnEquipped(Character character, Item item, EquipItemToSlot type) + public virtual void OnItemUnEquipped(Character character, Item item, EquipSlotType type) { } - protected Item(ItemType type, bool isInGame = true, EquipSlotType slot = EquipSlotType.None) + protected Item(ItemType type, bool isInGame = true) { ItemType = type; IsInGameItem = isInGame; - EquipSlotType = slot; } internal Item() { } @@ -307,7 +352,7 @@ namespace Milimoe.FunGame.Core.Entity StringBuilder builder = new(); builder.AppendLine($"【{Name}】"); - builder.AppendLine($"{ItemSet.GetItemTypeName(ItemType)}" + (IsPurchasable && Price > 0 ? $" 售价:{Price}" : "")); + builder.AppendLine($"{ItemSet.GetItemTypeName(ItemType) + (ItemType == ItemType.Weapon && WeaponType != WeaponType.None ? "-" + ItemSet.GetWeaponTypeName(WeaponType) : "")}" + (IsPurchasable && Price > 0 ? $" 售价:{Price}" : "")); if (RemainUseTimes > 0) { @@ -369,61 +414,83 @@ namespace Milimoe.FunGame.Core.Entity } /// - /// 设置一些属性给从 新建来的 - /// 对于还原存档而言,在使用 JSON 反序列化 Item,且从 中获取了实例后,需要使用此方法复制给新实例 + /// 设置一些属性给从工厂构造出来的 对象 /// - /// - public void SetPropertyToItemModuleNew(Item newbyItemModule) + /// + public void SetPropertyToItemModuleNew(Item newbyFactory) { - newbyItemModule.WeaponType = WeaponType; - newbyItemModule.EquipSlotType = EquipSlotType; - newbyItemModule.Equipable = Equipable; - newbyItemModule.IsPurchasable = IsPurchasable; - newbyItemModule.Price = Price; - newbyItemModule.IsSellable = IsSellable; - newbyItemModule.NextSellableTime = NextSellableTime; - newbyItemModule.IsTradable = IsTradable; - newbyItemModule.NextTradableTime = NextTradableTime; + newbyFactory.WeaponType = WeaponType; + newbyFactory.EquipSlotType = EquipSlotType; + newbyFactory.Equipable = Equipable; + newbyFactory.IsPurchasable = IsPurchasable; + newbyFactory.Price = Price; + newbyFactory.IsSellable = IsSellable; + newbyFactory.NextSellableTime = NextSellableTime; + newbyFactory.IsTradable = IsTradable; + newbyFactory.NextTradableTime = NextTradableTime; } /// /// 复制一个物品 /// /// - public Item Copy() + public Item Copy(bool copyLevel = false) { - Item item = new() - { - Id = Id, - Name = Name, - Description = Description, - GeneralDescription = GeneralDescription, - ItemType = ItemType, - Equipable = Equipable, - Unequipable = Unequipable, - EquipSlotType = EquipSlotType, - WeaponType = WeaponType, - Key = Key, - Enable = Enable, - IsInGameItem = IsInGameItem, - IsPurchasable = IsPurchasable, - Price = Price, - IsSellable = IsSellable, - NextSellableTime = NextSellableTime, - IsTradable = IsTradable, - NextTradableTime = NextTradableTime, - RemainUseTimes = RemainUseTimes, - }; + Item item = Factory.OpenFactory.GetInstance(Id, Name, []); + SetPropertyToItemModuleNew(item); + item.Id = Id; + item.Name = Name; + 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; item.Skills.Active = Skills.Active?.Copy(); + if (item.Skills.Active != null && copyLevel) + { + item.Skills.Active.Level = Skills.Active?.Level ?? 0; + } foreach (Skill skill in Skills.Passives) { Skill newskill = skill.Copy(); newskill.Item = item; + newskill.Level = copyLevel ? skill.Level : 0; item.Skills.Passives.Add(newskill); } return item; } + /// + /// 设置所有技能的等级 + /// + /// + public void SetLevel(int level) + { + if (Skills.Active != null) + { + Skills.Active.Level = level; + } + foreach (Skill skill in Skills.Passives) + { + skill.Level = level; + } + } + /// /// 所属的角色 /// diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs index 0757326..dc48c7f 100644 --- a/Entity/Skill/Effect.cs +++ b/Entity/Skill/Effect.cs @@ -90,6 +90,11 @@ namespace Milimoe.FunGame.Core.Entity /// public IGamingQueue? GamingQueue { get; set; } = null; + /// + /// 用于动态扩展特效的参数 + /// + public Dictionary Values { get; } = []; + /// /// 输出文本或日志 /// @@ -102,9 +107,16 @@ namespace Milimoe.FunGame.Core.Entity } } - protected Effect(Skill skill) + protected Effect(Skill skill, Dictionary? args = null) { Skill = skill; + if (args != null) + { + foreach (string key in args.Keys) + { + Values[key] = args[key]; + } + } } internal Effect() @@ -240,10 +252,9 @@ namespace Milimoe.FunGame.Core.Entity /// 吟唱结束后释放技能(魔法)/ 直接释放技能(战技/爆发技) /// /// - /// - /// + /// /// - public virtual void OnSkillCasted(Character caster, List enemys, List teammates, Dictionary others) + public virtual void OnSkillCasted(Character caster, List targets, Dictionary others) { } @@ -323,6 +334,17 @@ namespace Milimoe.FunGame.Core.Entity } + /// + /// 闪避检定前触发 + /// + /// + /// + /// 返回 false 表示不进行闪避检定 + public virtual bool BeforeEvadeCheck(Character character, ref double throwingBonus) + { + return true; + } + /// /// 在触发闪避时 /// @@ -335,6 +357,17 @@ namespace Milimoe.FunGame.Core.Entity return false; } + /// + /// 暴击检定前触发 + /// + /// + /// + /// 返回 false 表示不进行暴击检定 + public virtual bool BeforeCriticalCheck(Character character, ref double throwingBonus) + { + return true; + } + /// /// 在触发暴击时 /// @@ -441,20 +474,24 @@ namespace Milimoe.FunGame.Core.Entity /// public Effect Copy(Skill skill) { - Effect copy = new(skill) + Dictionary args = new() { - EffectType = EffectType, - TargetSelf = TargetSelf, - TargetCount = TargetCount, - TargetRange = TargetRange, - Durative = Durative, - Duration = Duration, - DurationTurn = DurationTurn, - MagicType = MagicType, - Description = Description, - GamingQueue = GamingQueue + { "skill", skill }, + { "values", Values } }; - + Effect copy = Factory.OpenFactory.GetInstance(Id, Name, args); + copy.Id = Id; + 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; + copy.MagicType = MagicType; + copy.GamingQueue = GamingQueue; return copy; } diff --git a/Entity/Skill/OpenSkill.cs b/Entity/Skill/OpenSkill.cs new file mode 100644 index 0000000..83d90c3 --- /dev/null +++ b/Entity/Skill/OpenSkill.cs @@ -0,0 +1,34 @@ +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity +{ + /// + /// 用于动态扩展技能, 返回所有特效的描述 + /// + public class OpenSkill : Skill + { + public override long Id { get; set; } + public override string Name { get; set; } + public override string Description => string.Join("\r\n", Effects); + + public OpenSkill(long id, string name, Dictionary args, Character? character = null) : base(SkillType.Passive, character) + { + Id = id; + Name = name; + foreach (string str in args.Keys) + { + Values[str] = args[str]; + switch (str) + { + default: + break; + } + } + } + + public override IEnumerable AddInactiveEffectToCharacter() + { + return Effects; + } + } +} diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs index e2039a1..86a48d6 100644 --- a/Entity/Skill/Skill.cs +++ b/Entity/Skill/Skill.cs @@ -2,7 +2,6 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Interface.Entity; -using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Entity @@ -27,6 +26,11 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual string GeneralDescription { get; set; } = ""; + /// + /// 释放技能时的口号 + /// + public virtual string Slogan { get; set; } = ""; + /// /// 技能等级,等于 0 时可以称之为尚未学习 /// @@ -78,6 +82,21 @@ namespace Milimoe.FunGame.Core.Entity [InitRequired] public bool IsMagic => SkillType == SkillType.Magic; + /// + /// 可选取自身 + /// + public virtual bool CanSelectSelf { get; set; } = false; + + /// + /// 可选取的作用目标数量 + /// + public virtual int CanSelectTargetCount { get; set; } = 1; + + /// + /// 可选取的作用范围 + /// + public virtual double CanSelectTargetRange { get; set; } = 0; + /// /// 实际魔法消耗 [ 魔法 ] /// @@ -103,7 +122,7 @@ namespace Milimoe.FunGame.Core.Entity /// /// 实际能量消耗 [ 战技 ] /// - public double RealEPCost => 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) * 0.00075)))); /// /// 能量消耗 [ 战技 ] @@ -111,6 +130,16 @@ namespace Milimoe.FunGame.Core.Entity [InitOptional] public virtual double EPCost { get; set; } = 0; + /// + /// 消耗所有能量 [ 战技 ] + /// + public virtual bool CostAllEP { get; set; } = false; + + /// + /// 消耗所有能量的最小能量限制 [ 战技 ] 默认值:100 + /// + public virtual double MinCostEP { get; set; } = 100; + /// /// 实际冷却时间 /// @@ -139,9 +168,9 @@ namespace Milimoe.FunGame.Core.Entity public HashSet Effects { get; } = []; /// - /// 其他参数 + /// 用于动态扩展技能的参数 /// - public Dictionary OtherArgs { get; } = []; + public Dictionary Values { get; } = []; /// /// 游戏中的行动顺序表实例,在技能效果被触发时,此实例会获得赋值,使用时需要判断其是否存在 @@ -170,15 +199,14 @@ namespace Milimoe.FunGame.Core.Entity internal Skill() { } /// - /// 设置一些属性给从 新建来的 - /// 对于还原存档而言,在使用 JSON 反序列化 Skill,且从 中获取了实例后,需要使用此方法复制给新实例 + /// 设置一些属性给从工厂构造出来的 对象 /// - /// - public void SetPropertyToItemModuleNew(Skill newbySkillModule) + /// + public void SetPropertyToItemModuleNew(Skill newbyFactory) { - newbySkillModule.Enable = Enable; - newbySkillModule.IsInEffect = IsInEffect; - newbySkillModule.CurrentCD = CurrentCD; + newbyFactory.Enable = Enable; + newbyFactory.IsInEffect = IsInEffect; + newbyFactory.CurrentCD = CurrentCD; } /// @@ -210,9 +238,37 @@ namespace Milimoe.FunGame.Core.Entity OnLevelUp(); } + /// + /// 选取技能目标 + /// + /// + /// + /// + /// + public virtual List SelectTargets(Character caster, List enemys, List teammates) + { + if (CanSelectSelf) + { + return [caster]; + } + else + { + List targets = []; + + if (enemys.Count <= CanSelectTargetCount) + { + return [.. enemys]; + } + + return enemys.OrderBy(x => Random.Shared.Next()).Take(CanSelectTargetCount).ToList(); + } + } + /// /// 技能开始吟唱时 [ 吟唱魔法、释放战技和爆发技、预释放爆发技均可触发 ] /// + /// + /// public void OnSkillCasting(IGamingQueue queue, Character caster) { GamingQueue = queue; @@ -226,13 +282,16 @@ namespace Milimoe.FunGame.Core.Entity /// /// 触发技能效果 /// - public void OnSkillCasted(IGamingQueue queue, Character caster, List enemys, List teammates) + /// + /// + /// + public void OnSkillCasted(IGamingQueue queue, Character caster, List targets) { GamingQueue = queue; foreach (Effect e in Effects) { e.GamingQueue = GamingQueue; - e.OnSkillCasted(caster, enemys, teammates, OtherArgs); + e.OnSkillCasted(caster, targets, Values); } } @@ -333,27 +392,33 @@ namespace Milimoe.FunGame.Core.Entity /// public Skill Copy() { - Skill s = new() + Dictionary args = new() { - Id = Id, - Name = Name, - Description = Description, - GeneralDescription = GeneralDescription, - SkillType = SkillType, - MPCost = MPCost, - CastTime = CastTime, - EPCost = EPCost, - CD = CD, - CurrentCD = CurrentCD, - HardnessTime = HardnessTime, - GamingQueue = GamingQueue + { "values", Values } }; - foreach (Effect e in Effects) + 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; + if (skill is OpenSkill) { - Effect neweffect = e.Copy(s); - s.Effects.Add(neweffect); + foreach (Effect e in Effects) + { + Effect neweffect = e.Copy(skill); + skill.Effects.Add(neweffect); + } } - return s; + return skill; } /// diff --git a/Entity/Skill/SkillTarget.cs b/Entity/Skill/SkillTarget.cs new file mode 100644 index 0000000..8e2971c --- /dev/null +++ b/Entity/Skill/SkillTarget.cs @@ -0,0 +1,8 @@ +namespace Milimoe.FunGame.Core.Entity +{ + public struct SkillTarget(Skill skill, List targets) + { + public Skill Skill { get; set; } = skill; + public List Targets { get; set; } = targets; + } +} diff --git a/Entity/Statistics/CharacterStatistics.cs b/Entity/Statistics/CharacterStatistics.cs index 4c677fa..eb0a110 100644 --- a/Entity/Statistics/CharacterStatistics.cs +++ b/Entity/Statistics/CharacterStatistics.cs @@ -32,6 +32,8 @@ public int Kills { get; set; } = 0; public int Deaths { get; set; } = 0; public int Assists { get; set; } = 0; + public int FirstKills { get; set; } = 0; + public int FirstDeaths { get; set; } = 0; public int Plays { get; set; } = 0; public int Wins { get; set; } = 0; public int Top3s { get; set; } = 0; @@ -40,5 +42,6 @@ public double Top3rates { get; set; } = 0; public int LastRank { get; set; } = 0; public double AvgRank { get; set; } = 0; + public double Rating { get; set; } = 0; } } diff --git a/Entity/System/RoundRecord.cs b/Entity/System/RoundRecord.cs new file mode 100644 index 0000000..6843c71 --- /dev/null +++ b/Entity/System/RoundRecord.cs @@ -0,0 +1,14 @@ +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Entity.System +{ + public class RoundRecord(int round, Character actor) + { + public int Round { get; set; } = round; + public Character Actor { get; set; } = actor; + public CharacterActionType ActionType { get; set; } = CharacterActionType.None; + public List Targets { get; set; } = []; + public Skill? Skill { get; set; } = null; + public Item? Item { get; set; } = null; + } +} diff --git a/Entity/System/Team.cs b/Entity/System/Team.cs new file mode 100644 index 0000000..0fb82a0 --- /dev/null +++ b/Entity/System/Team.cs @@ -0,0 +1,39 @@ +using Milimoe.FunGame.Core.Interface.Base; + +namespace Milimoe.FunGame.Core.Entity +{ + public class Team(string name, IEnumerable charaters) + { + public Guid Id { get; set; } = Guid.Empty; + public string Name { get; set; } = name; + public List Members { get; } = new(charaters); + public int Score { get; set; } = 0; + public bool IsWinner { get; set; } = false; + public int Count => Members.Count; + + public List GetActiveCharacters(IGamingQueue queue) + { + return [.. Members.Where(queue.Queue.Contains)]; + } + + public List GetTeammates(Character character) + { + return [.. Members.Where(c => c != character)]; + } + + public List GetActiveTeammates(IGamingQueue queue, Character character) + { + return [.. Members.Where(c => queue.Queue.Contains(c) && c != character)]; + } + + public bool IsOnThisTeam(Character character) + { + return Members.Contains(character); + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/Interface/Base/IGamingQueue.cs b/Interface/Base/IGamingQueue.cs index 8bb4756..c7c9e5d 100644 --- a/Interface/Base/IGamingQueue.cs +++ b/Interface/Base/IGamingQueue.cs @@ -99,5 +99,27 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// public void InterruptCasting(Character caster, Character interrupter); + + /// + /// 选取技能目标 + /// + /// + /// + /// + /// + /// + /// + public List SelectTargets(Character caster, Skill skill, List enemys, List teammates, out bool cancel); + + /// + /// 选取普通攻击目标 + /// + /// + /// + /// + /// + /// + /// + public List SelectTargets(Character character, NormalAttack attack, List enemys, List teammates, out bool cancel); } } diff --git a/Library/Common/Addon/CharacterModule.cs b/Library/Common/Addon/CharacterModule.cs index b51523f..8273b3e 100644 --- a/Library/Common/Addon/CharacterModule.cs +++ b/Library/Common/Addon/CharacterModule.cs @@ -1,4 +1,5 @@ -using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Addons; namespace Milimoe.FunGame.Core.Library.Common.Addon @@ -28,7 +29,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// /// 此模组中包含的角色 /// - public abstract List Characters { get; } + public abstract Dictionary Characters { get; } /// /// 加载标记 @@ -49,12 +50,28 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon { // 模组加载后,不允许再次加载此模组 IsLoaded = true; + // 注册工厂 + Factory.OpenFactory.RegisterFactory(EntityFactory()); // 如果加载后需要执行代码,请重写AfterLoad方法 AfterLoad(); } return IsLoaded; } + /// + /// 注册工厂 + /// + protected virtual Factory.EntityFactoryDelegate EntityFactory() + { + return (id, name, args) => + { + Character c = Factory.GetCharacter(); + c.Id = id; + c.Name = name; + return c; + }; + } + /// /// 模组加载后需要做的事 /// diff --git a/Library/Common/Addon/Example/ExampleGameModule.cs b/Library/Common/Addon/Example/ExampleGameModule.cs index 7b78f45..7530d31 100644 --- a/Library/Common/Addon/Example/ExampleGameModule.cs +++ b/Library/Common/Addon/Example/ExampleGameModule.cs @@ -251,11 +251,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example public override string Author => "FunGamer"; - public override List Characters + public override Dictionary Characters { get { - List list = []; + Dictionary dict = []; // 构建一个你想要的角色 Character c = Factory.GetCharacter(); c.Name = "Oshima"; @@ -268,8 +268,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example c.InitialINT = 5; c.InitialATK = 100; c.InitialDEF = 10; - list.Add(c); - return list; + dict.Add(c.Name, c); + return dict; } } } @@ -287,20 +287,31 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example public override string Author => "FunGamer"; - public override List Skills + public override Dictionary Skills { get { - List list = []; + Dictionary dict = []; // 技能应该在GameModule中新建类继承Skill实现,再自行构造。 - return list; + return dict; } } - public override Skill? GetSkill(long id, string name, SkillType type) + protected override Factory.EntityFactoryDelegate SkillFactory() { - // 此方法将根据id和name,返回一个你继承实现了的类对象。 - return Factory.GetSkill(); + // 注册一个工厂,根据id和name,返回一个你继承实现了的类对象。 + return (id, name, args) => + { + return null; + }; + } + + protected override Factory.EntityFactoryDelegate EffectFactory() + { + return (id, name, args) => + { + return null; + }; } } @@ -317,20 +328,23 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example public override string Author => "FunGamer"; - public override List Items + public override Dictionary Items { get { - List list = []; + Dictionary dict = []; // 物品应该在GameModule中新建类继承Item实现,再自行构造。 - return list; + return dict; } } - public override Item? GetItem(long id, string name, ItemType type) + protected override Factory.EntityFactoryDelegate ItemFactory() { - // 此方法将根据id和name,返回一个你继承实现了的类对象。 - return Factory.GetItem(); + // 注册一个工厂,根据id和name,返回一个你继承实现了的类对象。 + return (id, name, args) => + { + return null; + }; } } } diff --git a/Library/Common/Addon/GameModuleDepend.cs b/Library/Common/Addon/GameModuleDepend.cs index 7898824..b0a7802 100644 --- a/Library/Common/Addon/GameModuleDepend.cs +++ b/Library/Common/Addon/GameModuleDepend.cs @@ -42,19 +42,19 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// 实际使用的角色组对象 /// 请使用 自动填充,不要自己添加 /// - public List Characters { get; } = []; + public Dictionary Characters { get; } = []; /// /// 实际使用的技能组对象 /// 请使用 自动填充,不要自己添加 /// - public List Skills { get; } = []; + public Dictionary Skills { get; } = []; /// /// 实际使用的物品组对象 /// 请使用 自动填充,不要自己添加 /// - public List Items { get; } = []; + public Dictionary Items { get; } = []; /// /// 获得所有的依赖项 @@ -69,15 +69,24 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon Maps.AddRange(loader.Maps.Keys.Where(MapsDepend.Contains).Select(str => loader.Maps[str])); foreach (CharacterModule modules in loader.Characters.Keys.Where(CharactersDepend.Contains).Select(str => loader.Characters[str])) { - Characters.AddRange(modules.Characters); + foreach (string key in modules.Characters.Keys) + { + Characters[key] = modules.Characters[key]; + } } foreach (SkillModule modules in loader.Skills.Keys.Where(SkillsDepend.Contains).Select(str => loader.Skills[str])) { - Skills.AddRange(modules.Skills); + foreach (string key in modules.Skills.Keys) + { + Skills[key] = modules.Skills[key]; + } } foreach (ItemModule modules in loader.Items.Keys.Where(ItemsDepend.Contains).Select(str => loader.Items[str])) { - Items.AddRange(modules.Items); + foreach (string key in modules.Items.Keys) + { + Items[key] = modules.Items[key]; + } } } } diff --git a/Library/Common/Addon/ItemModule.cs b/Library/Common/Addon/ItemModule.cs index 464ee47..28a864f 100644 --- a/Library/Common/Addon/ItemModule.cs +++ b/Library/Common/Addon/ItemModule.cs @@ -1,7 +1,6 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Addons; -using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Library.Common.Addon { @@ -30,25 +29,13 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// /// 此模组中包含的物品 /// - public abstract List Items { get; } + public abstract Dictionary Items { get; } /// /// 加载标记 /// private bool IsLoaded = false; - /// - /// [可选实现] 在使用 后,可以按 id + name + type 来匹配已编码的物品 - /// - /// - /// - /// - /// - public virtual Item? GetItem(long id, string name, ItemType type) - { - return null; - } - /// /// 加载模组 /// @@ -63,12 +50,19 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon { // 模组加载后,不允许再次加载此模组 IsLoaded = true; + // 注册工厂 + Factory.OpenFactory.RegisterFactory(ItemFactory()); // 如果加载后需要执行代码,请重写AfterLoad方法 AfterLoad(); } return IsLoaded; } + /// + /// 注册工厂 + /// + protected abstract Factory.EntityFactoryDelegate ItemFactory(); + /// /// 模组加载后需要做的事 /// diff --git a/Library/Common/Addon/SkillModule.cs b/Library/Common/Addon/SkillModule.cs index b1f01fa..eb71906 100644 --- a/Library/Common/Addon/SkillModule.cs +++ b/Library/Common/Addon/SkillModule.cs @@ -1,7 +1,6 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Addons; -using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Library.Common.Addon { @@ -30,25 +29,13 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// /// 此模组中包含的技能 /// - public abstract List Skills { get; } + public abstract Dictionary Skills { get; } /// /// 加载标记 /// private bool IsLoaded = false; - /// - /// [可选实现] 在使用 后,可以按 id + name + type 来匹配已编码的技能 - /// - /// - /// - /// - /// - public virtual Skill? GetSkill(long id, string name, SkillType type) - { - return null; - } - /// /// 加载模组 /// @@ -63,12 +50,25 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon { // 模组加载后,不允许再次加载此模组 IsLoaded = true; + // 注册工厂 + Factory.OpenFactory.RegisterFactory(SkillFactory()); + Factory.OpenFactory.RegisterFactory(EffectFactory()); // 如果加载后需要执行代码,请重写AfterLoad方法 AfterLoad(); } return IsLoaded; } + /// + /// 注册工厂 + /// + protected abstract Factory.EntityFactoryDelegate SkillFactory(); + + /// + /// 注册工厂(特效类) + /// + protected abstract Factory.EntityFactoryDelegate EffectFactory(); + /// /// 模组加载后需要做的事 /// diff --git a/Library/Common/JsonConverter/EffectConverter.cs b/Library/Common/JsonConverter/EffectConverter.cs index 9768d15..b1a25d4 100644 --- a/Library/Common/JsonConverter/EffectConverter.cs +++ b/Library/Common/JsonConverter/EffectConverter.cs @@ -21,6 +21,20 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Effect.Name): result.Name = reader.GetString() ?? ""; break; + default: + if (reader.TokenType == JsonTokenType.Number) + { + result.Values[propertyName] = reader.GetDouble(); + } + else if (reader.TokenType == JsonTokenType.String) + { + result.Values[propertyName] = reader.GetString() ?? ""; + } + else if (reader.TokenType == JsonTokenType.True || reader.TokenType == JsonTokenType.False) + { + result.Values[propertyName] = reader.GetBoolean(); + } + break; } } @@ -31,6 +45,28 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteNumber(nameof(Effect.Id), (int)value.Id); writer.WriteString(nameof(Effect.Name), value.Name); + foreach (var kvp in value.Values) + { + switch (kvp.Value) + { + case int intValue: + writer.WriteNumber(kvp.Key, intValue); + break; + case double doubleValue: + writer.WriteNumber(kvp.Key, doubleValue); + break; + case bool boolValue: + writer.WriteBoolean(kvp.Key, boolValue); + break; + case string strValue: + writer.WriteString(kvp.Key, strValue); + break; + default: + JsonSerializer.Serialize(writer, kvp.Value, options); + break; + } + } + writer.WriteEndObject(); } } diff --git a/Library/Common/JsonConverter/EquipSlotConverter.cs b/Library/Common/JsonConverter/EquipSlotConverter.cs index b567c09..fe011cb 100644 --- a/Library/Common/JsonConverter/EquipSlotConverter.cs +++ b/Library/Common/JsonConverter/EquipSlotConverter.cs @@ -48,14 +48,14 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter break; case nameof(EquipSlot.Accessory1): temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); - if (temp.EquipSlotType == EquipSlotType.Accessory) + if (temp.EquipSlotType == EquipSlotType.Accessory1) { result.Accessory1 = temp; } break; case nameof(EquipSlot.Accessory2): temp = NetworkUtility.JsonDeserialize(ref reader, options) ?? new(); - if (temp.EquipSlotType == EquipSlotType.Accessory) + if (temp.EquipSlotType == EquipSlotType.Accessory2) { result.Accessory2 = temp; } diff --git a/Library/Common/JsonConverter/ItemConverter.cs b/Library/Common/JsonConverter/ItemConverter.cs index b184e8b..eec8e43 100644 --- a/Library/Common/JsonConverter/ItemConverter.cs +++ b/Library/Common/JsonConverter/ItemConverter.cs @@ -41,6 +41,15 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Item.EquipSlotType): result.EquipSlotType = (EquipSlotType)reader.GetInt32(); break; + case nameof(Item.QualityType): + result.QualityType = (QualityType)reader.GetInt32(); + break; + case nameof(Item.RarityType): + result.RarityType = (RarityType)reader.GetInt32(); + break; + case nameof(Item.RankType): + result.RankType = (ItemRankType)reader.GetInt32(); + break; case nameof(Item.IsInGameItem): result.IsInGameItem = reader.GetBoolean(); break; @@ -101,6 +110,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteNumber(nameof(Item.ItemType), (int)value.ItemType); writer.WriteNumber(nameof(Item.WeaponType), (int)value.WeaponType); writer.WriteNumber(nameof(Item.EquipSlotType), (int)value.EquipSlotType); + writer.WriteNumber(nameof(Item.QualityType), (int)value.QualityType); + writer.WriteNumber(nameof(Item.RarityType), (int)value.RarityType); + writer.WriteNumber(nameof(Item.RankType), (int)value.RankType); writer.WriteBoolean(nameof(Item.IsInGameItem), value.IsInGameItem); writer.WriteBoolean(nameof(Item.Equipable), value.Equipable); writer.WriteBoolean(nameof(Item.IsPurchasable), value.IsPurchasable); diff --git a/Library/Common/JsonConverter/SkillConverter.cs b/Library/Common/JsonConverter/SkillConverter.cs index 67a06c1..f7dcc44 100644 --- a/Library/Common/JsonConverter/SkillConverter.cs +++ b/Library/Common/JsonConverter/SkillConverter.cs @@ -10,7 +10,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter { public override Skill NewInstance() { - return new(); + return new OpenSkill(0, "", []); } public override void ReadPropertyName(ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref Skill result) @@ -29,12 +29,24 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter case nameof(Skill.GeneralDescription): result.GeneralDescription = reader.GetString() ?? ""; break; + case nameof(Skill.Slogan): + result.Slogan = reader.GetString() ?? ""; + break; case nameof(Skill.Level): result.Level = reader.GetInt32(); break; case nameof(Skill.SkillType): result.SkillType = (SkillType)reader.GetInt32(); break; + case nameof(Skill.CanSelectSelf): + result.CanSelectSelf = reader.GetBoolean(); + break; + case nameof(Skill.CanSelectTargetCount): + result.CanSelectTargetCount = reader.GetInt32(); + break; + case nameof(Skill.CanSelectTargetRange): + result.CanSelectTargetRange = reader.GetInt32(); + break; case nameof(Skill.Enable): result.Enable = reader.GetBoolean(); break; @@ -66,11 +78,11 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter result.Effects.Add(effect); } break; - case nameof(Skill.OtherArgs): - Dictionary others = NetworkUtility.JsonDeserialize>(ref reader, options) ?? []; - foreach (string key in others.Keys) + case nameof(Skill.Values): + Dictionary values = NetworkUtility.JsonDeserialize>(ref reader, options) ?? []; + foreach (string key in values.Keys) { - result.OtherArgs.Add(key, others[key]); + result.Values.Add(key, values[key]); } break; } @@ -84,8 +96,12 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter writer.WriteString(nameof(Skill.Name), value.Name); writer.WriteString(nameof(Skill.Description), value.Description); 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.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.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); @@ -96,8 +112,8 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter if (value.HardnessTime > 0) writer.WriteNumber(nameof(Skill.HardnessTime), value.HardnessTime); writer.WritePropertyName(nameof(Skill.Effects)); JsonSerializer.Serialize(writer, value.Effects, options); - writer.WritePropertyName(nameof(Skill.OtherArgs)); - JsonSerializer.Serialize(writer, value.OtherArgs, options); + writer.WritePropertyName(nameof(Skill.Values)); + JsonSerializer.Serialize(writer, value.Values, options); writer.WriteEndObject(); } diff --git a/Library/Constant/ConstantSet.cs b/Library/Constant/ConstantSet.cs index 8e1091a..9cb6f9b 100644 --- a/Library/Constant/ConstantSet.cs +++ b/Library/Constant/ConstantSet.cs @@ -325,6 +325,22 @@ namespace Milimoe.FunGame.Core.Library.Constant }; } + public static string GetMagicResistanceName(MagicType type) + { + return type switch + { + MagicType.Starmark => "星痕抗性", + MagicType.PurityNatural => "现代结晶抗性", + MagicType.PurityContemporary => "纯粹结晶抗性", + MagicType.Bright => "光抗性", + MagicType.Shadow => "影抗性", + MagicType.Element => "元素抗性", + MagicType.Fleabane => "紫宛抗性", + MagicType.Particle => "时空抗性", + _ => "魔法抗性", + }; + } + public static string GetContinuousKilling(int kills) { if (kills > 10) return "超越神的杀戮"; @@ -447,7 +463,8 @@ namespace Milimoe.FunGame.Core.Library.Constant EquipSlotType.Weapon => "武器", EquipSlotType.Armor => "防具", EquipSlotType.Shoes => "鞋子", - EquipSlotType.Accessory => "饰品", + EquipSlotType.Accessory1 => "饰品1", + EquipSlotType.Accessory2 => "饰品2", _ => "" }; } @@ -460,38 +477,11 @@ namespace Milimoe.FunGame.Core.Library.Constant "武器" => EquipSlotType.Weapon, "防具" => EquipSlotType.Armor, "鞋子" => EquipSlotType.Shoes, - "饰品" => EquipSlotType.Accessory, + "饰品1" => EquipSlotType.Accessory1, + "饰品2" => EquipSlotType.Accessory2, _ => EquipSlotType.None }; } - - public static string GetEquipItemToSlotTypeName(EquipItemToSlot type) - { - return type switch - { - EquipItemToSlot.MagicCardPack => "魔法卡包", - EquipItemToSlot.Weapon => "武器", - EquipItemToSlot.Armor => "防具", - EquipItemToSlot.Shoes => "鞋子", - EquipItemToSlot.Accessory1 => "饰品1", - EquipItemToSlot.Accessory2 => "饰品2", - _ => "" - }; - } - - public static EquipItemToSlot GetEquipItemToSlotTypeFromName(string name) - { - return name switch - { - "魔法卡包" => EquipItemToSlot.MagicCardPack, - "武器" => EquipItemToSlot.Weapon, - "防具" => EquipItemToSlot.Armor, - "鞋子" => EquipItemToSlot.Shoes, - "饰品1" => EquipItemToSlot.Accessory1, - "饰品2" => EquipItemToSlot.Accessory2, - _ => EquipItemToSlot.None - }; - } } public class SkillSet diff --git a/Library/Constant/General.cs b/Library/Constant/General.cs index 70c82d2..b573d3c 100644 --- a/Library/Constant/General.cs +++ b/Library/Constant/General.cs @@ -1,4 +1,5 @@ using System.Text; +using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Model; @@ -11,6 +12,11 @@ namespace Milimoe.FunGame.Core.Library.Constant { #region Static Variable + /// + /// 支持动态扩展的工厂实例 + /// + public static Factory OpenFactory { get; } = Factory.OpenFactory; + /// /// 空的实体类 用于object返回 /// diff --git a/Library/Constant/TypeEnum.cs b/Library/Constant/TypeEnum.cs index a158c20..7cd1291 100644 --- a/Library/Constant/TypeEnum.cs +++ b/Library/Constant/TypeEnum.cs @@ -479,22 +479,9 @@ namespace Milimoe.FunGame.Core.Library.Constant } /// - /// 区别于 ,这个是定义物品所属的栏位 + /// 指示物品在哪个栏位上 /// public enum EquipSlotType - { - None, - MagicCardPack, - Weapon, - Armor, - Shoes, - Accessory - } - - /// - /// 区别于 ,这个是指示物品具体在哪个栏位上 - /// - public enum EquipItemToSlot { None, MagicCardPack, @@ -759,7 +746,7 @@ namespace Milimoe.FunGame.Core.Library.Constant D } - public enum ItemQualityType + public enum QualityType { White, Green, @@ -769,7 +756,7 @@ namespace Milimoe.FunGame.Core.Library.Constant Red } - public enum ItemRarityType + public enum RarityType { OneStar, TwoStar, diff --git a/Library/Exception/Exception.cs b/Library/Exception/Exception.cs index ab1ac06..d2e9518 100644 --- a/Library/Exception/Exception.cs +++ b/Library/Exception/Exception.cs @@ -179,4 +179,9 @@ { public override string Message => "必须以异步方式读取数据 (#10036)"; } + + public class NotSupportedInstanceClassException : Exception + { + public override string Message => "试图构造一个不支持的类的实例 (#10037)"; + } } diff --git a/Model/ActionQueue.cs b/Model/ActionQueue.cs index 583ae01..2e26e7b 100644 --- a/Model/ActionQueue.cs +++ b/Model/ActionQueue.cs @@ -1,5 +1,6 @@ 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; @@ -25,11 +26,21 @@ namespace Milimoe.FunGame.Core.Model /// public List Eliminated => _eliminated; + /// + /// 当前团灭的团队顺序(第一个是最早死的) + /// + public List EliminatedTeams => _eliminatedTeams; + /// /// 角色数据 /// public Dictionary CharacterStatistics => _stats; + /// + /// 团队及其成员 + /// + public Dictionary Teams => _teams; + /// /// 游戏运行的时间 /// @@ -41,6 +52,38 @@ namespace Milimoe.FunGame.Core.Model /// public int TotalRound { get; set; } = 0; + /// + /// 第一滴血获得者 + /// + public Character? FirstKiller { get; set; } = null; + + /// + /// 最大复活次数 + /// 0:不复活 / -1:无限复活 + /// + public int MaxRespawnTimes { get; set; } = 0; + + /// + /// 复活次数统计 + /// + public Dictionary RespawnTimes => _respawnTimes; + + /// + /// 复活倒计时 + /// + public Dictionary RespawnCountdown => _respawnCountdown; + + /// + /// 最大获胜积分 [ 适用于团队模式 ] + /// 设置一个大于0的数以启用 + /// + public int MaxScoreToWin { get; set; } = 0; + + /// + /// 上回合记录 + /// + public RoundRecord LastRound { get; set; } = new(0, Factory.GetCharacter()); + /// /// 当前的行动顺序 /// @@ -51,6 +94,11 @@ namespace Milimoe.FunGame.Core.Model /// protected readonly List _eliminated = []; + /// + /// 当前团灭的团队顺序(第一个是最早死的) + /// + protected readonly List _eliminatedTeams = []; + /// /// 硬直时间表 /// @@ -59,7 +107,7 @@ namespace Milimoe.FunGame.Core.Model /// /// 角色正在吟唱的魔法 /// - protected readonly Dictionary _castingSkills = []; + protected readonly Dictionary _castingSkills = []; /// /// 角色预释放的爆发技 @@ -91,6 +139,26 @@ namespace Milimoe.FunGame.Core.Model /// protected readonly Dictionary _stats = []; + /// + /// 团队及其成员 + /// + protected readonly Dictionary _teams = []; + + /// + /// 复活次数统计 + /// + protected readonly Dictionary _respawnTimes = []; + + /// + /// 复活倒计时 + /// + protected readonly Dictionary _respawnCountdown = []; + + /// + /// 是否是团队模式 + /// + protected bool _isTeamMode = false; + /// /// 游戏是否结束 /// @@ -99,16 +167,41 @@ namespace Milimoe.FunGame.Core.Model /// /// 新建一个行动顺序表 /// - /// 参与本次游戏的角色列表 + /// 是否是团队模式 /// 用于文本输出 - public ActionQueue(List characters, Action? writer = null) + public ActionQueue(bool isTeamMdoe = false, Action? writer = null) { + _isTeamMode = isTeamMdoe; if (writer != null) { WriteLine = writer; } WriteLine ??= new Action(Console.WriteLine); + } + /// + /// 新建一个行动顺序表并初始化 + /// + /// 参与本次游戏的角色列表 + /// 是否是团队模式 + /// 用于文本输出 + public ActionQueue(List characters, bool isTeamMdoe = false, Action? writer = null) + { + _isTeamMode = isTeamMdoe; + if (writer != null) + { + WriteLine = writer; + } + WriteLine ??= new Action(Console.WriteLine); + InitCharacterQueue(characters); + } + + /// + /// 初始化行动顺序表 + /// + /// + public void InitCharacterQueue(List characters) + { // 初始排序:按速度排序 List> groupedBySpeed = [.. characters .GroupBy(c => c.SPD) @@ -192,6 +285,85 @@ namespace Milimoe.FunGame.Core.Model } } + /// + /// 清空行动队列 + /// + public void ClearQueue() + { + FirstKiller = null; + _queue.Clear(); + _hardnessTimes.Clear(); + _assistDamage.Clear(); + _stats.Clear(); + _cutCount.Clear(); + _castingSkills.Clear(); + _castingSuperSkills.Clear(); + _continuousKilling.Clear(); + _earnedMoney.Clear(); + _eliminated.Clear(); + } + + /// + /// 添加一个团队 + /// + /// + /// + public void AddTeam(string teamName, IEnumerable characters) + { + if (teamName != "" && characters.Any()) + { + _teams.Add(teamName, new(teamName, characters)); + } + } + + /// + /// 获取角色的团队 + /// + /// + public Team? GetTeam(Character character) + { + foreach (Team team in _teams.Values) + { + if (team.IsOnThisTeam(character)) + { + return team; + } + } + return null; + } + + /// + /// 从已淘汰的团队中获取角色的团队 + /// + /// + public Team? GetTeamFromEliminated(Character character) + { + foreach (Team team in _eliminatedTeams) + { + if (team.IsOnThisTeam(character)) + { + return team; + } + } + return null; + } + + /// + /// 获取某角色的团队成员 + /// + /// + public List GetTeammates(Character character) + { + foreach (string team in _teams.Keys) + { + if (_teams[team].IsOnThisTeam(character)) + { + return _teams[team].GetTeammates(character); + } + } + return []; + } + /// /// 将角色加入行动顺序表 /// @@ -320,13 +492,15 @@ namespace Milimoe.FunGame.Core.Model public bool ProcessTurn(Character character) { TotalRound++; + LastRound = new(TotalRound, character); if (!BeforeTurn(character)) { return _isGameEnd; } - foreach (Effect effect in character.Effects.Where(e => e.Level > 0).ToList()) + List effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { effect.OnTurnStart(character); } @@ -335,11 +509,11 @@ namespace Milimoe.FunGame.Core.Model double baseTime = 10; bool isCheckProtected = true; - // 敌人列表 - List enemys = [.. _queue.Where(c => c != character && !c.IsUnselectable)]; - // 队友列表 - List teammates = []; + List teammates = [.. GetTeammates(character).Where(_queue.Contains)]; + + // 敌人列表 + List enemys = [.. _queue.Where(c => c != character && !c.IsUnselectable && !teammates.Contains(c))]; // 技能列表 List skills = [.. character.Skills.Where(s => s.Level > 0 && s.SkillType != SkillType.Passive && s.Enable && !s.IsInEffect && s.CurrentCD == 0 && @@ -349,249 +523,290 @@ namespace Milimoe.FunGame.Core.Model List items = [.. character.Items.Where(i => i.IsActive && i.Skills.Active != null && i.Enable && i.IsInGameItem && i.Skills.Active.SkillType == SkillType.Item && i.Skills.Active.Enable && !i.Skills.Active.IsInEffect && i.Skills.Active.CurrentCD == 0 && i.Skills.Active.RealMPCost <= character.MP && i.Skills.Active.RealEPCost <= character.EP)]; + // 此变量用于在取消选择时,能够重新行动 + bool decided = false; + // 最大取消次数 + int cancelTimes = 3; + // 作出了什么行动 CharacterActionType type = CharacterActionType.None; - // 是否能使用物品和释放技能 - bool canUseItem = items.Count > 0; - bool canCastSkill = skills.Count > 0; - - // 使用物品和释放技能、使用普通攻击的概率 - double pUseItem = 0.33; - double pCastSkill = 0.33; - double pNormalAttack = 0.34; - - // 不允许在吟唱和预释放状态下,修改角色的行动 - if (character.CharacterState != CharacterState.Casting && character.CharacterState != CharacterState.PreCastSuperSkill) + while (!decided && cancelTimes > 0) { - CharacterActionType actionTypeTemp = CharacterActionType.None; - foreach (Effect e in character.Effects.Where(e => e.Level > 0).ToList()) - { - actionTypeTemp = e.AlterActionTypeBeforeAction(character, character.CharacterState, ref canUseItem, ref canCastSkill, ref pUseItem, ref pCastSkill, ref pNormalAttack); - } - if (actionTypeTemp != CharacterActionType.None && actionTypeTemp != CharacterActionType.CastSkill && actionTypeTemp != CharacterActionType.CastSuperSkill) - { - type = actionTypeTemp; - } - } + type = CharacterActionType.None; - if (type == CharacterActionType.None) - { - if (character.CharacterState != CharacterState.NotActionable && character.CharacterState != CharacterState.Casting && character.CharacterState != CharacterState.PreCastSuperSkill) - { - if (character.CharacterState == CharacterState.Actionable) - { - // 可以任意行动 - if (canUseItem && canCastSkill) - { - // 不做任何处理 - } - else if (canUseItem && !canCastSkill) - { - pCastSkill = 0; - } - else if (!canUseItem && canCastSkill) - { - pUseItem = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - } - } - else if (character.CharacterState == CharacterState.ActionRestricted) - { - // 行动受限,只能使用特殊物品 - if (canUseItem) - { - pCastSkill = 0; - pNormalAttack = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - pNormalAttack = 0; - } - } - else if (character.CharacterState == CharacterState.BattleRestricted) - { - // 战斗不能,只能使用物品 - if (canUseItem) - { - pCastSkill = 0; - pNormalAttack = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - pNormalAttack = 0; - } - } - else if (character.CharacterState == CharacterState.SkillRestricted) - { - // 技能受限,无法使用技能,可以使用物品 - if (canUseItem) - { - pCastSkill = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - } - } - type = GetActionType(pUseItem, pCastSkill, pNormalAttack); - _stats[character].ActionTurn += 1; - } - else if (character.CharacterState == CharacterState.Casting) - { - // 如果角色上一次吟唱了魔法,这次的行动则是结算这个魔法 - type = CharacterActionType.CastSkill; - } - else if (character.CharacterState == CharacterState.PreCastSuperSkill) - { - // 角色使用回合外爆发技插队 - type = CharacterActionType.CastSuperSkill; - } - else - { - WriteLine("[ " + character + $" ] 完全行动不能!"); - type = CharacterActionType.None; - } - } + // 是否能使用物品和释放技能 + bool canUseItem = items.Count > 0; + bool canCastSkill = skills.Count > 0; - List enemysTemp = new(enemys); - List teammatesTemp = new(teammates); - List skillsTemp = new(skills); - Dictionary continuousKillingTemp = new(_continuousKilling); - Dictionary earnedMoneyTemp = new(_earnedMoney); - foreach (Effect e in character.Effects.Where(e => e.Level > 0).ToList()) - { - if (e.AlterEnemyListBeforeAction(character, enemysTemp, teammatesTemp, skillsTemp, continuousKillingTemp, earnedMoneyTemp)) - { - enemys = enemysTemp.Distinct().ToList(); - teammates = teammatesTemp.Distinct().ToList(); - skills = skillsTemp.Distinct().ToList(); - } - } + // 使用物品和释放技能、使用普通攻击的概率 + double pUseItem = 0.33; + double pCastSkill = 0.33; + double pNormalAttack = 0.34; - if (type == CharacterActionType.NormalAttack) - { - // 使用普通攻击逻辑 - // 获取随机敌人 - if (enemys.Count > 0) + cancelTimes--; + // 不允许在吟唱和预释放状态下,修改角色的行动 + if (character.CharacterState != CharacterState.Casting && character.CharacterState != CharacterState.PreCastSuperSkill) { - Character enemy = enemys[Random.Shared.Next(enemys.Count)]; - character.NormalAttack.Attack(this, character, enemy); - baseTime = character.NormalAttack.HardnessTime; - foreach (Effect effect in character.Effects.Where(e => e.Level > 0).ToList()) + CharacterActionType actionTypeTemp = CharacterActionType.None; + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect e in effects) { - effect.AlterHardnessTimeAfterNormalAttack(character, ref baseTime, ref isCheckProtected); + actionTypeTemp = e.AlterActionTypeBeforeAction(character, character.CharacterState, ref canUseItem, ref canCastSkill, ref pUseItem, ref pCastSkill, ref pNormalAttack); + } + if (actionTypeTemp != CharacterActionType.None && actionTypeTemp != CharacterActionType.CastSkill && actionTypeTemp != CharacterActionType.CastSuperSkill) + { + type = actionTypeTemp; } } - } - else if (type == CharacterActionType.PreCastSkill) - { - // 预使用技能,即开始吟唱逻辑 - // 注意:FastAuto 模式下,此吟唱逻辑删减了选取目标的逻辑,将选取逻辑放在了实际释放的环节 - // 在正常交互式模式下,吟唱前需要先选取目标 - Skill skill = skills[Random.Shared.Next(skills.Count)]; - if (skill.SkillType == SkillType.Magic) + + if (type == CharacterActionType.None) { - character.CharacterState = CharacterState.Casting; - _castingSkills.Add(character, skill); - baseTime = skill.CastTime; - skill.OnSkillCasting(this, character); + if (character.CharacterState != CharacterState.NotActionable && character.CharacterState != CharacterState.Casting && character.CharacterState != CharacterState.PreCastSuperSkill) + { + if (character.CharacterState == CharacterState.Actionable) + { + // 可以任意行动 + if (canUseItem && canCastSkill) + { + // 不做任何处理 + } + else if (canUseItem && !canCastSkill) + { + pCastSkill = 0; + } + else if (!canUseItem && canCastSkill) + { + pUseItem = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + } + } + else if (character.CharacterState == CharacterState.ActionRestricted) + { + // 行动受限,只能使用特殊物品 + if (canUseItem) + { + pCastSkill = 0; + pNormalAttack = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + pNormalAttack = 0; + } + } + else if (character.CharacterState == CharacterState.BattleRestricted) + { + // 战斗不能,只能使用物品 + if (canUseItem) + { + pCastSkill = 0; + pNormalAttack = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + pNormalAttack = 0; + } + } + else if (character.CharacterState == CharacterState.SkillRestricted) + { + // 技能受限,无法使用技能,可以使用物品 + if (canUseItem) + { + pCastSkill = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + } + } + type = GetActionType(pUseItem, pCastSkill, pNormalAttack); + _stats[character].ActionTurn += 1; + } + else if (character.CharacterState == CharacterState.Casting) + { + // 如果角色上一次吟唱了魔法,这次的行动则是结算这个魔法 + type = CharacterActionType.CastSkill; + } + else if (character.CharacterState == CharacterState.PreCastSuperSkill) + { + // 角色使用回合外爆发技插队 + type = CharacterActionType.CastSuperSkill; + } + else + { + WriteLine("[ " + character + $" ] 完全行动不能!"); + type = CharacterActionType.None; + } } - else + + List enemysTemp = new(enemys); + List teammatesTemp = new(teammates); + List skillsTemp = new(skills); + Dictionary continuousKillingTemp = new(_continuousKilling); + Dictionary earnedMoneyTemp = new(_earnedMoney); + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect e in effects) { + if (e.AlterEnemyListBeforeAction(character, enemysTemp, teammatesTemp, skillsTemp, continuousKillingTemp, earnedMoneyTemp)) + { + enemys = enemysTemp.Distinct().ToList(); + teammates = teammatesTemp.Distinct().ToList(); + skills = skillsTemp.Distinct().ToList(); + } + } + + if (type == CharacterActionType.NormalAttack) + { + // 使用普通攻击逻辑 + Character[] targets = [.. SelectTargets(character, character.NormalAttack, enemys, teammates, out bool cancel)]; + LastRound.Targets = [.. targets]; + if (!cancel && targets.Length > 0) + { + decided = true; + character.NormalAttack.Attack(this, character, targets); + baseTime = character.NormalAttack.HardnessTime; + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) + { + effect.AlterHardnessTimeAfterNormalAttack(character, ref baseTime, ref isCheckProtected); + } + } + } + else if (type == CharacterActionType.PreCastSkill) + { + // 预使用技能,即开始吟唱逻辑 + // 吟唱前需要先选取目标 + Skill skill = skills[Random.Shared.Next(skills.Count)]; + if (skill.SkillType == SkillType.Magic) + { + List targets = SelectTargets(character, skill, enemys, teammates, out bool cancel); + LastRound.Targets = [.. targets]; + if (!cancel) + { + decided = true; + character.CharacterState = CharacterState.Casting; + _castingSkills.Add(character, new(skill, targets)); + baseTime = skill.CastTime; + skill.OnSkillCasting(this, character); + } + } + else + { + if (CheckCanCast(character, skill, out double cost)) + { + List targets = SelectTargets(character, skill, enemys, teammates, out bool cancel); + LastRound.Targets = [.. targets]; + if (!cancel) + { + decided = true; + skill.OnSkillCasting(this, character); + + character.EP -= cost; + baseTime = skill.HardnessTime; + skill.CurrentCD = skill.RealCD; + skill.Enable = false; + + WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + skill.OnSkillCasted(this, character, targets); + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) + { + effect.AlterHardnessTimeAfterCastSkill(character, skill, ref baseTime, ref isCheckProtected); + } + } + } + } + LastRound.Skill = skill; + } + else if (type == CharacterActionType.CastSkill) + { + decided = true; + // 使用技能逻辑,结束吟唱状态 + character.CharacterState = CharacterState.Actionable; + SkillTarget skillTarget = _castingSkills[character]; + Skill skill = skillTarget.Skill; + List targets = skillTarget.Targets; + LastRound.Targets = [.. targets]; + LastRound.Skill = skill; + _castingSkills.Remove(character); + + // 判断是否能够释放技能 if (CheckCanCast(character, skill, out double cost)) { - skill.OnSkillCasting(this, character); + character.MP -= cost; + baseTime = skill.HardnessTime; + skill.CurrentCD = skill.RealCD; + skill.Enable = false; + + WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点魔法值,释放了魔法 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + skill.OnSkillCasted(this, character, targets); + } + else + { + WriteLine("[ " + character + $" ] 放弃释放技能!"); + // 放弃释放技能会获得3的硬直时间 + baseTime = 3; + } + + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) + { + effect.AlterHardnessTimeAfterCastSkill(character, skill, ref baseTime, ref isCheckProtected); + } + } + else if (type == CharacterActionType.CastSuperSkill) + { + decided = true; + // 结束预释放爆发技的状态 + character.CharacterState = CharacterState.Actionable; + Skill skill = _castingSuperSkills[character]; + LastRound.Skill = skill; + _castingSuperSkills.Remove(character); + + // 判断是否能够释放技能 + if (CheckCanCast(character, skill, out double cost)) + { + // 预释放的爆发技不可取消 + List targets = SelectTargets(character, skill, enemys, teammates, out _); + LastRound.Targets = [.. targets]; character.EP -= cost; baseTime = skill.HardnessTime; skill.CurrentCD = skill.RealCD; skill.Enable = false; - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} {skill.Name}!"); - skill.OnSkillCasted(this, character, enemys, teammates); + WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量值,释放了爆发技 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + skill.OnSkillCasted(this, character, targets); + } + else + { + WriteLine("[ " + character + $" ] 因能量不足放弃释放爆发技!"); + // 放弃释放技能会获得3的硬直时间 + baseTime = 3; + } - foreach (Effect effect in character.Effects.Where(e => e.Level > 0).ToList()) - { - effect.AlterHardnessTimeAfterCastSkill(character, skill, ref baseTime, ref isCheckProtected); - } + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) + { + effect.AlterHardnessTimeAfterCastSkill(character, skill, ref baseTime, ref isCheckProtected); } } - } - else if (type == CharacterActionType.CastSkill) - { - // 使用技能逻辑,结束吟唱状态 - character.CharacterState = CharacterState.Actionable; - Skill skill = _castingSkills[character]; - _castingSkills.Remove(character); - - // 判断是否能够释放技能 - if (CheckCanCast(character, skill, out double cost)) + else if (type == CharacterActionType.UseItem) { - character.MP -= cost; - baseTime = skill.HardnessTime; - skill.CurrentCD = skill.RealCD; - skill.Enable = false; - - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点魔法值,释放了技能 {skill.Name}!"); - skill.OnSkillCasted(this, character, enemys, teammates); - } - else - { - WriteLine("[ " + character + $" ] 放弃释放技能!"); - // 放弃释放技能会获得3的硬直时间 - baseTime = 3; - } - - foreach (Effect effect in character.Effects.Where(e => e.Level > 0).ToList()) - { - effect.AlterHardnessTimeAfterCastSkill(character, skill, ref baseTime, ref isCheckProtected); + // 使用物品逻辑 } } - else if (type == CharacterActionType.CastSuperSkill) - { - // 结束预释放爆发技的状态 - character.CharacterState = CharacterState.Actionable; - Skill skill = _castingSuperSkills[character]; - _castingSuperSkills.Remove(character); - // 判断是否能够释放技能 - if (CheckCanCast(character, skill, out double cost)) - { - character.EP -= cost; - baseTime = skill.HardnessTime; - skill.CurrentCD = skill.RealCD; - skill.Enable = false; - - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量值,释放了爆发技 {skill.Name}!"); - skill.OnSkillCasted(this, character, enemys, teammates); - } - else - { - WriteLine("[ " + character + $" ] 放弃释放技能!"); - // 放弃释放技能会获得3的硬直时间 - baseTime = 3; - } - - foreach (Effect effect in character.Effects.Where(e => e.Level > 0).ToList()) - { - effect.AlterHardnessTimeAfterCastSkill(character, skill, ref baseTime, ref isCheckProtected); - } - } - else if (type == CharacterActionType.UseItem) - { - // 使用物品逻辑 - } - else + if (!decided || type == CharacterActionType.None) { WriteLine("[ " + character + $" ] 放弃了行动!"); } @@ -601,6 +816,8 @@ namespace Milimoe.FunGame.Core.Model return _isGameEnd; } + LastRound.ActionType = type; + // 减少硬直时间 double newHardnessTime = baseTime; if (character.CharacterState != CharacterState.Casting) @@ -618,7 +835,8 @@ namespace Milimoe.FunGame.Core.Model // 有人想要插队吗? WillPreCastSuperSkill(character); - foreach (Effect effect in character.Effects.Where(e => e.Level > 0).ToList()) + effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { effect.OnTurnEnd(character); @@ -722,7 +940,8 @@ namespace Milimoe.FunGame.Core.Model } // 移除到时间的特效 - foreach (Effect effect in character.Effects.ToList()) + List effects = character.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { if (effect.Level == 0) { @@ -751,6 +970,24 @@ 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"); return timeToReduce; } @@ -767,8 +1004,15 @@ namespace Milimoe.FunGame.Core.Model /// public void DamageToEnemy(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal) { + // 如果敌人在结算伤害之前就已经死亡,将不会继续下去 + if (enemy.HP <= 0) + { + return; + } + bool isEvaded = damageResult == DamageResult.Evaded; - foreach (Effect effect in actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList()) + List effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { if (effect.AlterActualDamageAfterCalculation(actor, enemy, ref damage, isNormalAttack, isMagicDamage, magicType, damageResult)) { @@ -796,36 +1040,92 @@ namespace Milimoe.FunGame.Core.Model _assistDamage[actor][enemy] += damage; // 造成伤害和受伤都可以获得能量 - double ep = GetEP(damage, 0.2, 40); - foreach (Effect effect in actor.Effects) + double ep = GetEP(damage, 0.03, 30); + effects = [.. actor.Effects]; + foreach (Effect effect in effects) { effect.AlterEPAfterDamage(actor, ref ep); } actor.EP += ep; - ep = GetEP(damage, 0.1, 20); - foreach (Effect effect in enemy.Effects.Where(e => e.Level > 0).ToList()) + ep = GetEP(damage, 0.015, 15); + effects = enemy.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { effect.AlterEPAfterGetDamage(enemy, ref ep); } enemy.EP += ep; } - foreach (Effect effect in 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) { effect.AfterDamageCalculation(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult); } + if (enemy.HP <= 0 && !_eliminated.Contains(enemy)) { DeathCalculation(actor, enemy); // 给所有角色的特效广播角色死亡结算 - foreach (Effect effect in _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0).ToList())) + effects = _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList(); + foreach (Effect effect in effects) { effect.AfterDeathCalculation(enemy, actor, _continuousKilling, _earnedMoney); } - if (_queue.Remove(enemy) && !_queue.Where(c => c != actor).Any()) + // 将死者移出队列 + _queue.Remove(enemy); + if (_isTeamMode) { - // 没有其他的角色了,游戏结束 - EndGameInfo(actor); + 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} 人)"); + } + } + } + else + { + if (!_queue.Where(c => c != actor).Any()) + { + // 没有其他的角色了,游戏结束 + EndGameInfo(actor); + } } } } @@ -854,7 +1154,8 @@ namespace Milimoe.FunGame.Core.Model { bool isMagic = false; MagicType magicType = MagicType.None; - foreach (Effect effect in actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList()) + List effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { effect.AlterDamageTypeBeforeCalculation(actor, enemy, ref isNormalAttack, ref isMagic, ref magicType); } @@ -863,31 +1164,45 @@ namespace Milimoe.FunGame.Core.Model return CalculateMagicalDamage(actor, enemy, isNormalAttack, magicType, expectedDamage, out finalDamage); } - foreach (Effect effect in 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) { effect.AlterExpectedDamageBeforeCalculation(actor, enemy, ref expectedDamage, isNormalAttack, false, MagicType.None); } double dice = Random.Shared.NextDouble(); + double throwingBonus = 0; + bool checkEvade = true; + bool checkCritical = true; if (isNormalAttack) { - // 闪避判定 - if (dice < enemy.EvadeRate) + effects = actor.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { - finalDamage = 0; - List characters = [actor, enemy]; - bool isAlterEvaded = false; - foreach (Effect effect in characters.SelectMany(c => c.Effects.Where(e => e.Level > 0).ToList())) + checkEvade = effect.BeforeEvadeCheck(actor, ref throwingBonus); + } + + if (checkEvade) + { + // 闪避判定 + if (dice < (enemy.EvadeRate + throwingBonus)) { - if (effect.OnEvadedTriggered(actor, enemy, dice)) + finalDamage = 0; + List characters = [actor, enemy]; + bool isAlterEvaded = false; + effects = characters.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList(); + foreach (Effect effect in effects) { - isAlterEvaded = true; + if (effect.OnEvadedTriggered(actor, enemy, dice)) + { + isAlterEvaded = true; + } + } + if (!isAlterEvaded) + { + WriteLine("此物理攻击被完美闪避了!"); + return DamageResult.Evaded; } - } - if (!isAlterEvaded) - { - WriteLine("此物理攻击被完美闪避了!"); - return DamageResult.Evaded; } } } @@ -902,16 +1217,26 @@ namespace Milimoe.FunGame.Core.Model finalDamage = expectedDamage * (1 - physicalDamageReduction); // 暴击判定 - dice = Random.Shared.NextDouble(); - if (dice < actor.CritRate) + effects = actor.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { - finalDamage *= actor.CritDMG; // 暴击伤害倍率加成 - WriteLine("暴击生效!!"); - foreach (Effect effect in actor.Effects.Where(e => e.Level > 0).ToList()) + checkCritical = effect.BeforeCriticalCheck(actor, ref throwingBonus); + } + + if (checkCritical) + { + dice = Random.Shared.NextDouble(); + if (dice < (actor.CritRate + throwingBonus)) { - effect.OnCriticalDamageTriggered(actor, dice); + finalDamage *= actor.CritDMG; // 暴击伤害倍率加成 + WriteLine("暴击生效!!"); + effects = actor.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) + { + effect.OnCriticalDamageTriggered(actor, dice); + } + return DamageResult.Critical; } - return DamageResult.Critical; } // 是否有效伤害 @@ -931,7 +1256,8 @@ namespace Milimoe.FunGame.Core.Model public DamageResult CalculateMagicalDamage(Character actor, Character enemy, bool isNormalAttack, MagicType magicType, double expectedDamage, out double finalDamage) { bool isMagic = true; - foreach (Effect effect in actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList()) + List effects = actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { effect.AlterDamageTypeBeforeCalculation(actor, enemy, ref isNormalAttack, ref isMagic, ref magicType); } @@ -940,31 +1266,45 @@ namespace Milimoe.FunGame.Core.Model return CalculatePhysicalDamage(actor, enemy, isNormalAttack, expectedDamage, out finalDamage); } - foreach (Effect effect in 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) { effect.AlterExpectedDamageBeforeCalculation(actor, enemy, ref expectedDamage, isNormalAttack, true, magicType); } double dice = Random.Shared.NextDouble(); + double throwingBonus = 0; + bool checkEvade = true; + bool checkCritical = true; if (isNormalAttack) { - // 闪避判定 - if (dice < enemy.EvadeRate) + effects = actor.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { - finalDamage = 0; - List characters = [actor, enemy]; - bool isAlterEvaded = false; - foreach (Effect effect in characters.SelectMany(c => c.Effects.Where(e => e.Level > 0).ToList())) + checkEvade = effect.BeforeEvadeCheck(actor, ref throwingBonus); + } + + if (checkEvade) + { + // 闪避判定 + if (dice < (enemy.EvadeRate + throwingBonus)) { - if (effect.OnEvadedTriggered(actor, enemy, dice)) + finalDamage = 0; + List characters = [actor, enemy]; + bool isAlterEvaded = false; + effects = characters.SelectMany(c => c.Effects.Where(e => e.Level > 0)).ToList(); + foreach (Effect effect in effects) { - isAlterEvaded = true; + if (effect.OnEvadedTriggered(actor, enemy, dice)) + { + isAlterEvaded = true; + } + } + if (!isAlterEvaded) + { + WriteLine("此魔法攻击被完美闪避了!"); + return DamageResult.Evaded; } - } - if (!isAlterEvaded) - { - WriteLine("此魔法攻击被完美闪避了!"); - return DamageResult.Evaded; } } } @@ -989,16 +1329,26 @@ namespace Milimoe.FunGame.Core.Model finalDamage = expectedDamage * (1 - MDF); // 暴击判定 - dice = Random.Shared.NextDouble(); - if (dice < actor.CritRate) + effects = actor.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) { - finalDamage *= actor.CritDMG; // 暴击伤害倍率加成 - WriteLine("暴击生效!!"); - foreach (Effect effect in actor.Effects.Where(e => e.Level > 0).ToList()) + checkCritical = effect.BeforeCriticalCheck(actor, ref throwingBonus); + } + + if (checkCritical) + { + dice = Random.Shared.NextDouble(); + if (dice < (actor.CritRate + throwingBonus)) { - effect.OnCriticalDamageTriggered(actor, dice); + finalDamage *= actor.CritDMG; // 暴击伤害倍率加成 + WriteLine("暴击生效!!"); + effects = actor.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect effect in effects) + { + effect.OnCriticalDamageTriggered(actor, dice); + } + return DamageResult.Critical; } - return DamageResult.Critical; } // 是否有效伤害 @@ -1058,6 +1408,15 @@ namespace Milimoe.FunGame.Core.Model WriteLine(msg); } + if (FirstKiller is null) + { + FirstKiller = killer; + _stats[killer].FirstKills += 1; + _stats[death].FirstDeaths += 1; + money += 200; + WriteLine($"[ {killer} ] 拿下了第一滴血!额外奖励 200 {General.GameplayEquilibriumConstant.InGameCurrency}!!"); + } + int kills = _continuousKilling[killer]; string continuousKilling = CharacterSet.GetContinuousKilling(kills); if (kills == 2 || kills == 3) @@ -1079,7 +1438,38 @@ namespace Milimoe.FunGame.Core.Model if (!_earnedMoney.TryAdd(killer, money)) _earnedMoney[killer] += money; - _eliminated.Add(death); + if (_isTeamMode) + { + Team? team = GetTeam(killer); + if (team != null) + { + team.Score++; + } + } + + // 清除对死者的助攻数据 + List ads = [.. _assistDamage.Values.Where(ad => ad.Character != death)]; + foreach (AssistDetail ad in ads) + { + ad[death] = 0; + } + + _continuousKilling.Remove(death); + if (MaxRespawnTimes == 0) + { + _eliminated.Add(death); + } + else if (_respawnTimes.TryGetValue(death, out int times) && MaxRespawnTimes != -1 && times > MaxRespawnTimes) + { + WriteLine($"[ {death} ] 已达到复活次数上限,将不能再复活!!"); + _eliminated.Add(death); + } + else + { + // 进入复活倒计时 + double respawnTime = Math.Min(90, death.Level * 0.36 + times * 2.77); + _respawnCountdown.TryAdd(death, respawnTime); + } } /// @@ -1096,7 +1486,11 @@ namespace Milimoe.FunGame.Core.Model for (int i = _eliminated.Count - 1; i >= 0; i--) { Character ec = _eliminated[i]; - string topCharacter = ec.ToString() + (_continuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") + (_earnedMoney.TryGetValue(ec, out int earned) ? $" [ 已赚取 {earned} {General.GameplayEquilibriumConstant.InGameCurrency} ]" : ""); + CharacterStatistics statistics = CharacterStatistics[ec]; + string topCharacter = ec.ToString() + + (statistics.FirstKills > 0 ? " [ 第一滴血 ]" : "") + + (_continuousKilling.TryGetValue(ec, out int kills) && kills > 1 ? $" [ {CharacterSet.GetContinuousKilling(kills)} ]" : "") + + (_earnedMoney.TryGetValue(ec, out int earned) ? $" [ 已赚取 {earned} {General.GameplayEquilibriumConstant.InGameCurrency} ]" : ""); if (top == 1) { WriteLine("冠军:" + topCharacter); @@ -1129,6 +1523,88 @@ namespace Milimoe.FunGame.Core.Model _isGameEnd = true; } + /// + /// 游戏结束信息 [ 团队版 ] + /// + public void EndGameInfo(Team winner) + { + WriteLine("[ " + winner + " ] 是胜利者。"); + + int top = 1; + WriteLine(""); + WriteLine("=== 排名 ==="); + WriteLine(""); + + _eliminatedTeams.Add(winner); + _teams.Remove(winner.Name); + + for (int i = _eliminatedTeams.Count - 1; i >= 0; i--) + { + Team team = _eliminatedTeams[i]; + string topTeam = ""; + if (top == 1) + { + topTeam = "冠军"; + } + if (top == 2) + { + topTeam = "亚军"; + } + if (top == 3) + { + topTeam = "季军"; + } + if (top > 3) + { + topTeam = $"第 {top} 名"; + } + topTeam = $"☆--- {topTeam}团队:" + team.Name + " ---☆" + $"(得分:{team.Score})\r\n"; + foreach (Character ec in team.Members) + { + CharacterStatistics statistics = CharacterStatistics[ec]; + + string respawning = ""; + if (ec.HP <= 0) + { + respawning = "[ " + (_respawnCountdown.TryGetValue(ec, out double time) && time > 0 ? $"{time:0.##} 时间后复活" : "阵亡") + " ] "; + } + + string topCharacter = respawning + ec.ToString() + + (statistics.FirstKills > 0 ? " [ 第一滴血 ]" : "") + + (_continuousKilling.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"; + if (top == 1) + { + _stats[ec].Wins += 1; + _stats[ec].Top3s += 1; + } + else if (top == 2) + { + _stats[ec].Loses += 1; + _stats[ec].Top3s += 1; + } + else if (top == 3) + { + _stats[ec].Loses += 1; + _stats[ec].Top3s += 1; + } + else + { + _stats[ec].Loses += 1; + } + _stats[ec].Plays += 1; + _stats[ec].TotalEarnedMoney += earned; + _stats[ec].LastRank = top; + } + WriteLine(topTeam); + top++; + } + WriteLine(""); + _isGameEnd = true; + } + /// /// 检查是否可以释放技能 /// @@ -1208,20 +1684,23 @@ namespace Milimoe.FunGame.Core.Model /// public void InterruptCasting(Character caster, Character interrupter) { - if (_castingSkills.TryGetValue(caster, out Skill? cast)) + Skill? skill = null; + if (_castingSkills.TryGetValue(caster, out SkillTarget target)) { + skill = target.Skill; _castingSkills.Remove(caster); } - else if (_castingSuperSkills.TryGetValue(caster, out cast)) + else if (_castingSuperSkills.TryGetValue(caster, out skill)) { _castingSuperSkills.Remove(caster); } - if (cast != null) + if (skill != null) { WriteLine($"[ {caster} ] 的施法被 [ {interrupter} ] 打断了!!"); - foreach (Effect e in cast.Effects.Where(e => e.Level > 0).ToList()) + List effects = skill.Effects.Where(e => e.Level > 0).ToList(); + foreach (Effect e in effects) { - e.OnSkillCastInterrupted(caster, cast, interrupter); + e.OnSkillCastInterrupted(caster, skill, interrupter); } } } @@ -1311,8 +1790,8 @@ namespace Milimoe.FunGame.Core.Model { if (character.Equip(item)) { - EquipItemToSlot type = character.EquipSlot.GetEquipItemToSlot(item); - WriteLine($"[ {character} ] 装备了 [ {item.Name} ]。" + (type != EquipItemToSlot.None ? $"({ItemSet.GetEquipItemToSlotTypeName(type)} 栏位)" : "")); + EquipSlotType type = item.EquipSlotType; + WriteLine($"[ {character} ] 装备了 [ {item.Name} ]。" + (type != EquipSlotType.None ? $"({ItemSet.GetEquipSlotTypeName(type)} 栏位)" : "")); } } @@ -1322,11 +1801,11 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public void Equip(Character character, EquipItemToSlot type, Item item) + public void Equip(Character character, EquipSlotType type, Item item) { if (character.Equip(item, type)) { - WriteLine($"[ {character} ] 装备了 [ {item.Name} ]。({ItemSet.GetEquipItemToSlotTypeName(type)} 栏位)"); + WriteLine($"[ {character} ] 装备了 [ {item.Name} ]。({ItemSet.GetEquipSlotTypeName(type)} 栏位)"); } } @@ -1335,13 +1814,50 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public void UnEquip(Character character, EquipItemToSlot type) + public void UnEquip(Character character, EquipSlotType type) { Item? item = character.UnEquip(type); if (item != null) { - WriteLine($"[ {character} ] 取消装备了 [ {item.Name} ]。({ItemSet.GetEquipItemToSlotTypeName(type)} 栏位)"); + WriteLine($"[ {character} ] 取消装备了 [ {item.Name} ]。({ItemSet.GetEquipSlotTypeName(type)} 栏位)"); } } + + /// + /// 选取技能目标 + /// + /// + /// + /// + /// + /// + /// + public virtual List SelectTargets(Character caster, Skill skill, List enemys, List teammates, out bool cancel) + { + cancel = false; + if (skill.SkillType == SkillType.SuperSkill) cancel = false; + List targets = skill.SelectTargets(caster, enemys, teammates); + return targets; + } + + /// + /// 选取普通攻击目标 + /// + /// + /// + /// + /// + /// + /// + public virtual List SelectTargets(Character character, NormalAttack attack, List enemys, List teammates, out bool cancel) + { + cancel = false; + if (enemys.Count > 0) + { + List targets = [enemys[Random.Shared.Next(enemys.Count)]]; + return targets; + } + return []; + } } } diff --git a/Model/EquilibriumConstant.cs b/Model/EquilibriumConstant.cs index ed17e9d..af1c6ea 100644 --- a/Model/EquilibriumConstant.cs +++ b/Model/EquilibriumConstant.cs @@ -64,7 +64,7 @@ /// 角色最高等级 /// public int MaxLevel { get; set; } = 60; - + /// /// 魔法最高等级 /// @@ -74,17 +74,17 @@ /// 战技最高等级 /// public int MaxSkillLevel { get; set; } = 6; - + /// /// 爆发技最高等级 /// public int MaxSuperSkillLevel { get; set; } = 6; - + /// /// 被动最高等级 /// public int MaxPassiveSkillLevel { get; set; } = 6; - + /// /// 普通攻击最高等级 /// @@ -119,7 +119,7 @@ /// 初始智力 /// public double InitialINT { get; set; } = 0; - + /// /// 力量成长 /// @@ -154,7 +154,7 @@ /// 每级增加基础生命值 /// public double LevelToHPFactor { get; set; } = 17; - + /// /// 生命值增长因子 /// @@ -164,27 +164,27 @@ /// 每级增加基础魔法值 /// public double LevelToMPFactor { get; set; } = 1.5; - + /// /// 魔法值增长因子 /// - public double MPGrowthFactor { get; set; } = 1.5; - + public double MPGrowthFactor { get; set; } = 0.14; + /// /// 每级增加基础攻击力 /// public double LevelToATKFactor { get; set; } = 0.95; - + /// /// 攻击力增长因子 /// public double ATKGrowthFactor { get; set; } = 0.045; - + /// /// 物理伤害减免因子 /// public double DEFReductionFactor { get; set; } = 120; - + /// /// 行动速度上限 /// @@ -194,7 +194,7 @@ /// 每 1 点力量增加生命值 /// public double STRtoHPFactor { get; set; } = 9; - + /// /// 每 1 点力量增加生命回复力 /// diff --git a/Service/AddonManager.cs b/Service/AddonManager.cs index 6dfef1e..2eeba5d 100644 --- a/Service/AddonManager.cs +++ b/Service/AddonManager.cs @@ -1,4 +1,3 @@ -using System; using System.Reflection; using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Library.Common.Addon; @@ -93,7 +92,7 @@ namespace Milimoe.FunGame.Core.Service return plugins; } - + /// /// 从plugins目录加载所有WebAPI插件 ///