diff --git a/Common/Data/Excel/SpecialBreakExcel.cs b/Common/Data/Excel/SpecialBreakExcel.cs new file mode 100644 index 0000000..a1919f3 --- /dev/null +++ b/Common/Data/Excel/SpecialBreakExcel.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; + +namespace MikuSB.Data.Excel; + +[ResourceEntity("item/cardbreak/breaknew.json")] +public class SpecialBreakExcel : ExcelResource +{ + [JsonProperty("ID")] public int Id { get; set; } + + [JsonProperty("1Items1")] public List> Items1 { get; set; } = []; + [JsonProperty("2Items1")] public List> Items2 { get; set; } = []; + [JsonProperty("3Items1")] public List> Items3 { get; set; } = []; + [JsonProperty("4Items1")] public List> Items4 { get; set; } = []; + + public List> GetItems(uint breakLevel) => breakLevel switch + { + 1 => Items1, + 2 => Items2, + 3 => Items3, + 4 => Items4, + _ => [] + }; + + public bool HasBreakLevel(uint breakLevel) => breakLevel switch + { + 1 => Items1.Count > 0, + 2 => Items2.Count > 0, + 3 => Items3.Count > 0, + 4 => Items4.Count > 0, + _ => false + }; + + public override uint GetId() => (uint)Id; + + public override void Loaded() + { + GameData.SpecialBreakData[Id] = this; + } +} diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index e63e26f..c6e7433 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -21,6 +21,7 @@ public static class GameData public static Dictionary Rogue3DTalentData { get; private set; } = []; public static Dictionary Rogue3DDailyBuffData { get; private set; } = []; public static Dictionary BreakData { get; private set; } = []; + public static Dictionary SpecialBreakData { get; private set; } = []; public static Dictionary SpineData { get; private set; } = []; public static Dictionary NodeConditionData { get; private set; } = []; public static List SupportCardData { get; private set; } = []; diff --git a/GameServer/Server/CallGS/Handlers/Girl/GirlCard_UpBySpecialBreak.cs b/GameServer/Server/CallGS/Handlers/Girl/GirlCard_UpBySpecialBreak.cs new file mode 100644 index 0000000..790fd75 --- /dev/null +++ b/GameServer/Server/CallGS/Handlers/Girl/GirlCard_UpBySpecialBreak.cs @@ -0,0 +1,127 @@ +using MikuSB.Data; +using MikuSB.Database; +using MikuSB.Database.Inventory; +using MikuSB.Proto; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MikuSB.GameServer.Server.CallGS.Handlers.Girl; + +[CallGSApi("GirlCard_UpBySpecialBreak")] +public class GirlCard_UpBySpecialBreak : ICallGSHandler +{ + public async Task Handle(Connection connection, string param, ushort seqNo) + { + var player = connection.Player!; + var req = JsonSerializer.Deserialize(param); + if (req == null || req.CardId == 0) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"error.BadParam\"}"); + return; + } + + var card = player.CharacterManager.GetCharacterByGUID((uint)req.CardId); + if (card == null) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"error.BadParam\"}"); + return; + } + + var cardTemplate = GameData.CardData.Values.FirstOrDefault(x => + GameResourceTemplateId.FromGdpl(x.Genre, x.Detail, x.Particular, x.Level) == card.TemplateId); + if (cardTemplate == null) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"error.BadParam\"}"); + return; + } + + if (cardTemplate.BreakMatID <= 10000 || + !GameData.SpecialBreakData.TryGetValue(cardTemplate.BreakMatID, out var specialBreakExcel)) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"error.BadParam\"}"); + return; + } + + var nextBreak = card.Break + 1; + if (!specialBreakExcel.HasBreakLevel(nextBreak)) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"tip.already_max_break\"}"); + return; + } + + var requestedMaterials = new Dictionary(); + foreach (var row in specialBreakExcel.GetItems(nextBreak)) + { + if (row.Count < 5) + continue; + + var templateId = GameResourceTemplateId.FromGdpl( + (uint)Math.Max(0, row[0]), + (uint)Math.Max(0, row[1]), + (uint)Math.Max(0, row[2]), + (uint)Math.Max(0, row[3])); + var count = (uint)Math.Max(0, row[4]); + if (templateId == 0 || count == 0) + continue; + + requestedMaterials[templateId] = requestedMaterials.GetValueOrDefault(templateId) + count; + } + + if (requestedMaterials.Count == 0) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"tip.not_material_for_break\"}"); + return; + } + + foreach (var (templateId, count) in requestedMaterials) + { + var item = player.InventoryManager.InventoryData.Items.Values.FirstOrDefault(x => x.TemplateId == templateId); + if (item == null || item.ItemCount < count) + { + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{\"sErr\":\"tip.not_material_for_break\"}"); + return; + } + } + + var syncItems = new List(); + foreach (var (templateId, count) in requestedMaterials) + { + var item = player.InventoryManager.InventoryData.Items.Values.First(x => x.TemplateId == templateId); + item.ItemCount -= count; + + if (item.ItemCount == 0) + { + player.InventoryManager.InventoryData.Items.Remove(item.UniqueId); + syncItems.Add(BuildRemovedProto(item)); + } + else + { + syncItems.Add(item.ToProto()); + } + } + + card.Break = nextBreak; + syncItems.Add(card.ToProto()); + + DatabaseHelper.SaveDatabaseType(player.InventoryManager.InventoryData); + DatabaseHelper.SaveDatabaseType(player.CharacterManager.CharacterData); + + var sync = new NtfSyncPlayer(); + sync.Items.AddRange(syncItems); + + await CallGSRouter.SendScript(connection, "GirlCard_UpBySpecialBreak", "{}", sync); + } + + private static Item BuildRemovedProto(BaseGameItemInfo item) + { + var proto = item.ToProto(); + proto.Count = 0; + return proto; + } +} + +internal sealed class GirlCardUpBySpecialBreakParam +{ + [JsonPropertyName("nCardId")] + public int CardId { get; set; } +}