From 8d6e0d763879753353537805be3af88db957f676 Mon Sep 17 00:00:00 2001 From: Naruse <71993948+DevilProMT@users.noreply.github.com> Date: Tue, 28 Apr 2026 23:48:14 +0800 Subject: [PATCH] add weapon skin & command for it --- Common/Data/Excel/WeaponSkinExcel.cs | 24 ++++++++++ Common/Data/GameData.cs | 1 + Common/Database/Character/CharacterData.cs | 2 + .../Message/LanguageCHS.cs | 6 ++- .../Message/LanguageCHT.cs | 2 + .../Message/LanguageEN.cs | 3 +- GameServer/Command/Commands/CommandGiveAll.cs | 34 ++++++++++++++ GameServer/Game/Inventory/InventoryManager.cs | 22 +++++++++ .../Handlers/Girl/GirlWeaponSkin_Change.cs | 47 +++++++++++++++++++ 9 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 Common/Data/Excel/WeaponSkinExcel.cs create mode 100644 GameServer/Server/CallGS/Handlers/Girl/GirlWeaponSkin_Change.cs diff --git a/Common/Data/Excel/WeaponSkinExcel.cs b/Common/Data/Excel/WeaponSkinExcel.cs new file mode 100644 index 0000000..a51d9c1 --- /dev/null +++ b/Common/Data/Excel/WeaponSkinExcel.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace MikuSB.Data.Excel; + +[ResourceEntity("weapon_skin.json")] +public class WeaponSkinExcel : ExcelResource +{ + public uint Genre { get; set; } + public uint Detail { get; set; } + public uint Particular { get; set; } + public uint Level { get; set; } + public string I18n { get; set; } = ""; + + public override uint GetId() + { + return (uint)I18n.GetHashCode(); + } + + public override void Loaded() + { + GameData.WeaponSkinData.Add(GetId(), this); + } +} diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index 45cf34c..48152f4 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -19,6 +19,7 @@ public static class GameData public static Dictionary SpineData { get; private set; } = []; public static Dictionary NodeConditionData { get; private set; } = []; public static List SupportCardData { get; private set; } = []; + public static Dictionary WeaponSkinData { get; private set; } = []; } public static class GameResourceTemplateId diff --git a/Common/Database/Character/CharacterData.cs b/Common/Database/Character/CharacterData.cs index 3cbb890..2330f96 100644 --- a/Common/Database/Character/CharacterData.cs +++ b/Common/Database/Character/CharacterData.cs @@ -23,6 +23,7 @@ public class CharacterInfo public int Trust { get; set; } public uint WeaponUniqueId { get; set; } public uint SkinId { get; set; } + public uint WeaponSkinId { get; set; } public ItemFlagEnum Flag { get; set; } = ItemFlagEnum.FLAG_READED; public uint Expiration { get; set; } [SugarColumn(IsJson = true)] public List UnlockedSkin { get; set; } = []; @@ -57,6 +58,7 @@ public class CharacterInfo proto.Slots[4] = WeaponUniqueId; proto.Slots[5] = SkinId; + proto.Slots[6] = WeaponSkinId; foreach (var (slot, uid) in SupportSlots) proto.Slots[slot] = uid; diff --git a/Common/Internationalization/Message/LanguageCHS.cs b/Common/Internationalization/Message/LanguageCHS.cs index 24e601d..61ed2dc 100644 --- a/Common/Internationalization/Message/LanguageCHS.cs +++ b/Common/Internationalization/Message/LanguageCHS.cs @@ -35,6 +35,7 @@ public class ServerTextCHS /// public class WordTextCHS { + public string WeaponSkin => "武器皮肤"; public string SupportCard => "支援卡"; public string Weapon => "武器"; public string Rank => "星魂"; @@ -236,8 +237,9 @@ public class GiveAllTextCHS { public string Desc => "给予玩家所有物品\n" + "注意:-1 表示全部"; - public string Usage => "用法:/giveall weapon -p<特定> -l<等级>\n" + - "用法:/giveall card -p<特定> -l<等级>"; + public string Usage => "用法:/giveall weapon -p<特定> -l<等級>\n" + + "用法:/giveall weaponskin -p<特定>\n" + + "用法:/giveall card -p<特定> -l<等級>"; public string NotFound => "未找到 {0}!"; public string GiveAllItems => "已向玩家添加 {0} 个 {1}!"; } diff --git a/Common/Internationalization/Message/LanguageCHT.cs b/Common/Internationalization/Message/LanguageCHT.cs index deadf27..f979e16 100644 --- a/Common/Internationalization/Message/LanguageCHT.cs +++ b/Common/Internationalization/Message/LanguageCHT.cs @@ -35,6 +35,7 @@ public class ServerTextCHT /// public class WordTextCHT { + public string WeaponSkin => "武器外觀"; public string SupportCard => "支援卡"; public string Weapon => "武器"; public string Rank => "星魂"; @@ -237,6 +238,7 @@ public class GiveAllTextCHT public string Desc => "給予玩家所有物品\n" + "注意:-1 表示全部"; public string Usage => "用法:/giveall weapon -p<特定> -l<等級>\n" + + "用法:/giveall weaponskin -p<特定>\n" + "用法:/giveall card -p<特定> -l<等級>"; public string NotFound => "未找到 {0}!"; public string GiveAllItems => "已向玩家添加 {0} 個 {1}!"; diff --git a/Common/Internationalization/Message/LanguageEN.cs b/Common/Internationalization/Message/LanguageEN.cs index cc81189..242fcdc 100644 --- a/Common/Internationalization/Message/LanguageEN.cs +++ b/Common/Internationalization/Message/LanguageEN.cs @@ -35,7 +35,7 @@ public class ServerTextEN /// public class WordTextEN { - public string Star => "Star"; + public string WeaponSkin => "Weapon Skin"; public string Valk => "Valkyrie"; public string Material => "Material"; public string SupportCard => "Support Card"; @@ -204,6 +204,7 @@ public class GiveAllTextEN public string Desc => "Give all items to player\n"+ "Note: -1 means all"; public string Usage => "Usage: /giveall weapon -p -l\n" + + "Usage: /giveall weaponskin -p\n" + "Usage: /giveall card -p -l"; public string NotFound => "{0} not found!"; public string GiveAllItems => "Added {0} {1} to player!"; diff --git a/GameServer/Command/Commands/CommandGiveAll.cs b/GameServer/Command/Commands/CommandGiveAll.cs index 72864fc..6787528 100644 --- a/GameServer/Command/Commands/CommandGiveAll.cs +++ b/GameServer/Command/Commands/CommandGiveAll.cs @@ -80,4 +80,38 @@ public class CommandGiveAll : ICommands await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems", I18NManager.Translate("Word.SupportCard"), supportCards.Count.ToString())); } + + [CommandMethod("weaponskin")] + public async ValueTask GiveAllWeaponSkin(CommandArg arg) + { + if (!await arg.CheckOnlineTarget()) return; + if (await arg.GetOption('p') is not int particular) return; + + var detail = arg.GetInt(0); + var player = arg.Target!.Player!; + List weaponSkins = []; + if (detail == -1) + { + // add all + foreach (var config in GameData.WeaponSkinData.Values) + { + var weaponSkin = await player.InventoryManager! + .AddWeaponSkinItem((ItemTypeEnum)config.Genre, config.Detail, config.Particular, 1, false); + if (weaponSkin != null) weaponSkins.Add(weaponSkin); + } + } + else + { + var weaponSkin = await player.InventoryManager!.AddWeaponSkinItem(ItemTypeEnum.TYPE_WEAPON, (uint)detail, (uint)particular, 1, false); + if (weaponSkin == null) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.NotFound", I18NManager.Translate("Word.WeaponSkin"))); + return; + } + weaponSkins.Add(weaponSkin); + } + if (weaponSkins.Count > 0) await player.SendPacket(new PacketNtfCallScript(weaponSkins)); + await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems", + I18NManager.Translate("Word.WeaponSkin"), weaponSkins.Count.ToString())); + } } \ No newline at end of file diff --git a/GameServer/Game/Inventory/InventoryManager.cs b/GameServer/Game/Inventory/InventoryManager.cs index f1c8a7e..c86f57a 100644 --- a/GameServer/Game/Inventory/InventoryManager.cs +++ b/GameServer/Game/Inventory/InventoryManager.cs @@ -216,4 +216,26 @@ public class InventoryManager(PlayerInstance player) : BasePlayerManager(player) return itemInfo; } + + public async ValueTask AddWeaponSkinItem(ItemTypeEnum genre, uint detail, uint particular, uint level = 1, bool sendPacket = true) + { + if (genre != ItemTypeEnum.TYPE_WEAPON_SKIN) return null; + var skinData = GameData.WeaponSkinData.Values.FirstOrDefault(x => x.Genre == (int)genre && x.Detail == detail && x.Particular == particular && x.Level == level); + if (skinData == null) return null; + + var templateId = GameResourceTemplateId.FromGdpl((uint)genre, detail, particular, level); + if (InventoryData.Items.Values.Any(x => x.TemplateId == templateId)) return null; + var skinInfo = new BaseGameItemInfo + { + TemplateId = templateId, + UniqueId = InventoryData.NextUniqueUid++, + ItemType = ItemTypeEnum.TYPE_WEAPON_SKIN, + ItemCount = 1 + }; + InventoryData.Items[skinInfo.UniqueId] = skinInfo; + + if (sendPacket) await Player.SendPacket(new PacketNtfCallScript([skinInfo])); + + return skinInfo; + } } \ No newline at end of file diff --git a/GameServer/Server/CallGS/Handlers/Girl/GirlWeaponSkin_Change.cs b/GameServer/Server/CallGS/Handlers/Girl/GirlWeaponSkin_Change.cs new file mode 100644 index 0000000..01708fe --- /dev/null +++ b/GameServer/Server/CallGS/Handlers/Girl/GirlWeaponSkin_Change.cs @@ -0,0 +1,47 @@ +using MikuSB.Proto; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace MikuSB.GameServer.Server.CallGS.Handlers.Girl; + +[CallGSApi("GirlWeaponSkin_Change")] +public class GirlWeaponSkin_Change : ICallGSHandler +{ + public async Task Handle(Connection connection, string param, ushort seqNo) + { + var req = JsonSerializer.Deserialize(param); + if (req == null) + { + await CallGSRouter.SendScript(connection, "GirlWeaponSkin_Change", "{}"); + return; + } + + var player = connection.Player!; + var cardData = player.CharacterManager.GetCharacterByGUID(req.CardId); + if (cardData == null) return; + var skinData = player.InventoryManager.GetNormalItem(req.SkinId); + if (skinData == null) + { + await CallGSRouter.SendScript(connection, "GirlWeaponSkin_Change", "{\"err\":\"error.BadParam\"}"); + return; + } + + cardData.WeaponSkinId = req.SkinId; + var sync = new NtfSyncPlayer + { + Items = { cardData.ToProto() } + }; + + await CallGSRouter.SendScript(connection, "GirlWeaponSkin_Change", "{}", sync); + } +} + +internal sealed class GirlWeaponSkinParam +{ + [JsonPropertyName("nCardId")] + public uint CardId { get; set; } + + [JsonPropertyName("nSkinId")] + public uint SkinId { get; set; } +}