diff --git a/Common/Data/Excel/DormGiftExcel.cs b/Common/Data/Excel/DormGiftExcel.cs new file mode 100644 index 0000000..9c2bd6e --- /dev/null +++ b/Common/Data/Excel/DormGiftExcel.cs @@ -0,0 +1,21 @@ +namespace MikuSB.Data.Excel; + +[ResourceEntity("item/templates/dorm_gift.json")] +public class DormGiftExcel : 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 (Genre << 24) | (Detail << 16) | (Particular << 8) | Level; + } + + public override void Loaded() + { + GameData.DormGiftData.Add(GetId(), this); + } +} diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index 5cd26e8..b0e1e1f 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -30,6 +30,7 @@ public static class GameData public static Dictionary CallItemData { get; private set; } = []; public static Dictionary WeaponPartsData { get; private set; } = []; public static Dictionary GuideData { get; private set; } = []; + public static Dictionary DormGiftData { get; private set; } = []; } public static class GameResourceTemplateId diff --git a/Common/Internationalization/Message/LanguageCHS.cs b/Common/Internationalization/Message/LanguageCHS.cs index b7e19e7..b1fd691 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 Furniture => "家具"; public string Skin => "皮肤"; public string WeaponPart => "武器部件"; public string CallItem => "召唤道具"; diff --git a/Common/Internationalization/Message/LanguageCHT.cs b/Common/Internationalization/Message/LanguageCHT.cs index 1764272..b2240b9 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 Furniture => "傢俱"; public string Skin => "皮膚"; public string WeaponPart => "武器部件"; public string CallItem => "召喚道具"; diff --git a/Common/Internationalization/Message/LanguageEN.cs b/Common/Internationalization/Message/LanguageEN.cs index 6cb5cf6..2e903eb 100644 --- a/Common/Internationalization/Message/LanguageEN.cs +++ b/Common/Internationalization/Message/LanguageEN.cs @@ -35,6 +35,7 @@ public class ServerTextEN /// public class WordTextEN { + public string Furniture => "Furniture"; public string Skin => "Skin"; public string WeaponPart => "Weapon Part"; public string CallItem => "Call Item"; diff --git a/GameServer/Command/Commands/CommandGiveAll.cs b/GameServer/Command/Commands/CommandGiveAll.cs index 8e68009..3642135 100644 --- a/GameServer/Command/Commands/CommandGiveAll.cs +++ b/GameServer/Command/Commands/CommandGiveAll.cs @@ -294,4 +294,40 @@ public class CommandGiveAll : ICommands await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems", I18NManager.Translate("Word.Skin"), skinItems.Count.ToString())); } + + [CommandMethod("furniture")] + public async ValueTask GiveAllHouseFurniture(CommandArg arg) + { + if (!await arg.CheckOnlineTarget()) return; + if (await arg.GetOption('p') is not int particular) return; + if (await arg.GetOption('l') is not int level) return; + if (await arg.GetOption('g') is not int genre) return; + + var detail = arg.GetInt(0); + var player = arg.Target!.Player!; + List furnitureItems = []; + if (detail == -1) + { + // add all + foreach (var config in GameData.DormGiftData.Values) + { + var furniture = await player.InventoryManager! + .AddHouseFurnitureItem((ItemTypeEnum)config.Genre, config.Detail, config.Particular, config.Level, false); + if (furniture != null) furnitureItems.Add(furniture); + } + } + else + { + var furniture = await player.InventoryManager!.AddHouseFurnitureItem((ItemTypeEnum)genre, (uint)detail, (uint)particular, (uint)level, false); + if (furniture == null) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.NotFound", I18NManager.Translate("Word.Furniture"))); + return; + } + furnitureItems.Add(furniture); + } + if (furnitureItems.Count > 0) await player.SendPacket(new PacketNtfCallScript(furnitureItems)); + await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems", + I18NManager.Translate("Word.Furniture"), furnitureItems.Count.ToString())); + } } \ No newline at end of file diff --git a/GameServer/Game/Inventory/InventoryManager.cs b/GameServer/Game/Inventory/InventoryManager.cs index 9300984..c3d79fd 100644 --- a/GameServer/Game/Inventory/InventoryManager.cs +++ b/GameServer/Game/Inventory/InventoryManager.cs @@ -333,4 +333,25 @@ public class InventoryManager(PlayerInstance player) : BasePlayerManager(player) return weaponPartInfo; } + + public async ValueTask AddHouseFurnitureItem(ItemTypeEnum genre, uint detail, uint particular, uint level = 1, bool sendPacket = true) + { + if (genre != ItemTypeEnum.TYPE_HOUSE) return null; + var houseFurnitureData = GameData.DormGiftData.Values.FirstOrDefault(x => x.Genre == (int)genre && x.Detail == detail && x.Particular == particular && x.Level == level); + if (houseFurnitureData == null) return null; + var templateId = GameResourceTemplateId.FromGdpl((uint)genre, detail, particular, level); + if (InventoryData.Items.Values.Any(x => x.TemplateId == templateId)) return null; + var furnitureInfo = new BaseGameItemInfo + { + TemplateId = templateId, + UniqueId = InventoryData.NextUniqueUid++, + ItemType = genre, + ItemCount = 1 + }; + InventoryData.Items[furnitureInfo.UniqueId] = furnitureInfo; + + if (sendPacket) await Player.SendPacket(new PacketNtfCallScript([furnitureInfo])); + + return furnitureInfo; + } } \ No newline at end of file diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs index 2b5e263..fe96d62 100644 --- a/GameServer/Game/Player/PlayerInstance.cs +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -288,9 +288,10 @@ public class PlayerInstance(PlayerGameData data) return (gid << 16) | sid; } - public void BuildPlayerAttr() + public void BuildPlayerAttr(bool additional = false) { - var bootstrapAttrs = BuildLobbyBootstrapAttrs(); + var bootstrapAttrs = BuildLobbyBootstrapAttrs().ToList(); + if (additional) bootstrapAttrs.AddRange(BuildGirlFurnitureAttrs()); var existingAttrs = Data.Attrs .ToDictionary(x => (x.Gid, x.Sid)); var seenAttrs = new HashSet<(uint Gid, uint Sid)>(); @@ -320,6 +321,24 @@ public class PlayerInstance(PlayerGameData data) } } + private static IEnumerable<(uint Gid, uint Sid, uint Value)> BuildGirlFurnitureAttrs() + { + // Unlock some furniture slots for every girl + // Each furniture attr int stores 10 slots using 3 bits per slot + // Value below means slot 0..9 = 1 + const uint furnitureUnlockedValue = 153391689; + + for (uint girlId = 0; girlId <= 50; girlId++) + { + // FurnitureStart..FurnitureEnd = 10..19 + for (uint offset = 10; offset <= 19; offset++) + { + uint sid = (girlId * 50) + offset; + yield return (101, sid, furnitureUnlockedValue); + } + } + } + private static IEnumerable<(uint Gid, uint Sid, uint Value)> BuildLobbyBootstrapAttrs() { // GuideLogic uses group 4. Value 999 is safely above every configured step count, @@ -375,7 +394,7 @@ public class PlayerInstance(PlayerGameData data) yield return (4, guide.ID, 999); } - for (uint favor = 1; favor <= 50; favor++) + for (uint favor = 0; favor <= 50; favor++) yield return (101, favor * 50, 500); // Main Scene 0 mean default scene diff --git a/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs b/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs index a46bf09..6c70d67 100644 --- a/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs +++ b/GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs @@ -53,6 +53,7 @@ public class HandlerReqLogin : Handler await connection.Player.OnEnterGame(); connection.Player.Connection = connection; await connection.SendPacket(new PacketRspLogin(connection.Player!)); + await connection.SendPacket(new PacketNtfCallScript(connection.Player!)); await SendDebugLoginState(connection); await connection.Player.OnHeartBeat(); diff --git a/GameServer/Server/Packet/Send/Misc/PacketNtfCallScript.cs b/GameServer/Server/Packet/Send/Misc/PacketNtfCallScript.cs index e73c800..1b25602 100644 --- a/GameServer/Server/Packet/Send/Misc/PacketNtfCallScript.cs +++ b/GameServer/Server/Packet/Send/Misc/PacketNtfCallScript.cs @@ -102,7 +102,7 @@ public class PacketNtfCallScript : BasePacket public PacketNtfCallScript(PlayerInstance Player) : base(CmdIds.NtfScript) { - Player.BuildPlayerAttr(); + Player.BuildPlayerAttr(true); var proto = new NtfCallScript { Api = "", diff --git a/version.txt b/version.txt index c7e7580..78166ec 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v=2.2 \ No newline at end of file +v=2.3 \ No newline at end of file